"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var CommissioningController_exports = {};
__export(CommissioningController_exports, {
  CommissioningController: () => CommissioningController,
  cancelDiscoverCommissionableDevices: () => cancelDiscoverCommissionableDevices,
  runDiscoverCommissionableDevices: () => runDiscoverCommissionableDevices
});
module.exports = __toCommonJS(CommissioningController_exports);
var import_InteractionClient = require("#cluster/client/InteractionClient.js");
var import_general = require("@matter/general");
var import_node = require("@matter/node");
var import_operational_credentials = require("@matter/node/behaviors/operational-credentials");
var import_protocol = require("@matter/protocol");
var import_PairedNode = require("./device/PairedNode.js");
var import_MatterController = require("./MatterController.js");
/**
 * @license
 * Copyright 2022-2026 Matter.js Authors
 * SPDX-License-Identifier: Apache-2.0
 */
const logger = new import_general.Logger("CommissioningController");
function discoveryKey(identifierData, discoveryCapabilities) {
  return JSON.stringify({ id: identifierData, caps: discoveryCapabilities });
}
async function runDiscoverCommissionableDevices(node, identifierData, discoveryCapabilities, discoveredCallback, timeout, activeDiscoveries) {
  const key = discoveryKey(identifierData, discoveryCapabilities);
  const discovery = new import_node.ContinuousDiscovery(node, {
    ...identifierData,
    timeout,
    scannerFilter: discoveryCapabilities ? (s) => s.type === import_general.ChannelType.UDP || !!discoveryCapabilities.ble && s.type === import_general.ChannelType.BLE : void 0
  });
  const results = Array();
  const seen = /* @__PURE__ */ new Set();
  discovery.discovered.on((discoveredNode) => {
    const device = import_node.RemoteDescriptor.fromLongForm(discoveredNode.state.commissioning);
    const id = device.deviceIdentifier ?? JSON.stringify(discoveredNode.state.commissioning.addresses ?? []);
    if (!seen.has(id)) {
      seen.add(id);
      results.push(device);
      discoveredCallback?.(device);
    }
  });
  activeDiscoveries.set(key, discovery);
  try {
    await discovery;
    return results;
  } finally {
    activeDiscoveries.delete(key);
  }
}
function cancelDiscoverCommissionableDevices(identifierData, discoveryCapabilities, activeDiscoveries) {
  activeDiscoveries.get(discoveryKey(identifierData, discoveryCapabilities))?.stop();
}
class CommissioningController {
  #crypto;
  #started = false;
  #ipv4Disabled;
  #listeningAddressIpv4;
  #listeningAddressIpv6;
  #options;
  #id;
  #environment;
  // Set when the new API was initialized correctly
  #controllerInstance;
  #initializedNodes = /* @__PURE__ */ new Map();
  #nodeChangeObservers = /* @__PURE__ */ new Map();
  #nodeUpdateLabelHandlers = /* @__PURE__ */ new Map();
  #observers = new import_general.ObserverGroup();
  #endpointsToPeers = /* @__PURE__ */ new WeakMap();
  // Keyed by JSON-stringified identifier so cancelCommissionableDeviceDiscovery() can look up active discoveries.
  #activeDiscoveries = /* @__PURE__ */ new Map();
  /**
   * Creates a new CommissioningController instance
   *
   * @param options The options for the CommissioningController
   */
  constructor(options) {
    if (options.environment === void 0) {
      throw new import_general.ImplementationError("Initialization not done. Add the controller to the MatterServer first.");
    }
    const { environment, id } = options.environment;
    this.#environment = new import_general.Environment(id, environment);
    this.#id = id;
    this.#options = options;
    this.#crypto = this.#environment.get(import_general.Crypto);
    this.#crypto.reportUsage();
  }
  /** Returns the controller node instance. Throws an error when called before start() or after close(). */
  get node() {
    return this.#assertControllerIsStarted().node;
  }
  /**
   * Returns the OTA provider endpoint on the controller node, if enabled and controller node was started.
   * Else throws an error.
   */
  get otaProvider() {
    return this.#assertControllerIsStarted().node.endpoints.for("ota-provider");
  }
  get crypto() {
    return this.#crypto;
  }
  get nodeId() {
    return this.#controllerInstance?.nodeId;
  }
  /** Returns the configuration data needed to create a PASE commissioner, e.g. in a mobile app. */
  get paseCommissionerConfig() {
    const controller = this.#assertControllerIsStarted(
      "The CommissioningController needs to be started to get the PASE commissioner data."
    );
    const { caConfig, fabricConfig: fabricData } = controller;
    return {
      caConfig,
      fabricData
    };
  }
  #assertControllerIsStarted(errorText) {
    if (this.#controllerInstance === void 0) {
      throw new import_general.ImplementationError(
        errorText ?? "Controller instance not yet started. Please call start() first."
      );
    }
    return this.#controllerInstance;
  }
  /** Internal method to initialize a MatterController instance. */
  async #initializeController() {
    if (this.#controllerInstance !== void 0) {
      return this.#controllerInstance;
    }
    const {
      localPort,
      adminFabricId,
      adminVendorId,
      adminFabricIndex,
      caseAuthenticatedTags,
      adminFabricLabel,
      rootNodeId,
      rootCertificateAuthority,
      rootFabric,
      enableOtaProvider,
      basicInformation = {}
    } = this.#options;
    const controller = await import_MatterController.MatterController.create({
      id: this.#id,
      adminVendorId,
      adminFabricId,
      adminFabricIndex,
      caseAuthenticatedTags,
      adminFabricLabel,
      rootNodeId,
      rootCertificateAuthority,
      rootFabric,
      ble: !!(this.#environment.maybeGet(import_protocol.Ble) ?? import_general.Environment.default.maybeGet(import_protocol.Ble)),
      ipv4: !this.#ipv4Disabled,
      listeningAddressIpv4: this.#listeningAddressIpv4,
      listeningAddressIpv6: this.#listeningAddressIpv6,
      localPort,
      environment: this.#environment,
      enableOtaProvider,
      basicInformation
    });
    if (!controller.ble) {
      logger.warn("BLE is not enabled on this platform");
    }
    for (const peer of controller.node.peers) {
      if (!peer.lifecycle.isCommissioned) {
        continue;
      }
      const networkState = peer.stateOf(import_node.NetworkClient);
      const desiredDisabled = this.#options.autoConnect === false;
      if (desiredDisabled !== networkState.isDisabled) {
        await peer.set({ network: { isDisabled: desiredDisabled } });
      }
      const desiredAutoSubscribe = !!this.#options.autoSubscribe;
      if (desiredAutoSubscribe !== networkState.autoSubscribe) {
        await peer.set({ network: { autoSubscribe: desiredAutoSubscribe } });
      }
    }
    return controller;
  }
  /**
   * Commissions/Pairs a new device into the controller fabric. The method returns the NodeId of the commissioned
   * node on success.
   */
  async commissionNode(nodeOptions, commissionOptions) {
    const controller = this.#assertControllerIsStarted();
    const { connectNodeAfterCommissioning = true, commissioningFlowImpl } = commissionOptions ?? {};
    if (this.#options.enableOtaProvider && nodeOptions.commissioning.otaUpdateProviderLocation === void 0 && this.otaProvider.stateOf(import_node.SoftwareUpdateManager).announceAsDefaultProvider) {
      nodeOptions.commissioning.otaUpdateProviderLocation = {
        nodeId: this.fabric.rootNodeId,
        endpoint: this.otaProvider.number
      };
    }
    const nodeOptionsWithDefaults = {
      caseAuthenticatedTags: this.#options.caseAuthenticatedTags,
      ...nodeOptions
    };
    const nodeId = await controller.commission(nodeOptionsWithDefaults, { commissioningFlowImpl });
    if (connectNodeAfterCommissioning) {
      const node = await this.#createPairedNode(nodeId, {
        ...nodeOptions,
        autoSubscribe: nodeOptions.autoSubscribe ?? this.#options.autoSubscribe,
        subscribeMinIntervalFloorSeconds: nodeOptions.subscribeMinIntervalFloorSeconds ?? this.#options.subscribeMinIntervalFloorSeconds,
        subscribeMaxIntervalCeilingSeconds: nodeOptions.subscribeMaxIntervalCeilingSeconds ?? this.#options.subscribeMaxIntervalCeilingSeconds
      });
      await node.events.initialized;
    }
    return nodeId;
  }
  connectPaseChannel(nodeOptions) {
    const controller = this.#assertControllerIsStarted();
    return controller.connectPaseChannel(nodeOptions);
  }
  /**
   * Completes the commissioning process for a node when the initial commissioning process was done by a PASE
   * commissioner. This method should be called to discover the device operational and complete the commissioning
   * process.
   */
  completeCommissioningForNode(peerNodeId, discoveryData) {
    const controller = this.#assertControllerIsStarted();
    return controller.completeCommissioning(peerNodeId, discoveryData);
  }
  /** Check if a given node id is commissioned on this controller. */
  isNodeCommissioned(nodeId) {
    const controller = this.#assertControllerIsStarted();
    return controller.getCommissionedNodes().includes(nodeId) ?? false;
  }
  #pairedNodeForNodeId(nodeId) {
    const controller = this.#assertControllerIsStarted();
    const peerId = controller.node.peers.get(controller.fabric.addressOf(nodeId))?.id;
    if (peerId === void 0) {
      return void 0;
    }
    return this.#initializedNodes.get(peerId);
  }
  /**
   * Remove a Node id from the controller. This method should only be used if the decommission method on the
   * PairedNode instance returns an error. By default, it tries to decommission the node from the controller but will
   * remove it also in case of an error during decommissioning. Ideally try to decommission the node before and only
   * use this in case of an error as last option.
   * If this method is used the state of the PairedNode instance might be out of sync, so the PairedNode instance
   * should be disconnected first.
   */
  async removeNode(nodeId, tryDecommissioning = true) {
    const controller = this.#assertControllerIsStarted();
    const node = this.#pairedNodeForNodeId(nodeId);
    if (node !== void 0) {
      try {
        for (const ep of node.node.endpoints) {
          this.#endpointsToPeers.delete(ep);
        }
      } catch (error) {
        import_general.MatterError.accept(error);
      }
    }
    let decommissionSuccess = false;
    if (tryDecommissioning) {
      try {
        if (node === void 0) {
          throw new import_general.ImplementationError(`Node ${nodeId} is not initialized.`);
        }
        await node.decommission();
        decommissionSuccess = true;
      } catch (error) {
        logger.warn(`Decommissioning node ${nodeId} failed with error, remove node anyway: ${error}`);
      }
    }
    if (node !== void 0) {
      node.close(!decommissionSuccess);
    }
    await controller.removeNode(nodeId);
    if (node !== void 0) {
      this.#initializedNodes.delete(node.id);
      this.#nodeChangeObservers.delete(node.id);
    }
  }
  /** @deprecated Use PairedNode.disconnect() instead */
  async disconnectNode(nodeId, force = false) {
    const node = this.#pairedNodeForNodeId(nodeId);
    if (node === void 0 && !force) {
      throw new import_general.ImplementationError(`Node ${nodeId} is not connected!`);
    }
    await this.#controllerInstance?.disconnect(nodeId);
    if (force) {
      const address = this.fabric.addressOf(nodeId);
      const peer = this.node.env.get(import_protocol.PeerSet).for(address);
      await peer.disconnect();
      for (const session of [...this.node.env.get(import_protocol.SessionManager).sessionsFor(address)]) {
        await session.initiateClose();
      }
    }
  }
  /**
   * Returns the PairedNode instance for a given NodeId. The instance is initialized without auto-connect if not yet
   * created.
   */
  async getNode(nodeId, allowUnknownNode = false) {
    const existingNode = this.#pairedNodeForNodeId(nodeId);
    if (existingNode !== void 0) {
      return existingNode;
    }
    return await this.#createPairedNode(nodeId, { autoConnect: false }, allowUnknownNode);
  }
  /**
   * Connect to an already paired Node.
   * After connection the endpoint data of the device is analyzed and an object structure is created.
   * This call is not blocking and returns an initialized PairedNode instance. The connection or reconnection
   * happens in the background. Please monitor the state of the node to see if the connection was successful.
   *
   * @deprecated Use getNode() instead and call PairedNode.connect() or PairedNode.disconnect() as needed.
   */
  connectNode(nodeId, connectOptions, allowUnknownNode = false) {
    return this.#createPairedNode(nodeId, connectOptions, allowUnknownNode);
  }
  async #createPairedNode(nodeId, connectOptions, allowUnknownNode = false) {
    const controller = this.#assertControllerIsStarted();
    const nodeIsCommissioned = controller.getCommissionedNodes().includes(nodeId);
    if (!nodeIsCommissioned && !allowUnknownNode) {
      throw new import_general.ImplementationError(`Node ${nodeId} is not commissioned!`);
    }
    const existingNode = this.#pairedNodeForNodeId(nodeId);
    if (existingNode !== void 0) {
      if (!existingNode.initialized) {
        existingNode.connect(connectOptions);
      }
      return existingNode;
    }
    const peerAddress = controller.fabric.addressOf(nodeId);
    logger.info("Connecting to node", peerAddress, "...");
    let peerNode = this.node.peers.get(peerAddress);
    if (peerNode === void 0) {
      if (allowUnknownNode) {
        peerNode = await this.node.peers.forAddress(peerAddress, {
          commissioning: {
            caseAuthenticatedTags: connectOptions?.caseAuthenticatedTags ?? this.#options.caseAuthenticatedTags
          },
          network: {
            autoSubscribe: false
          }
        });
      } else {
        throw new import_general.ImplementationError(`Node ${nodeId} is no known peer to the controller`);
      }
    }
    if (peerNode.stateOf(import_node.NetworkClient).isDisabled) {
      await peerNode.enable();
    } else {
      await peerNode.start();
    }
    const { subscribeMinIntervalFloorSeconds, subscribeMaxIntervalCeilingSeconds } = connectOptions ?? {};
    if (subscribeMinIntervalFloorSeconds !== void 0 || subscribeMaxIntervalCeilingSeconds !== void 0) {
      const subscriptionOptions = {};
      if (subscribeMinIntervalFloorSeconds !== void 0) {
        subscriptionOptions.minIntervalFloor = (0, import_general.Seconds)(subscribeMinIntervalFloorSeconds);
      }
      if (subscribeMaxIntervalCeilingSeconds !== void 0) {
        subscriptionOptions.maxIntervalCeiling = (0, import_general.Seconds)(subscribeMaxIntervalCeilingSeconds);
      }
      await peerNode.set({ network: { defaultSubscription: subscriptionOptions } });
    }
    const changeObserver = new import_general.Observable();
    const { caseAuthenticatedTags = this.#options.caseAuthenticatedTags } = connectOptions ?? {};
    const pairedNode = await import_PairedNode.PairedNode.create(
      nodeId,
      this,
      connectOptions,
      peerNode,
      await this.createInteractionClient(nodeId, import_InteractionClient.NodeDiscoveryType.None, {
        forcedConnection: false,
        caseAuthenticatedTags
      }),
      // First, connect without discovery to the last known address
      controller.peers.for(peerAddress),
      this.#crypto,
      changeObserver
    );
    this.#initializedNodes.set(peerNode.id, pairedNode);
    this.#nodeChangeObservers.set(peerNode.id, changeObserver);
    return pairedNode;
  }
  /**
   * Connects to all paired nodes.
   * After connection the endpoint data of the device is analyzed and an object structure is created.
   *
   * @deprecated Use getCommissionedNodes() to get the list of nodes and getNode(nodeId) instead and call PairedNode.connect() or PairedNode.disconnect() as needed.
   */
  async connect(connectOptions) {
    const controller = this.#assertControllerIsStarted();
    if (!controller.isCommissioned()) {
      throw new import_general.ImplementationError(
        "Controller instance not yet paired with any device, so nothing to connect to."
      );
    }
    for (const nodeId of controller.getCommissionedNodes()) {
      await this.#createPairedNode(nodeId, connectOptions);
    }
    return Array.from(this.#initializedNodes.values());
  }
  /** Returns true if t least one node is commissioned/paired with this controller instance. */
  isCommissioned() {
    const controller = this.#assertControllerIsStarted();
    return controller.isCommissioned();
  }
  /**
   * Creates and Return a new InteractionClient to communicate with a node. This is mainly used internally and should
   * not be used directly. See the PairedNode class for the public API.
   */
  async createInteractionClient(nodeIdOrSession, discoveryType, options) {
    const controller = this.#assertControllerIsStarted();
    const {
      forcedConnection,
      caseAuthenticatedTags = this.#options.caseAuthenticatedTags,
      discoveryTimeout
    } = options ?? {};
    if (nodeIdOrSession instanceof import_protocol.Session || !forcedConnection) {
      return controller.createInteractionClient(nodeIdOrSession, {
        discoveryOptions: { discoveryType, timeout: discoveryTimeout },
        caseAuthenticatedTags
      });
    }
    return controller.connect(nodeIdOrSession, {
      discoveryOptions: { discoveryType, timeout: discoveryTimeout },
      allowUnknownPeer: forcedConnection,
      caseAuthenticatedTags
    });
  }
  /**
   * Returns the PairedNode instance for a given node id, if this node is connected.
   * @deprecated Use getNode() instead
   */
  getPairedNode(nodeId) {
    return this.#pairedNodeForNodeId(nodeId);
  }
  /** Returns an array with the NodeIds of all commissioned nodes. */
  getCommissionedNodes() {
    const controller = this.#assertControllerIsStarted();
    return controller.getCommissionedNodes() ?? [];
  }
  /** Returns an array with all commissioned NodeIds and their metadata. */
  getCommissionedNodesDetails() {
    const controller = this.#assertControllerIsStarted();
    return controller.getCommissionedNodesDetails() ?? [];
  }
  /**
   * Disconnects all connected nodes and closes the network connections and other resources of the controller.
   * You can use "start()" to restart the controller after closing it.
   */
  async close() {
    this.#observers.close();
    for (const node of this.#initializedNodes.values()) {
      node.close();
    }
    await this.#controllerInstance?.close();
    this.#controllerInstance = void 0;
    this.#initializedNodes.clear();
    this.#nodeChangeObservers.clear();
    this.#ipv4Disabled = void 0;
    this.#started = false;
  }
  /** Return the port used by the controller for the UDP interface. */
  getPort() {
    return this.#options.localPort;
  }
  /** @private */
  initialize(ipv4Disabled) {
    if (this.#started) {
      throw new import_general.ImplementationError("Controller instance already started.");
    }
    if (this.#ipv4Disabled !== void 0 && this.#ipv4Disabled !== ipv4Disabled) {
      throw new import_general.ImplementationError(
        "Changing the IPv4 disabled flag after starting the controller is not supported."
      );
    }
    this.#ipv4Disabled = ipv4Disabled;
  }
  get env() {
    return this.#environment;
  }
  /**
   * Initialize the controller and initialize and connect to all commissioned nodes if autoConnect is not set to false.
   */
  async start() {
    if (this.#ipv4Disabled === void 0) {
      const env = this.#environment;
      this.#environment = env;
      const runtime = env.runtime;
      runtime.add(this);
    }
    this.#started = true;
    if (this.#controllerInstance === void 0) {
      this.#controllerInstance = await this.#initializeController();
    }
    await this.#controllerInstance.start();
    const changeNotifications = this.#controllerInstance.node.env.get(import_node.ChangeNotificationService);
    this.#observers.on(changeNotifications.change, this.#handleNodeChange.bind(this));
    if (this.#options.autoConnect !== false && this.#controllerInstance.isCommissioned()) {
      await this.connect();
    }
  }
  #handleNodeChange(changes) {
    const { endpoint } = changes;
    let peerNodeId = this.#endpointsToPeers.get(endpoint);
    if (peerNodeId === void 0) {
      try {
        peerNodeId = import_node.Node.forEndpoint(endpoint).id;
        this.#endpointsToPeers.set(endpoint, peerNodeId);
      } catch (error) {
        import_general.ImplementationError.accept(error);
        return;
      }
    }
    const changeHandler = this.#nodeChangeObservers.get(peerNodeId);
    if (changeHandler === void 0) {
      return;
    }
    changeHandler.emit(changes);
  }
  /**
   * Cancels the discovery process for commissionable devices started with discoverCommissionableDevices().
   */
  cancelCommissionableDeviceDiscovery(identifierData, discoveryCapabilities) {
    cancelDiscoverCommissionableDevices(identifierData, discoveryCapabilities, this.#activeDiscoveries);
  }
  /**
   * Starts to discover commissionable devices.
   * The promise will be fulfilled after the provided timeout or when the discovery is stopped via
   * cancelCommissionableDeviceDiscovery(). The discoveredCallback will be called for each discovered device.
   */
  async discoverCommissionableDevices(identifierData, discoveryCapabilities, discoveredCallback, timeout = (0, import_general.Minutes)(15)) {
    return runDiscoverCommissionableDevices(
      this.node,
      identifierData,
      discoveryCapabilities,
      discoveredCallback,
      timeout,
      this.#activeDiscoveries
    );
  }
  /**
   * Use this method to reset the Controller storage. The method can only be called if the controller is stopped and
   * will remove all commissioning data and paired nodes from the controller.
   */
  async resetStorage() {
    if (this.#started) {
      throw new import_general.ImplementationError(
        "Storage cannot be reset while the controller is operating! Please close the controller first."
      );
    }
    await this.node.erase();
  }
  /** Returns active session information for all connected nodes. */
  getActiveSessionInformation() {
    return this.#controllerInstance?.getActiveSessionInformation() ?? [];
  }
  /** @private */
  async validateAndUpdateFabricLabel(nodeId) {
    const controller = this.#assertControllerIsStarted();
    const node = this.#pairedNodeForNodeId(nodeId);
    if (node === void 0) {
      throw new import_general.ImplementationError(`Node ${nodeId} is not connected!`);
    }
    if (!node.node.behaviors.has(import_operational_credentials.OperationalCredentialsClient)) {
      throw new import_general.UnexpectedDataError(`Node ${nodeId}: Operational Credentials Cluster not available!`);
    }
    let fabrics = node.node.stateOf(import_operational_credentials.OperationalCredentialsClient).fabrics;
    if (fabrics.length !== 1) {
      const ownFabricId = node.node.stateOf(import_operational_credentials.OperationalCredentialsClient).currentFabricIndex;
      fabrics = fabrics.filter((fabric2) => fabric2.fabricIndex === ownFabricId);
      if (fabrics.length !== 1) {
        logger.info(`Invalid fabrics returned from node ${nodeId}.`, fabrics);
        return;
      }
    }
    const label = controller.fabricConfig.label;
    const fabric = fabrics[0];
    if (fabric.label !== label) {
      logger.info(
        this.fabric.addressOf(nodeId),
        `Fabric label "${fabric.label}" does not match requested admin fabric Label "${label}". Updating...`
      );
      await node.node.commandsOf(import_operational_credentials.OperationalCredentialsClient).updateFabricLabel({
        label,
        fabricIndex: fabric.fabricIndex
      });
    }
  }
  /**
   * Updates the fabric label for the controller and all connected nodes.
   * The label is used to identify the controller and all connected nodes in the fabric.
   */
  async updateFabricLabel(label) {
    const controller = this.#assertControllerIsStarted();
    if (controller.fabricConfig.label === label) {
      return;
    }
    await controller.updateFabricLabel(label);
    for (const node of this.#initializedNodes.values()) {
      if (node.isConnected) {
        try {
          await this.validateAndUpdateFabricLabel(node.nodeId);
          return;
        } catch (error) {
          logger.warn(`Error updating fabric label on node ${node.nodeId}:`, error);
        }
      }
      if (!node.remoteInitializationDone) {
        logger.info(
          this.fabric.addressOf(node.nodeId),
          `Node is offline. Fabric label will be updated on next connection.`
        );
        return;
      }
      logger.info(
        this.fabric.addressOf(node.nodeId),
        `Node is reconnecting. Delaying fabric label update to when node is back online.`
      );
      if (!this.#nodeUpdateLabelHandlers.has(node.nodeId)) {
        const updateOnReconnect = (nodeState) => {
          if (nodeState === import_PairedNode.NodeStates.Connected) {
            this.validateAndUpdateFabricLabel(node.nodeId).catch((error) => logger.warn(`Error updating fabric label on node ${node.nodeId}:`, error)).finally(() => {
              node.events.stateChanged.off(updateOnReconnect);
              this.#nodeUpdateLabelHandlers.delete(node.nodeId);
            });
          }
        };
        node.events.stateChanged.on(updateOnReconnect);
      }
    }
  }
  get groups() {
    const controllerInstance = this.#assertControllerIsStarted();
    return controllerInstance.fabric.groups;
  }
  get fabric() {
    const controllerInstance = this.#assertControllerIsStarted();
    return controllerInstance.fabric;
  }
}
//# sourceMappingURL=CommissioningController.js.map
