var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __using = (stack, value, async) => {
  if (value != null) {
    if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
    var dispose, inner;
    if (async) dispose = value[__knownSymbol("asyncDispose")];
    if (dispose === void 0) {
      dispose = value[__knownSymbol("dispose")];
      if (async) inner = dispose;
    }
    if (typeof dispose !== "function") __typeError("Object not disposable");
    if (inner) dispose = function() {
      try {
        inner.call(this);
      } catch (e) {
        return Promise.reject(e);
      }
    };
    stack.push([async, dispose, value]);
  } else if (async) {
    stack.push([async]);
  }
  return value;
};
var __callDispose = (stack, error, hasError) => {
  var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
    return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
  };
  var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
  var next = (it) => {
    while (it = stack.pop()) {
      try {
        var result = it[1] && it[1].call(it[2]);
        if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
      } catch (e) {
        fail(e);
      }
    }
    if (hasError) throw error;
  };
  return next();
};
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
import { NodeActivity } from "#behavior/context/NodeActivity.js";
import { AccessControlServer } from "#behaviors/access-control";
import {
  Crypto,
  Diagnostic,
  Duration,
  InternalError,
  Logger,
  MatterError,
  MaybePromise,
  Millis,
  Observable,
  Seconds
} from "@matter/general";
import { GLOBAL_IDS, Specification } from "@matter/model";
import {
  ExchangeManager,
  InteractionServerMessenger,
  Mark,
  MessageType,
  NodeSession,
  PeerAddress,
  Session,
  SessionType,
  Subscription
} from "@matter/protocol";
import {
  DEFAULT_MAX_PATHS_PER_INVOKE,
  INTERACTION_PROTOCOL_ID,
  ReceivedStatusResponseError,
  Status,
  StatusResponseError,
  TlvAny,
  TlvInvokeResponseData,
  TlvInvokeResponseForSend,
  TlvSubscribeResponse
} from "@matter/types";
import { OnlineServerInteraction } from "./OnlineServerInteraction.js";
import { ServerSubscription, ServerSubscriptionConfig } from "./ServerSubscription.js";
const logger = Logger.get("InteractionServer");
function validateReadAttributesPath(path) {
  const { clusterId, attributeId } = path;
  if (clusterId === void 0 && attributeId !== void 0) {
    if (!GLOBAL_IDS.has(attributeId)) {
      throw new StatusResponseError(
        `Illegal read request for wildcard cluster and non global attribute ${attributeId}`,
        Status.InvalidAction
      );
    }
  }
}
function validateReadEventPath(path) {
  const { clusterId, eventId } = path;
  if (clusterId === void 0 && eventId !== void 0) {
    throw new StatusResponseError("Illegal read request with wildcard cluster ID", Status.InvalidAction);
  }
}
function validateReadPaths(attributeRequests, eventRequests) {
  attributeRequests?.forEach((path) => validateReadAttributesPath(path));
  eventRequests?.forEach((path) => validateReadEventPath(path));
}
function clusterPathToId({ nodeId, endpointId, clusterId }) {
  return `${nodeId}/${endpointId}/${clusterId}`;
}
class InteractionServer {
  #lifetime;
  id = INTERACTION_PROTOCOL_ID;
  requiresSecureSession = true;
  #context;
  #nextSubscriptionId;
  #isClosing = false;
  #clientHandler;
  #subscriptionConfig;
  #maxPathsPerInvoke;
  #subscriptionEstablishmentStarted = Observable();
  #node;
  #activity;
  #newActivityBlocked = false;
  #aclServer;
  #serverInteraction;
  constructor(node, sessions) {
    this.#lifetime = node.construction.join("interaction server");
    this.#nextSubscriptionId = node.env.get(Crypto).randomUint32;
    this.#context = {
      sessions,
      exchangeManager: node.env.get(ExchangeManager)
    };
    this.#subscriptionConfig = ServerSubscriptionConfig.of(node.state.network.subscriptionOptions);
    this.#maxPathsPerInvoke = node.state.basicInformation.maxPathsPerInvoke ?? DEFAULT_MAX_PATHS_PER_INVOKE;
    this.#activity = node.env.get(NodeActivity);
    this.#node = node;
    this.#serverInteraction = new OnlineServerInteraction(node.protocol);
  }
  async [Symbol.asyncDispose]() {
    await this.close();
  }
  blockNewActivity() {
    this.#newActivityBlocked = true;
  }
  get isClosing() {
    return this.#isClosing;
  }
  get maxPathsPerInvoke() {
    return this.#maxPathsPerInvoke;
  }
  get subscriptionEstablishmentStarted() {
    return this.#subscriptionEstablishmentStarted;
  }
  async onNewExchange(exchange, message) {
    var _stack = [];
    try {
      if (this.#newActivityBlocked || this.isClosing) {
        return;
      }
      if (message.payloadHeader.messageType === MessageType.ReportData && this.clientHandler) {
        return await this.clientHandler.onNewExchange(exchange, message);
      }
      const activity = __using(_stack, this.#activity.begin(`session#${exchange.session.id.toString(16)}`));
      exchange[NodeActivity.activityKey] = activity;
      try {
        return await new InteractionServerMessenger(exchange).handleRequest(this);
      } finally {
        delete exchange[NodeActivity.activityKey];
      }
    } catch (_) {
      var _error = _, _hasError = true;
    } finally {
      __callDispose(_stack, _error, _hasError);
    }
  }
  get aclServer() {
    if (this.#aclServer !== void 0) {
      return this.#aclServer;
    }
    const aclServer = this.#node.act((agent) => agent.get(AccessControlServer));
    if (MaybePromise.is(aclServer)) {
      throw new InternalError("AccessControlServer should already be initialized.");
    }
    return this.#aclServer = aclServer;
  }
  get clientHandler() {
    return this.#clientHandler;
  }
  set clientHandler(clientHandler) {
    this.#clientHandler = clientHandler;
  }
  #prepareOnlineContext(exchange, message, fabricFiltered, timed = false) {
    return {
      activity: exchange[NodeActivity.activityKey],
      fabricFiltered,
      timed,
      message,
      exchange,
      node: this.#node
    };
  }
  #checkSenderRevision(interactionModelRevision) {
    if (interactionModelRevision === void 0) {
      logger.debug("Sender omitted interaction model revision");
    } else if (interactionModelRevision > Specification.INTERACTION_MODEL_REVISION) {
      logger.debug(
        `Interaction model revision of sender ${interactionModelRevision} is higher than supported ${Specification.INTERACTION_MODEL_REVISION}`
      );
    }
  }
  /**
   * Returns an iterator that yields the data reports and events data for the given read request.
   */
  async *#executeReadInteraction(readRequest, exchange, message) {
    const readContext = this.#prepareOnlineContext(exchange, message, readRequest.isFabricFiltered);
    for await (const chunk of this.#serverInteraction.read(readRequest, readContext)) {
      for await (const report of chunk) {
        yield InteractionServerMessenger.convertServerInteractionReport(report);
      }
    }
  }
  async handleReadRequest(exchange, readRequest, message) {
    const {
      attributeRequests,
      eventRequests,
      isFabricFiltered,
      dataVersionFilters,
      eventFilters,
      interactionModelRevision
    } = readRequest;
    logger.debug(() => [
      "Read",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.asFlags({ fabricFiltered: isFabricFiltered }),
      Diagnostic.dict({
        attributes: `${attributeRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") ?? "none"}${dataVersionFilters?.length ? ` with ${dataVersionFilters?.length} filters` : ""}`,
        events: `${eventRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") ?? "none"}${eventFilters?.length ? `, ${eventFilters?.length} filters` : ""}`
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    if (attributeRequests === void 0 && eventRequests === void 0) {
      return {
        dataReport: {
          interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
          suppressResponse: true
        }
      };
    }
    if (message.packetHeader.sessionType !== SessionType.Unicast) {
      throw new StatusResponseError(
        "Reads are only allowed on unicast sessions",
        // Means "No groups"
        Status.InvalidAction
      );
    }
    validateReadPaths(attributeRequests, eventRequests);
    return {
      dataReport: {
        interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
        suppressResponse: true
      },
      payload: this.#executeReadInteraction(readRequest, exchange, message)
    };
  }
  async handleWriteRequest(exchange, writeRequest, messenger, message) {
    let { suppressResponse, writeRequests, moreChunkedMessages } = writeRequest;
    const { timedRequest, interactionModelRevision } = writeRequest;
    const sessionType = message.packetHeader.sessionType;
    logger.info(() => [
      "Write",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.asFlags({ suppressResponse, moreChunkedMessages }),
      Diagnostic.weak(writeRequests.map((req) => this.#node.protocol.inspectPath(req.path)).join(", "))
    ]);
    if (moreChunkedMessages && suppressResponse) {
      throw new StatusResponseError(
        "MoreChunkedMessages and SuppressResponse cannot be used together in write messages",
        Status.InvalidAction
      );
    }
    this.#checkSenderRevision(interactionModelRevision);
    const receivedWithinTimedInteraction = exchange.hasActiveTimedInteraction();
    if (receivedWithinTimedInteraction && moreChunkedMessages) {
      throw new StatusResponseError(
        "Write Request action that is part of a Timed Write Interaction SHALL NOT be chunked.",
        Status.InvalidAction
      );
    }
    if (exchange.hasExpiredTimedInteraction()) {
      exchange.clearTimedInteraction();
      throw new StatusResponseError(`Timed request window expired. Decline write request.`, Status.Timeout);
    }
    if (timedRequest !== exchange.hasTimedInteraction()) {
      throw new StatusResponseError(
        `timedRequest flag of write interaction (${timedRequest}) mismatch with expected timed interaction (${receivedWithinTimedInteraction}).`,
        Status.TimedRequestMismatch
      );
    }
    if (receivedWithinTimedInteraction) {
      logger.debug("Write request for timed interaction on", exchange.channel.name);
      exchange.clearTimedInteraction();
      if (sessionType !== SessionType.Unicast) {
        throw new StatusResponseError(
          "Write requests are only allowed on unicast sessions when a timed interaction is running.",
          Status.InvalidAction
        );
      }
    }
    if (sessionType === SessionType.Group && !suppressResponse) {
      throw new StatusResponseError(
        "Write requests are only allowed as group casts when suppressResponse=true.",
        Status.InvalidAction
      );
    }
    let previousProcessedAttributePath;
    while (true) {
      const allResponses = new Array();
      let currentBatch = new Array();
      const processBatch = async () => {
        if (currentBatch.length === 0) {
          return;
        }
        const context = this.#prepareOnlineContext(
          exchange,
          message,
          true,
          // always fabric filtered
          receivedWithinTimedInteraction
        );
        const batchRequest = { ...writeRequest, writeRequests: currentBatch, suppressResponse: false };
        const batchResults = await this.#serverInteraction.write(batchRequest, context);
        if (batchResults) {
          allResponses.push(...batchResults);
        }
        currentBatch = [];
      };
      for (const request of writeRequests) {
        const { path } = request;
        const listIndex = path.listIndex;
        if (listIndex === null) {
          if (previousProcessedAttributePath?.endpointId !== path.endpointId || previousProcessedAttributePath?.clusterId !== path.clusterId || previousProcessedAttributePath?.attributeId !== path.attributeId) {
            await processBatch();
            allResponses.push({
              kind: "attr-status",
              path,
              status: Status.Busy
            });
            continue;
          }
        }
        currentBatch.push(request);
        if (path.endpointId !== void 0 && path.clusterId !== void 0 && path.attributeId !== void 0) {
          previousProcessedAttributePath = path;
        }
      }
      await processBatch();
      if (suppressResponse) {
        break;
      }
      const chunkResponse = {
        writeResponses: allResponses.map(({ path, status, clusterStatus }) => ({
          path,
          status: { status, clusterStatus }
        })),
        interactionModelRevision: Specification.INTERACTION_MODEL_REVISION
      };
      await messenger.sendWriteResponse(chunkResponse, {
        logContext: moreChunkedMessages ? "WriteResponse-chunk" : void 0
      });
      if (!moreChunkedMessages) {
        break;
      }
      const nextChunk = await messenger.readNextWriteRequest();
      const nextRequest = nextChunk.writeRequest;
      ({ writeRequests, moreChunkedMessages, suppressResponse } = nextRequest);
      logger.info(() => [
        "Write",
        Mark.INBOUND,
        exchange.via,
        Diagnostic.asFlags({ suppressResponse, moreChunkedMessages }),
        Diagnostic.weak(writeRequests.map((req) => this.#node.protocol.inspectPath(req.path)).join(", "))
      ]);
      if (suppressResponse) {
        throw new StatusResponseError(
          "Multiple chunked messages and SuppressResponse cannot be used together in write messages",
          Status.InvalidAction
        );
      }
    }
  }
  async handleSubscribeRequest(exchange, request, messenger, message) {
    const {
      minIntervalFloorSeconds,
      maxIntervalCeilingSeconds,
      attributeRequests,
      dataVersionFilters,
      eventRequests,
      eventFilters,
      keepSubscriptions,
      isFabricFiltered,
      interactionModelRevision
    } = request;
    logger.info(() => [
      "Subscribe",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.asFlags({ fabricFiltered: isFabricFiltered, keepSubscriptions }),
      Diagnostic.dict({
        attributePaths: attributeRequests?.length,
        eventPaths: eventRequests?.length
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    if (message.packetHeader.sessionType !== SessionType.Unicast) {
      throw new StatusResponseError("Subscriptions are only allowed on unicast sessions", Status.InvalidAction);
    }
    NodeSession.assert(exchange.session, "Subscriptions are only implemented on secure sessions");
    const session = exchange.session;
    const fabric = session.fabric;
    if (fabric !== void 0 && !keepSubscriptions) {
      let clearedCount = 0;
      for (const sess of this.#context.sessions.sessions) {
        if (!PeerAddress.is(sess.peerAddress, session.peerAddress)) {
          continue;
        }
        for (const subscription2 of sess.subscriptions) {
          await subscription2.handlePeerCancel();
          clearedCount++;
        }
      }
      if (clearedCount > 0) {
        logger.debug(
          `Cleared ${clearedCount} subscriptions for Subscriber node ${session.peerNodeId} because keepSubscriptions=false`
        );
      }
    }
    if ((!Array.isArray(attributeRequests) || attributeRequests.length === 0) && (!Array.isArray(eventRequests) || eventRequests.length === 0)) {
      throw new StatusResponseError("No attributes or events requested", Status.InvalidAction);
    }
    logger.debug(() => [
      "Subscribe request details",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.dict({
        attributes: attributeRequests?.length ? attributeRequests?.map((path) => this.#node.protocol.inspectPath(path)).join(", ") : void 0,
        dataVersionFilters: dataVersionFilters?.length ? dataVersionFilters.map(
          ({ path: { nodeId, endpointId, clusterId }, dataVersion }) => `${clusterPathToId({ nodeId, endpointId, clusterId })}=${dataVersion}`
        ).join(", ") : void 0,
        events: eventRequests?.length ? eventRequests.map((path) => this.#node.protocol.inspectPath(path)).join(", ") : void 0,
        eventFilters: eventFilters?.length ? eventFilters.map((filter) => `${filter.nodeId}/${filter.eventMin}`).join(", ") : void 0
      })
    ]);
    validateReadPaths(attributeRequests, eventRequests);
    if (minIntervalFloorSeconds < 0) {
      throw new StatusResponseError(
        "minIntervalFloorSeconds should be greater or equal to 0",
        Status.InvalidAction
      );
    }
    if (maxIntervalCeilingSeconds < 0) {
      throw new StatusResponseError(
        "maxIntervalCeilingSeconds should be greater or equal to 0",
        Status.InvalidAction
      );
    }
    if (maxIntervalCeilingSeconds < minIntervalFloorSeconds) {
      throw new StatusResponseError(
        "maxIntervalCeilingSeconds should be greater or equal to minIntervalFloorSeconds",
        Status.InvalidAction
      );
    }
    if (this.#nextSubscriptionId === 4294967295) this.#nextSubscriptionId = 0;
    const subscriptionId = this.#nextSubscriptionId++;
    this.#subscriptionEstablishmentStarted.emit(session.peerAddress);
    let subscription;
    try {
      subscription = await this.#establishSubscription(
        subscriptionId,
        request,
        messenger,
        session,
        exchange,
        message
      );
    } catch (error) {
      logger.warn(
        `Subscription ${Subscription.idStrOf(subscriptionId)} for session ${session.via}: Error while sending initial data reports:`,
        error instanceof MatterError ? error.message : error
      );
      const sre = StatusResponseError.of(error);
      if (sre && !(sre instanceof ReceivedStatusResponseError)) {
        logger.info(
          "Status",
          Diagnostic.strong(`${Status[sre.code]}(${sre.code})`),
          Mark.OUTBOUND,
          exchange.via,
          exchange.diagnostics,
          "Error:",
          Diagnostic.errorMessage(sre)
        );
        await messenger.sendStatus(sre.code, {
          logContext: {
            for: "I/SubscriptionSeed-Status"
          }
        });
      }
      await messenger.close();
      return;
    }
    const maxInterval = subscription.maxInterval;
    await messenger.send(
      MessageType.SubscribeResponse,
      TlvSubscribeResponse.encode({
        subscriptionId,
        maxInterval: Seconds.of(maxInterval),
        interactionModelRevision: Specification.INTERACTION_MODEL_REVISION
      }),
      {
        logContext: {
          ...Subscription.diagnosticOf(subscriptionId),
          maxInterval: Duration.format(maxInterval)
        }
      }
    );
    subscription.activate();
  }
  #initiateSubscriptionExchange(addressOrSession, protocolId) {
    if (addressOrSession instanceof Session) {
      return this.#context.exchangeManager.initiateExchangeForSession(addressOrSession, protocolId);
    }
    return this.#context.exchangeManager.initiateExchange(addressOrSession, protocolId);
  }
  async #establishSubscription(id, request, messenger, session, exchange, message) {
    const context = {
      session,
      node: this.#node,
      initiateExchange: (addressOrSession, protocolId) => this.#initiateSubscriptionExchange(addressOrSession, protocolId)
    };
    const subscription = new ServerSubscription({
      id,
      context,
      request,
      subscriptionOptions: this.#subscriptionConfig
    });
    const readContext = this.#prepareOnlineContext(exchange, message, request.isFabricFiltered);
    try {
      await subscription.sendInitialReport(messenger, readContext);
    } catch (error) {
      await subscription.close();
      throw error;
    }
    logger.notice(
      "Subscribe successful",
      Mark.OUTBOUND,
      exchange.via,
      exchange.diagnostics,
      Diagnostic.dict({
        ...Subscription.diagnosticOf(subscription),
        timing: `${Duration.format(subscription.minIntervalFloor)} - ${Duration.format(subscription.maxIntervalCeiling)} => ${Duration.format(subscription.maxInterval)}`,
        sendInterval: Duration.format(subscription.sendInterval)
      })
    );
    return subscription;
  }
  async establishFormerSubscription({
    subscriptionId,
    attributeRequests,
    eventRequests,
    isFabricFiltered,
    minIntervalFloor,
    maxIntervalCeiling,
    maxInterval,
    sendInterval
  }, session) {
    const exchange = this.#context.exchangeManager.initiateExchange(session.peerAddress, INTERACTION_PROTOCOL_ID);
    logger.info(
      `Reestablish subscription`,
      Mark.OUTBOUND,
      exchange.via,
      Diagnostic.dict({
        ...Subscription.diagnosticOf(subscriptionId),
        isFabricFiltered,
        maxInterval: Duration.format(maxInterval),
        sendInterval: Duration.format(sendInterval)
      })
    );
    const context = {
      session,
      node: this.#node,
      initiateExchange: (addressOrSession, protocolId) => this.#initiateSubscriptionExchange(addressOrSession, protocolId)
    };
    const subscription = new ServerSubscription({
      id: subscriptionId,
      context,
      request: {
        attributeRequests,
        eventRequests,
        isFabricFiltered,
        minIntervalFloorSeconds: Seconds.of(minIntervalFloor),
        maxIntervalCeilingSeconds: Seconds.of(maxIntervalCeiling)
      },
      subscriptionOptions: this.#subscriptionConfig,
      useAsMaxInterval: maxInterval,
      useAsSendInterval: sendInterval
    });
    const readContext = this.#prepareOnlineContext(exchange, void 0, isFabricFiltered);
    try {
      await subscription.sendInitialReport(
        new InteractionServerMessenger(exchange),
        readContext,
        true
        // Do not send status responses because we simulate that the subscription is still established
      );
      subscription.activate();
      logger.notice(
        `Subscription successfully reestablished`,
        Mark.OUTBOUND,
        exchange.via,
        exchange.diagnostics,
        Diagnostic.dict({
          ...Subscription.diagnosticOf(subscriptionId),
          timing: `${Duration.format(minIntervalFloor)} - ${Duration.format(maxIntervalCeiling)} => ${Duration.format(subscription.maxInterval)}`,
          sendInterval: Duration.format(subscription.sendInterval)
        })
      );
    } catch (error) {
      await subscription.close();
      throw error;
    }
    return subscription;
  }
  async handleInvokeRequest(exchange, request, messenger, message) {
    const { invokeRequests, timedRequest, suppressResponse, interactionModelRevision } = request;
    logger.info(() => [
      "Invoke",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.asFlags({ suppressResponse, timedRequest }),
      Diagnostic.dict({
        invokes: invokeRequests.map(
          ({ commandPath: { endpointId, clusterId, commandId } }) => this.#node.protocol.inspectPath({ endpointId, clusterId, commandId })
        ).join(", ")
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    const receivedWithinTimedInteraction = exchange.hasActiveTimedInteraction();
    if (exchange.hasExpiredTimedInteraction()) {
      exchange.clearTimedInteraction();
      throw new StatusResponseError(`Timed request window expired. Decline invoke request.`, Status.Timeout);
    }
    if (timedRequest !== exchange.hasTimedInteraction()) {
      throw new StatusResponseError(
        `timedRequest flag of invoke interaction (${timedRequest}) mismatch with expected timed interaction (${receivedWithinTimedInteraction}).`,
        Status.TimedRequestMismatch
      );
    }
    if (receivedWithinTimedInteraction) {
      logger.debug("Invoke request for timed interaction on", exchange.channel.name);
      exchange.clearTimedInteraction();
      if (message.packetHeader.sessionType !== SessionType.Unicast) {
        throw new StatusResponseError(
          "Invoke requests are only allowed on unicast sessions when a timed interaction is running.",
          Status.InvalidAction
        );
      }
    }
    if (invokeRequests.length > this.#maxPathsPerInvoke) {
      throw new StatusResponseError(
        `Only ${this.#maxPathsPerInvoke} invoke requests are supported in one message. This message contains ${invokeRequests.length}`,
        Status.InvalidAction
      );
    }
    const context = this.#prepareOnlineContext(exchange, message, void 0, receivedWithinTimedInteraction);
    const isGroupSession = message.packetHeader.sessionType === SessionType.Group;
    const results = this.#serverInteraction.invoke(request, context);
    if (isGroupSession) {
      for await (const _chunk of results) ;
      return;
    }
    const currentChunkResponses = new Array();
    const emptyInvokeResponse = {
      suppressResponse: false,
      // Deprecated but must be present
      interactionModelRevision: Specification.INTERACTION_MODEL_REVISION,
      invokeResponses: []
    };
    const emptyInvokeResponseLength = TlvInvokeResponseForSend.encode(emptyInvokeResponse).byteLength;
    let messageSize = emptyInvokeResponseLength;
    let chunkedTransmissionTerminated = false;
    const sendChunkIfNeeded = async (invokeResponse) => {
      const encodedInvokeResponse = TlvInvokeResponseData.encodeTlv(invokeResponse);
      const invokeResponseBytes = TlvAny.getEncodedByteLength(encodedInvokeResponse);
      if (messageSize + invokeResponseBytes > exchange.maxPayloadSize && currentChunkResponses.length > 0) {
        logger.debug(
          "Invoke (chunk)",
          Mark.OUTBOUND,
          exchange.via,
          Diagnostic.dict({ commands: currentChunkResponses.length })
        );
        const chunkResponse = {
          ...emptyInvokeResponse,
          invokeResponses: currentChunkResponses.map((r) => TlvInvokeResponseData.encodeTlv(r))
        };
        if (!await messenger.sendInvokeResponseChunk(chunkResponse)) {
          chunkedTransmissionTerminated = true;
          return;
        }
        currentChunkResponses.length = 0;
        messageSize = emptyInvokeResponseLength;
      }
      currentChunkResponses.push(invokeResponse);
      messageSize += invokeResponseBytes;
    };
    const toInvokeResponseData = (data) => {
      if (data.kind === "cmd-response") {
        const { path: commandPath2, commandRef: commandRef2, data: commandFields } = data;
        return { command: { commandPath: commandPath2, commandFields, commandRef: commandRef2 } };
      }
      const { path: commandPath, commandRef, status, clusterStatus } = data;
      return { status: { commandPath, status: { status, clusterStatus }, commandRef } };
    };
    let suppressedBuffer = suppressResponse ? [] : void 0;
    for await (const chunk of results) {
      if (chunkedTransmissionTerminated) {
        continue;
      }
      for (const data of chunk) {
        const responseData = toInvokeResponseData(data);
        if (suppressedBuffer !== void 0) {
          if (data.kind !== "cmd-response") {
            suppressedBuffer.push(responseData);
            continue;
          }
          for (const held of suppressedBuffer) {
            await sendChunkIfNeeded(held);
          }
          suppressedBuffer = void 0;
        }
        await sendChunkIfNeeded(responseData);
      }
    }
    if (suppressedBuffer !== void 0) {
      logger.debug("Invoke (suppressed)", Mark.OUTBOUND, exchange.via, Diagnostic.weak("no response sent"));
      return;
    }
    if (!chunkedTransmissionTerminated) {
      if (currentChunkResponses.length > 0) {
        logger.debug(
          "Invoke (final)",
          Mark.OUTBOUND,
          exchange.via,
          Diagnostic.dict({ commands: currentChunkResponses.length })
        );
      }
      const finalResponse = {
        ...emptyInvokeResponse,
        invokeResponses: currentChunkResponses.map((r) => TlvInvokeResponseData.encodeTlv(r))
      };
      await messenger.sendInvokeResponse(finalResponse);
    }
  }
  handleTimedRequest(exchange, { timeout, interactionModelRevision }) {
    const interval = Millis(timeout);
    logger.debug(() => [
      "Timed request",
      Mark.INBOUND,
      exchange.via,
      Diagnostic.dict({
        interval: Duration.format(interval)
      })
    ]);
    this.#checkSenderRevision(interactionModelRevision);
    exchange.startTimedInteraction(interval);
  }
  async close() {
    this.#isClosing = true;
    this.#lifetime[Symbol.dispose]();
  }
}
export {
  InteractionServer
};
//# sourceMappingURL=InteractionServer.js.map
