"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.Workspace = exports.ComponentRemoved = exports.ComponentChanged = exports.ComponentAdded = exports.AspectSpecificField = void 0;
function _memoizee() {
  const data = _interopRequireDefault(require("memoizee"));
  _memoizee = function () {
    return data;
  };
  return data;
}
function _pMapSeries() {
  const data = _interopRequireDefault(require("p-map-series"));
  _pMapSeries = function () {
    return data;
  };
  return data;
}
function _graph() {
  const data = require("@teambit/graph.cleargraph");
  _graph = function () {
    return data;
  };
  return data;
}
function _dependenciesModules() {
  const data = require("@teambit/dependencies.modules.packages-excluder");
  _dependenciesModules = function () {
    return data;
  };
  return data;
}
function _component() {
  const data = require("@teambit/component");
  _component = function () {
    return data;
  };
  return data;
}
function _bitError() {
  const data = require("@teambit/bit-error");
  _bitError = function () {
    return data;
  };
  return data;
}
function _config() {
  const data = require("@teambit/legacy/dist/consumer/config");
  _config = function () {
    return data;
  };
  return data;
}
function _dependencyResolver() {
  const data = require("@teambit/dependency-resolver");
  _dependencyResolver = function () {
    return data;
  };
  return data;
}
function _envs() {
  const data = require("@teambit/envs");
  _envs = function () {
    return data;
  };
  return data;
}
function _workspaceModules() {
  const data = require("@teambit/workspace.modules.match-pattern");
  _workspaceModules = function () {
    return data;
  };
  return data;
}
function _componentId() {
  const data = require("@teambit/component-id");
  _componentId = function () {
    return data;
  };
  return data;
}
function _legacyBitId() {
  const data = require("@teambit/legacy-bit-id");
  _legacyBitId = function () {
    return data;
  };
  return data;
}
function _consumer() {
  const data = require("@teambit/legacy/dist/consumer");
  _consumer = function () {
    return data;
  };
  return data;
}
function _legacy() {
  const data = require("@teambit/legacy.bit-map");
  _legacy = function () {
    return data;
  };
  return data;
}
function _harmonyModules() {
  const data = require("@teambit/harmony.modules.in-memory-cache");
  _harmonyModules = function () {
    return data;
  };
  return data;
}
function _legacy2() {
  const data = require("@teambit/legacy.component-list");
  _legacy2 = function () {
    return data;
  };
  return data;
}
function _extensionData() {
  const data = require("@teambit/legacy/dist/consumer/config/extension-data");
  _extensionData = function () {
    return data;
  };
  return data;
}
function _toolboxPath() {
  const data = require("@teambit/toolbox.path.path");
  _toolboxPath = function () {
    return data;
  };
  return data;
}
function _toolboxPath2() {
  const data = require("@teambit/toolbox.path.is-path-inside");
  _toolboxPath2 = function () {
    return data;
  };
  return data;
}
function _fsExtra() {
  const data = _interopRequireDefault(require("fs-extra"));
  _fsExtra = function () {
    return data;
  };
  return data;
}
function _lodash() {
  const data = require("lodash");
  _lodash = function () {
    return data;
  };
  return data;
}
function _constants() {
  const data = require("@teambit/legacy/dist/constants");
  _constants = function () {
    return data;
  };
  return data;
}
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _component2() {
  const data = _interopRequireDefault(require("@teambit/legacy/dist/consumer/component"));
  _component2 = function () {
    return data;
  };
  return data;
}
function _component3() {
  const data = require("@teambit/component.sources");
  _component3 = function () {
    return data;
  };
  return data;
}
function _scopeComponentsImporter() {
  const data = _interopRequireDefault(require("@teambit/legacy/dist/scope/component-ops/scope-components-importer"));
  _scopeComponentsImporter = function () {
    return data;
  };
  return data;
}
function _loader() {
  const data = _interopRequireDefault(require("@teambit/legacy/dist/cli/loader"));
  _loader = function () {
    return data;
  };
  return data;
}
function _legacy3() {
  const data = require("@teambit/legacy.scope-api");
  _legacy3 = function () {
    return data;
  };
  return data;
}
function _scopeNotFoundOrDenied() {
  const data = require("@teambit/legacy/dist/remotes/exceptions/scope-not-found-or-denied");
  _scopeNotFoundOrDenied = function () {
    return data;
  };
  return data;
}
function _componentVersion() {
  const data = require("@teambit/component-version");
  _componentVersion = function () {
    return data;
  };
  return data;
}
function _http() {
  const data = require("@teambit/legacy/dist/scope/network/http/http");
  _http = function () {
    return data;
  };
  return data;
}
function _componentConfigFile() {
  const data = require("./component-config-file");
  _componentConfigFile = function () {
    return data;
  };
  return data;
}
function _componentStatus() {
  const data = require("./workspace-component/component-status");
  _componentStatus = function () {
    return data;
  };
  return data;
}
function _workspaceComponentLoader() {
  const data = require("./workspace-component/workspace-component-loader");
  _workspaceComponentLoader = function () {
    return data;
  };
  return data;
}
function _buildGraphFromFs() {
  const data = require("./build-graph-from-fs");
  _buildGraphFromFs = function () {
    return data;
  };
  return data;
}
function _bitMap() {
  const data = require("./bit-map");
  _bitMap = function () {
    return data;
  };
  return data;
}
function _workspace() {
  const data = require("./workspace.aspect");
  _workspace = function () {
    return data;
  };
  return data;
}
function _buildGraphIdsFromFs() {
  const data = require("./build-graph-ids-from-fs");
  _buildGraphIdsFromFs = function () {
    return data;
  };
  return data;
}
function _aspectsMerger() {
  const data = require("./aspects-merger");
  _aspectsMerger = function () {
    return data;
  };
  return data;
}
function _workspaceAspectsLoader() {
  const data = require("./workspace-aspects-loader");
  _workspaceAspectsLoader = function () {
    return data;
  };
  return data;
}
function _mergeConfigConflict() {
  const data = require("./exceptions/merge-config-conflict");
  _mergeConfigConflict = function () {
    return data;
  };
  return data;
}
function _compFiles() {
  const data = require("./workspace-component/comp-files");
  _compFiles = function () {
    return data;
  };
  return data;
}
function _filter() {
  const data = require("./filter");
  _filter = function () {
    return data;
  };
  return data;
}
function _componentStatusLoader() {
  const data = require("./workspace-component/component-status-loader");
  _componentStatusLoader = function () {
    return data;
  };
  return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable max-lines */
const AspectSpecificField = exports.AspectSpecificField = '__specific';
const ComponentAdded = exports.ComponentAdded = 'componentAdded';
const ComponentChanged = exports.ComponentChanged = 'componentChanged';
const ComponentRemoved = exports.ComponentRemoved = 'componentRemoved';
const DEFAULT_VENDOR_DIR = 'vendor';

/**
 * API of the Bit Workspace
 */
class Workspace {
  constructor(
  /**
   * private pubsub.
   */
  pubsub, config,
  /**
   * private access to the legacy consumer instance.
   */
  consumer,
  /**
   * access to the workspace `Scope` instance
   */
  scope,
  /**
   * access to the `ComponentProvider` instance
   */
  componentAspect, dependencyResolver, variants, aspectLoader, logger, componentList = new (_legacy2().ComponentsList)(consumer),
  /**
   * private reference to the instance of Harmony.
   */
  harmony,
  /**
   * on component load slot.
   */
  onComponentLoadSlot,
  /**
   * on component change slot.
   */
  onComponentChangeSlot, envs, globalConfig,
  /**
   * on component add slot.
   */
  onComponentAddSlot, onComponentRemoveSlot, onAspectsResolveSlot, onRootAspectAddedSlot, graphql, onBitmapChangeSlot, onWorkspaceConfigChangeSlot) {
    this.pubsub = pubsub;
    this.config = config;
    this.consumer = consumer;
    this.scope = scope;
    this.componentAspect = componentAspect;
    this.dependencyResolver = dependencyResolver;
    this.variants = variants;
    this.aspectLoader = aspectLoader;
    this.logger = logger;
    this.componentList = componentList;
    this.harmony = harmony;
    this.onComponentLoadSlot = onComponentLoadSlot;
    this.onComponentChangeSlot = onComponentChangeSlot;
    this.envs = envs;
    this.globalConfig = globalConfig;
    this.onComponentAddSlot = onComponentAddSlot;
    this.onComponentRemoveSlot = onComponentRemoveSlot;
    this.onAspectsResolveSlot = onAspectsResolveSlot;
    this.onRootAspectAddedSlot = onRootAspectAddedSlot;
    this.graphql = graphql;
    this.onBitmapChangeSlot = onBitmapChangeSlot;
    this.onWorkspaceConfigChangeSlot = onWorkspaceConfigChangeSlot;
    _defineProperty(this, "warnedAboutMisconfiguredEnvs", []);
    // cache env-ids that have been errored about not having "env" type
    _defineProperty(this, "priority", true);
    _defineProperty(this, "owner", void 0);
    _defineProperty(this, "componentsScopeDirsMap", void 0);
    _defineProperty(this, "componentLoader", void 0);
    _defineProperty(this, "componentStatusLoader", void 0);
    _defineProperty(this, "bitMap", void 0);
    /**
     * Indicate that we are now running installation process
     * This is important to know to ignore missing modules across different places
     */
    _defineProperty(this, "inInstallContext", false);
    _defineProperty(this, "componentLoadedSelfAsAspects", void 0);
    // cache loaded components
    _defineProperty(this, "aspectsMerger", void 0);
    _defineProperty(this, "componentDefaultScopeFromComponentDirAndNameWithoutConfigFileMemoized", void 0);
    /**
     * Components paths are calculated from the component package names of the workspace
     * They are used in webpack configuration to only track changes from these paths inside `node_modules`
     */
    _defineProperty(this, "componentPathsRegExps", []);
    _defineProperty(this, "localAspects", {});
    _defineProperty(this, "filter", void 0);
    this.componentLoadedSelfAsAspects = (0, _harmonyModules().createInMemoryCache)({
      maxSize: (0, _harmonyModules().getMaxSizeForComponents)()
    });
    this.componentLoader = new (_workspaceComponentLoader().WorkspaceComponentLoader)(this, logger, dependencyResolver, envs, aspectLoader);
    this.validateConfig();
    // @ts-ignore todo: remove after deleting teambit.legacy
    this.bitMap = new (_bitMap().BitMap)(this.consumer.bitMap, this.consumer);
    // memoize this method to improve performance.
    this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFileMemoized = (0, _memoizee().default)(this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFile.bind(this), {
      primitive: true,
      promise: true,
      maxAge: 60 * 1000 // 1 min
    });
    this.aspectsMerger = new (_aspectsMerger().AspectsMerger)(this, this.harmony);
    this.filter = new (_filter().Filter)(this);
    this.componentStatusLoader = new (_componentStatusLoader().ComponentStatusLoader)(this);
  }
  validateConfig() {
    if (this.consumer.isLegacy) return;
    if ((0, _lodash().isEmpty)(this.config)) throw new (_bitError().BitError)(`fatal: workspace config is empty. probably one of bit files is missing. please run "bit init" to rewrite them`);
    const defaultScope = this.config.defaultScope;
    if (!defaultScope) throw new (_bitError().BitError)('defaultScope is missing');
    if (!(0, _legacyBitId().isValidScopeName)(defaultScope)) throw new (_legacyBitId().InvalidScopeName)(defaultScope);
  }

  /**
   * root path of the Workspace.
   */
  get path() {
    return this.consumer.getPath();
  }

  /**
   * Get the location of the bit roots folder
   */
  get rootComponentsPath() {
    const baseDir = this.config.rootComponentsDirectory != null ? _path().default.join(this.path, this.config.rootComponentsDirectory) : this.modulesPath;
    return _path().default.join(baseDir, _constants().BIT_ROOTS_DIR);
  }

  /** get the `node_modules` folder of this workspace */
  get modulesPath() {
    return _path().default.join(this.path, 'node_modules');
  }
  get isLegacy() {
    return this.consumer.isLegacy;
  }
  registerOnComponentLoad(loadFn) {
    this.onComponentLoadSlot.register(loadFn);
    return this;
  }
  registerOnComponentChange(onComponentChangeFunc) {
    this.onComponentChangeSlot.register(onComponentChangeFunc);
    return this;
  }
  registerOnComponentAdd(onComponentAddFunc) {
    this.onComponentAddSlot.register(onComponentAddFunc);
    return this;
  }
  registerOnComponentRemove(onComponentRemoveFunc) {
    this.onComponentRemoveSlot.register(onComponentRemoveFunc);
    return this;
  }
  registerOnBitmapChange(OnBitmapChangeFunc) {
    this.onBitmapChangeSlot.register(OnBitmapChangeFunc);
    return this;
  }
  registerOnWorkspaceConfigChange(onWorkspaceConfigChangeFunc) {
    this.onWorkspaceConfigChangeSlot.register(onWorkspaceConfigChangeFunc);
  }
  registerOnAspectsResolve(onAspectsResolveFunc) {
    this.onAspectsResolveSlot.register(onAspectsResolveFunc);
    return this;
  }
  registerOnRootAspectAdded(onRootAspectAddedFunc) {
    this.onRootAspectAddedSlot.register(onRootAspectAddedFunc);
    return this;
  }

  /**
   * name of the workspace as configured in either `workspace.json`.
   * defaults to workspace root directory name.
   */
  get name() {
    if (this.config.name) return this.config.name;
    const tokenizedPath = this.path.split('/');
    return tokenizedPath[tokenizedPath.length - 1];
  }
  get icon() {
    return this.config.icon;
  }
  async listAutoTagPendingComponentIds() {
    const componentsList = new (_legacy2().ComponentsList)(this.consumer);
    const modifiedComponents = (await this.modified()).map(c => c.id);
    const newComponents = await componentsList.listNewComponents();
    if (!modifiedComponents || !modifiedComponents.length) return [];
    const autoTagPending = await this.consumer.listComponentsForAutoTagging(_componentId().ComponentIdList.fromArray(modifiedComponents));
    const localOnly = this.listLocalOnly();
    const comps = autoTagPending.filter(autoTagComp => !newComponents.has(autoTagComp.componentId)).filter(autoTagComp => !localOnly.has(autoTagComp.componentId));
    return comps.map(c => c.id);
  }
  async hasModifiedDependencies(component) {
    const listAutoTagPendingComponents = await this.listAutoTagPendingComponentIds();
    const isAutoTag = listAutoTagPendingComponents.find(id => id.isEqualWithoutVersion(component.id));
    if (isAutoTag) return true;
    return false;
  }

  /**
   * get Component issues
   */
  getComponentIssues(component) {
    return component.state._consumer.issues || null;
  }

  /**
   * provides status of all components in the workspace.
   */
  async getComponentStatus(component) {
    const status = await this.getComponentStatusById(component.id);
    const hasModifiedDependencies = await this.hasModifiedDependencies(component);
    return _componentStatus().ComponentStatus.fromLegacy(status, hasModifiedDependencies, component.isOutdated());
  }

  /**
   * list all workspace components.
   */
  async list(filter, loadOpts) {
    const loadOptsWithDefaults = Object.assign(loadOpts || {});
    const ids = this.consumer.bitMap.getAllIdsAvailableOnLane();
    const idsToGet = filter && filter.limit ? (0, _lodash().slice)(ids, filter.offset, filter.offset + filter.limit) : ids;
    return this.getMany(idsToGet, loadOptsWithDefaults);
  }
  async listWithInvalid(loadOpts) {
    const legacyIds = this.consumer.bitMap.getAllIdsAvailableOnLane();
    return this.componentLoader.getMany(legacyIds, loadOpts, false);
  }

  /**
   * list all invalid components.
   * (see the invalid criteria in ConsumerComponent.isComponentInvalidByErrorType())
   */
  async listInvalid() {
    const ids = this.consumer.bitMap.getAllIdsAvailableOnLane();
    return this.componentLoader.getInvalid(ids);
  }

  /**
   * get ids of all workspace components.
   * deleted components are filtered out. (use this.listIdsIncludeRemoved() if you need them)
   */
  listIds() {
    return this.consumer.bitmapIdsFromCurrentLane;
  }
  listIdsIncludeRemoved() {
    return this.consumer.bitmapIdsFromCurrentLaneIncludeRemoved;
  }

  /**
   * Check if a specific id exist in the workspace
   * @param componentId
   */
  hasId(componentId) {
    const ids = this.listIds();
    const found = ids.find(id => {
      return id.isEqual(componentId);
    });
    return !!found;
  }

  /**
   * given component-ids, return the ones that are part of the workspace
   */
  async filterIds(ids) {
    const workspaceIds = await this.listIds();
    return ids.filter(id => workspaceIds.find(wsId => wsId.isEqual(id, {
      ignoreVersion: !id.hasVersion()
    })));
  }

  /**
   * whether or not a workspace has a component with the given name
   */
  async hasName(name) {
    const ids = await this.listIds();
    return Boolean(ids.find(id => id.fullName === name));
  }

  /**
   * Check if a specific id exist in the workspace or in the scope
   * @param componentId
   */
  async hasIdNested(componentId, includeCache = true) {
    const found = await this.hasId(componentId);
    if (found) return found;
    return this.scope.hasIdNested(componentId, includeCache);
  }

  /**
   * list all modified components in the workspace.
   */
  async modified(loadOpts) {
    const {
      components
    } = await this.listWithInvalid(loadOpts);
    const modifiedIncludeNulls = await (0, _pMapSeries().default)(components, async component => {
      const modified = await this.isModified(component);
      return modified ? component : null;
    });
    return (0, _lodash().compact)(modifiedIncludeNulls);
  }

  /**
   * list all new components in the workspace.
   */
  async newComponents() {
    const componentIds = await this.newComponentIds();
    return this.getMany(componentIds);
  }
  async newComponentIds() {
    const allIds = this.listIds();
    return allIds.filter(id => !id.hasVersion());
  }
  async locallyDeletedIds() {
    return this.componentList.listLocallySoftRemoved();
  }
  async duringMergeIds() {
    const duringMerge = this.componentList.listDuringMergeStateComponents();
    return this.resolveMultipleComponentIds(duringMerge);
  }

  /**
   * @deprecated use `listIds()` instead.
   * get all workspace component-ids
   */
  getAllComponentIds() {
    return this.listIds();
  }
  async listTagPendingIds() {
    const newComponents = await this.newComponentIds();
    const modifiedComponents = (await this.modified()).map(c => c.id);
    const removedComponents = await this.locallyDeletedIds();
    const duringMergeIds = await this.duringMergeIds();
    const allIds = [...newComponents, ...modifiedComponents, ...removedComponents, ...duringMergeIds];
    const allIdsUniq = (0, _lodash().uniqBy)(allIds, id => id.toString());
    return allIdsUniq;
  }

  /**
   * list all components that can be tagged. (e.g. when tagging/snapping with --unmodified).
   * which are all components in the workspace, include locally deleted components.
   */
  async listPotentialTagIds() {
    const deletedIds = await this.locallyDeletedIds();
    const allIdsWithoutDeleted = this.listIds();
    return [...deletedIds, ...allIdsWithoutDeleted];
  }
  async getNewAndModifiedIds() {
    const ids = await this.listTagPendingIds();
    return ids;
  }
  async newAndModified() {
    const ids = await this.getNewAndModifiedIds();
    return this.getMany(ids);
  }
  async getLogs(id, shortHash = false, startsFrom) {
    return this.scope.getLogs(id, shortHash, startsFrom);
  }
  async getGraph(ids, shouldThrowOnMissingDep = true) {
    if (!ids || ids.length < 1) ids = await this.listIds();
    return this.buildOneGraphForComponents(ids, undefined, undefined, shouldThrowOnMissingDep);
  }
  async getGraphIds(ids, shouldThrowOnMissingDep = true) {
    if (!ids || ids.length < 1) ids = await this.listIds();
    const graphIdsFromFsBuilder = new (_buildGraphIdsFromFs().GraphIdsFromFsBuilder)(this, this.logger, this.dependencyResolver, shouldThrowOnMissingDep);
    return graphIdsFromFsBuilder.buildGraph(ids);
  }
  async getUnavailableOnMainComponents() {
    const currentLaneId = this.consumer.getCurrentLaneId();
    if (!currentLaneId.isDefault()) return [];
    const allIds = this.consumer.bitMap.getAllBitIdsFromAllLanes();
    const availableIds = this.consumer.bitMap.getAllIdsAvailableOnLane();
    if (allIds.length === availableIds.length) return [];
    const unavailableIds = allIds.filter(id => !availableIds.hasWithoutVersion(id));
    if (!unavailableIds.length) return [];
    const removedIds = this.consumer.bitMap.getRemoved();
    const compsWithHead = [];
    await Promise.all(unavailableIds.map(async id => {
      if (removedIds.has(id)) return; // we don't care about removed components
      const modelComp = await this.scope.legacyScope.getModelComponentIfExist(id);
      if (modelComp && modelComp.head) compsWithHead.push(id);
    }));
    return compsWithHead;
  }
  getDependencies(component) {
    return this.dependencyResolver.getDependencies(component);
  }
  async getSavedGraphOfComponentIfExist(component) {
    if (!component.id.hasVersion()) return null;
    const flattenedEdges = await this.scope.getFlattenedEdges(component.id);
    const versionObj = await this.scope.getBitObjectVersionById(component.id);
    if (!flattenedEdges || !versionObj) return null;
    if (!flattenedEdges.length && versionObj.flattenedDependencies.length) {
      // there are flattenedDependencies, so must be edges, if they're empty, it's because the component was tagged
      // with a version < ~0.0.901, so this flattenedEdges wasn't exist.
      return null;
    }
    const flattenedBitIdCompIdMap = {};
    const getCurrentVersionAsTagIfPossible = () => {
      const currentVer = component.id.version;
      if (!currentVer) return undefined;
      const isCurrentVerAHash = (0, _componentVersion().isHash)(currentVer);
      if (!isCurrentVerAHash) return currentVer;
      const tag = component.tags.byHash(currentVer)?.version.raw;
      return tag || currentVer;
    };
    const currentVersion = getCurrentVersionAsTagIfPossible();
    flattenedBitIdCompIdMap[component.id.changeVersion(currentVersion).toString()] = component.id;
    versionObj.flattenedDependencies.forEach(bitId => {
      flattenedBitIdCompIdMap[bitId.toString()] = bitId;
    });
    const getCompIdByIdStr = idStr => {
      const compId = flattenedBitIdCompIdMap[idStr];
      if (!compId) {
        const suggestWrongSnap = (0, _componentVersion().isHash)(component.id.version) ? `\nplease check that .bitmap has the correct versions of ${component.id.toStringWithoutVersion()}.
it's possible that the version ${component.id.version} belong to ${idStr.split('@')[0]}` : '';
        throw new Error(`id ${idStr} exists in flattenedEdges but not in flattened of ${component.id.toString()}.${suggestWrongSnap}`);
      }
      return compId;
    };
    const nodes = Object.values(flattenedBitIdCompIdMap);
    const edges = flattenedEdges.map(edge => _objectSpread(_objectSpread({}, edge), {}, {
      source: getCompIdByIdStr(edge.source.toString()),
      target: getCompIdByIdStr(edge.target.toString())
    }));
    const graph = new (_graph().Graph)();
    nodes.forEach(node => graph.setNode(new (_graph().Node)(node.toString(), node)));
    edges.forEach(edge => graph.setEdge(new (_graph().Edge)(edge.source.toString(), edge.target.toString(), edge.type)));
    return graph;
  }

  /**
   * given component ids, find their dependents in the workspace
   */
  async getDependentsIds(ids, filterOutNowWorkspaceIds = true) {
    const graph = await this.getGraphIds();
    const dependents = ids.map(id => graph.predecessors(id.toString())).flat().map(node => node.attr);
    const uniq = _componentId().ComponentIdList.uniqFromArray(dependents);
    if (!filterOutNowWorkspaceIds) return uniq;
    const workspaceIds = await this.listIds();
    return uniq.filter(id => workspaceIds.has(id));
  }
  async createAspectList(extensionDataList) {
    const entiresP = extensionDataList.map(entry => this.extensionDataEntryToAspectEntry(entry));
    const entries = await Promise.all(entiresP);
    return this.componentAspect.createAspectListFromEntries(entries);
  }
  async extensionDataEntryToAspectEntry(dataEntry) {
    return new (_component().AspectEntry)(await this.resolveComponentId(dataEntry.id), dataEntry);
  }

  /**
   * this is not the complete legacy component (ConsumerComponent), it's missing dependencies and hooks from Harmony
   * are skipped. do not trust the data you get from this method unless you know what you're doing.
   */
  async getLegacyMinimal(id) {
    try {
      const componentMap = this.consumer.bitMap.getComponent(id);
      return await _component2().default.loadFromFileSystem({
        componentMap,
        id,
        consumer: this.consumer
      });
    } catch (err) {
      return undefined;
    }
  }
  async getFilesModification(id) {
    const bitMapEntry = this.bitMap.getBitmapEntry(id, {
      ignoreVersion: true
    });
    const compDir = bitMapEntry.getComponentDir();
    const compDirAbs = _path().default.join(this.path, compDir);
    const sourceFilesVinyls = bitMapEntry.files.map(file => {
      const filePath = _path().default.join(compDirAbs, file.relativePath);
      return _component3().SourceFile.load(filePath, compDirAbs, this.path, {});
    });
    const repo = this.scope.legacyScope.objects;
    const getModelFiles = async () => {
      const modelComp = await this.scope.legacyScope.getModelComponentIfExist(id);
      if (!modelComp) return [];
      if (!bitMapEntry.id.hasVersion()) return [];
      const verObj = await modelComp.loadVersion(bitMapEntry.id.version, repo);
      return verObj.files;
    };
    return new (_compFiles().CompFiles)(id, repo, sourceFilesVinyls, compDir, await getModelFiles());
  }

  /**
   * get a component from workspace
   * @param id component ID
   */
  async get(componentId, legacyComponent, useCache = true, storeInCache = true, loadOpts) {
    this.logger.debug(`get ${componentId.toString()}`);
    const component = await this.componentLoader.get(componentId, legacyComponent, useCache, storeInCache, loadOpts);
    // When loading a component if it's an env make sure to load it as aspect as well
    // We only want to try load it as aspect if it's the first time we load the component
    const tryLoadAsAspect = this.componentLoadedSelfAsAspects.get(component.id.toString()) === undefined;
    // const config = this.harmony.get<ConfigMain>('teambit.harmony/config');

    // We are loading the component as aspect if it's an env, in order to be able to run the env-preview-template task which run only on envs.
    // Without this loading we will have a problem in case the env is the only component in the workspace. in that case we will load it as component
    // then we don't run it's provider so it doesn't register to the env slot, so we don't know it's an env.
    if (tryLoadAsAspect && this.envs.isUsingEnvEnv(component) && !this.aspectLoader.isCoreAspect(component.id.toStringWithoutVersion()) && !this.aspectLoader.isAspectLoaded(component.id.toString()) && this.hasId(component.id)
    // !config.extension(component.id.toStringWithoutVersion(), true)
    ) {
      try {
        this.componentLoadedSelfAsAspects.set(component.id.toString(), true);
        this.logger.debug(`trying to load self as aspect with id ${component.id.toString()}`);
        // ignore missing modules when loading self
        await this.loadAspects([component.id.toString()], undefined, component.id.toString(), {
          hideMissingModuleError: true
        });
        // In most cases if the load self as aspect failed we don't care about it.
        // we only need it in specific cases to work, but this workspace.get runs on different
        // cases where it might fail (like when importing aspect, after the import objects
        // when we write the package.json we run the applyTransformers which get to pkg which call
        // host.get, but the component not written yet to the fs, so it fails.)
      } catch (e) {
        this.logger.debug(`fail to load self as aspect with id ${component.id.toString()}`);
        this.componentLoadedSelfAsAspects.delete(component.id.toString());
        return component;
      }
    }
    this.componentLoadedSelfAsAspects.set(component.id.toString(), false);
    return component;
  }
  async getConfiguredUserAspectsPackages(options) {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.getConfiguredUserAspectsPackages(options);
  }

  /**
   * clears workspace, scope and all components caches.
   * doesn't clear the dependencies-data from the filesystem-cache.
   */
  async clearCache(options = {}) {
    this.logger.debug('clearing the workspace and scope caches');
    this.aspectLoader.resetFailedLoadAspects();
    if (!options.skipClearFailedToLoadEnvs) this.envs.resetFailedToLoadEnvs();
    await this.scope.clearCache();
    this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFileMemoized.clear();
    this.clearAllComponentsCache();
  }

  /**
   * clear the cache of all components in the workspace.
   * doesn't clear the dependencies-data from the filesystem-cache.
   */
  clearAllComponentsCache() {
    this.logger.debug('clearing all components caches');
    this.componentLoader.clearCache();
    this.consumer.componentLoader.clearComponentsCache();
    this.componentStatusLoader.clearCache();
    this.componentList = new (_legacy2().ComponentsList)(this.consumer);
  }
  clearComponentCache(id) {
    this.componentLoader.clearComponentCache(id);
    this.componentStatusLoader.clearOneComponentCache(id);
    this.consumer.clearOneComponentCache(id);
    this.componentList = new (_legacy2().ComponentsList)(this.consumer);
  }
  async warmCache() {
    await this.list();
  }
  getWorkspaceConfig() {
    const config = this.harmony.get('teambit.harmony/config');
    const workspaceConfig = config.workspaceConfig;
    if (!workspaceConfig) throw new Error('workspace config is missing from Config aspect');
    return workspaceConfig;
  }
  async cleanFromConfig(ids) {
    const workspaceConfig = this.getWorkspaceConfig();
    const wereIdsRemoved = ids.map(id => workspaceConfig.removeExtension(id));
    const hasChanged = wereIdsRemoved.some(isRemoved => isRemoved);
    if (hasChanged) await workspaceConfig.write({
      reasonForChange: 'remove components'
    });
    return hasChanged;
  }
  async triggerOnComponentChange(id, files, removedFiles, watchOpts) {
    const component = await this.get(id);
    const onChangeEntries = this.onComponentChangeSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
    const results = [];
    await (0, _pMapSeries().default)(onChangeEntries, async ([extension, onChangeFunc]) => {
      const onChangeResult = await onChangeFunc(component, files, removedFiles, watchOpts);
      if (onChangeResult) results.push({
        extensionId: extension,
        results: onChangeResult
      });
    });

    // TODO: find way to standardize event names.
    await this.graphql.pubsub.publish(ComponentChanged, {
      componentChanged: {
        component
      }
    });
    return results;
  }
  async triggerOnComponentAdd(id, watchOpts) {
    const component = await this.get(id);
    const onAddEntries = this.onComponentAddSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
    const results = [];
    const files = component.state.filesystem.files.map(file => file.path);
    await (0, _pMapSeries().default)(onAddEntries, async ([extension, onAddFunc]) => {
      const onAddResult = await onAddFunc(component, files, watchOpts);
      if (onAddResult) results.push({
        extensionId: extension,
        results: onAddResult
      });
    });
    await this.graphql.pubsub.publish(ComponentAdded, {
      componentAdded: {
        component
      }
    });
    return results;
  }
  async triggerOnComponentRemove(id) {
    const onRemoveEntries = this.onComponentRemoveSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
    const results = [];
    await (0, _pMapSeries().default)(onRemoveEntries, async ([extension, onRemoveFunc]) => {
      const onRemoveResult = await onRemoveFunc(id);
      results.push({
        extensionId: extension,
        results: onRemoveResult
      });
    });
    await this.graphql.pubsub.publish(ComponentRemoved, {
      componentRemoved: {
        componentIds: [id.toObject()]
      }
    });
    return results;
  }
  async triggerOnBitmapChange() {
    const onBitmapChangeEntries = this.onBitmapChangeSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
    await (0, _pMapSeries().default)(onBitmapChangeEntries, async ([, onBitmapChangeFunc]) => {
      await onBitmapChangeFunc();
    });
  }

  /**
   * the purpose is mostly to reload the workspace config when it changes, so entries like "defaultScope" are updated.
   * it also updates the DependencyResolver config. I couldn't find a good way to update all aspects in workspace.jsonc.
   */
  async triggerOnWorkspaceConfigChange() {
    this.logger.debug('triggerOnWorkspaceConfigChange, reloading workspace config');
    const config = this.harmony.get('teambit.harmony/config');
    await config.reloadWorkspaceConfig(this.path);
    const workspaceConfig = config.workspaceConfig;
    if (!workspaceConfig) throw new Error('workspace config is missing from Config aspect');
    const configOfWorkspaceAspect = workspaceConfig.extensions.findExtension(_workspace().WorkspaceAspect.id);
    if (!configOfWorkspaceAspect) throw new Error('workspace extension is missing from workspace config');
    this.config = configOfWorkspaceAspect.config;
    const configOfDepResolverAspect = workspaceConfig.extensions.findExtension(_dependencyResolver().DependencyResolverAspect.id);
    if (configOfDepResolverAspect) this.dependencyResolver.setConfig(configOfDepResolverAspect.config);
    this.dependencyResolver.clearCache();
    const onWorkspaceConfigChangeEntries = this.onWorkspaceConfigChangeSlot.toArray(); // e.g. [ [ 'teambit.bit/compiler', [Function: bound onComponentChange] ] ]
    await (0, _pMapSeries().default)(onWorkspaceConfigChangeEntries, async ([, onWorkspaceConfigFunc]) => {
      await onWorkspaceConfigFunc();
    });
  }
  getState(id, hash) {
    return this.scope.getState(id, hash);
  }
  getSnap(id, hash) {
    return this.scope.getSnap(id, hash);
  }
  getCurrentLaneId() {
    return this.consumer.getCurrentLaneId();
  }
  async getCurrentLaneObject() {
    return this.consumer.getCurrentLaneObject();
  }
  isOnMain() {
    return this.consumer.isOnMain();
  }
  isOnLane() {
    return this.consumer.isOnLane();
  }

  /**
   * if checked out to a lane and the lane exists in the remote,
   * return the remote lane. otherwise, return null.
   */
  async getCurrentRemoteLane() {
    const currentLaneId = this.getCurrentLaneId();
    if (currentLaneId.isDefault()) {
      return null;
    }
    const scopeComponentImporter = _scopeComponentsImporter().default.getInstance(this.consumer.scope);
    try {
      const lanes = await scopeComponentImporter.importLanes([currentLaneId]);
      return lanes[0];
    } catch (err) {
      if (err instanceof _legacyBitId().InvalidScopeName || err instanceof _scopeNotFoundOrDenied().ScopeNotFoundOrDenied || err instanceof _legacy3().LaneNotFound || err instanceof _legacyBitId().InvalidScopeNameFromRemote) {
        const bitMapLaneId = this.bitMap.getExportedLaneId();
        if (bitMapLaneId?.isEqual(currentLaneId)) {
          throw err; // we know the lane is not new, so the error is legit
        }
        // the lane could be a local lane so no need to throw an error in such case
        _loader().default.stop();
        this.logger.warn(`unable to get lane's data from a remote due to an error:\n${err.message}`);
        return null;
      }
      throw err;
    }
  }
  getDefaultExtensions() {
    if (!this.config.extensions) {
      return new (_extensionData().ExtensionDataList)();
    }
    return _extensionData().ExtensionDataList.fromConfigObject(this.config.extensions);
  }
  async getComponentConfigVinylFile(id, options, excludeLocalChanges = false) {
    const componentId = await this.resolveComponentId(id);
    const extensions = await this.getExtensionsFromScopeAndSpecific(id, excludeLocalChanges);
    const aspects = await this.createAspectList(extensions);
    this.removeEnvVersionIfExistsLocally(aspects);
    const componentDir = this.componentDir(id, {
      ignoreVersion: true
    }, {
      relative: true
    });
    const configFile = new (_componentConfigFile().ComponentConfigFile)(componentId, aspects, componentDir, options.propagate);
    return configFile.toVinylFile(options);
  }
  removeEnvVersionIfExistsLocally(aspects) {
    const env = aspects.get(_envs().EnvsAspect.id)?.config?.env;
    if (!env) return;
    const envAspect = aspects.get(env);
    if (!envAspect) return;
    const envExtId = envAspect.id;
    if (!envExtId?.hasVersion()) return;
    if (!this.exists(envExtId)) return;
    envAspect.id = envExtId.changeVersion(undefined);
  }
  async ejectMultipleConfigs(ids, options) {
    const vinylFiles = await Promise.all(ids.map(id => this.getComponentConfigVinylFile(id, options)));
    const EjectConfResult = vinylFiles.map(file => ({
      configPath: file.path
    }));
    const dataToPersist = new (_component3().DataToPersist)();
    dataToPersist.addManyFiles(vinylFiles);
    dataToPersist.addBasePath(this.path);
    await dataToPersist.persistAllToFS();
    ids.map(id => this.bitMap.removeEntireConfig(id));
    await this.bitMap.write(`eject-conf (${ids.length} component(s))`);
    return EjectConfResult;
  }
  async getAspectConfigForComponent(id, aspectId) {
    const extensions = await this.getExtensionsFromScopeAndSpecific(id);
    const obj = extensions.toConfigObject();
    return obj[aspectId];
  }
  async getExtensionsFromScopeAndSpecific(id, excludeComponentJson = false) {
    const componentFromScope = await this.scope.get(id);
    const exclude = ['WorkspaceVariants'];
    if (excludeComponentJson) exclude.push('ComponentJsonFile');
    const {
      extensions
    } = await this.componentExtensions(id, componentFromScope, exclude);
    return extensions;
  }

  /**
   * @deprecated use `this.idsByPattern` instead for consistency. also, it supports negation and list of patterns.
   *
   * load components into the workspace through a variants pattern.
   * @param pattern variants.
   * @param scope scope name.
   */
  async byPattern(pattern, scope = '**') {
    const ids = await this.listIds();
    const finalPattern = `${scope}/${pattern || '**'}`;
    const targetIds = ids.filter(id => {
      const spec = (0, _workspaceModules().isMatchNamespacePatternItem)(id.toStringWithoutVersion(), finalPattern);
      return spec.match;
    });
    const components = await this.getMany(targetIds);
    return components;
  }
  hasPattern(strArr) {
    return strArr.some(str => this.isPattern(str));
  }
  isPattern(str) {
    const specialSyntax = ['*', ',', '!', '$', ':'];
    return specialSyntax.some(char => str.includes(char));
  }

  /**
   * get component-ids matching the given pattern. a pattern can have multiple patterns separated by a comma.
   * it supports negate (!) character to exclude ids.
   */
  async idsByPattern(pattern, throwForNoMatch = true, opts = {}) {
    const isId = !this.isPattern(pattern);
    if (isId) {
      // if it's not a pattern but just id, resolve it without multimatch to support specifying id without scope-name
      const id = await this.resolveComponentId(pattern);
      if (this.exists(id, {
        includeDeleted: opts.includeDeleted
      })) return [id];
      if (throwForNoMatch) throw new (_legacy().MissingBitMapComponent)(pattern);
      return [];
    }
    const ids = opts.includeDeleted ? this.listIdsIncludeRemoved() : await this.listIds();
    return this.filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch);
  }
  async filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch = true) {
    return this.scope.filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch, this.filter.by.bind(this.filter));
  }

  /**
   * useful for workspace commands, such as `bit build`, `bit compile`.
   * by default, it should be running on new and modified components.
   * a user can specify `--all` to run on all components or specify a pattern to limit to specific components.
   * some commands such as build/test needs to run also on the dependents.
   */
  async getComponentsByUserInput(all, pattern, includeDependents = false) {
    if (all) {
      return this.list();
    }
    if (pattern) {
      const ids = await this.idsByPattern(pattern);
      return this.getMany(ids);
    }
    const newAndModified = await this.newAndModified();
    if (includeDependents) {
      const newAndModifiedIds = newAndModified.map(comp => comp.id);
      const dependentsIds = await this.getDependentsIds(newAndModifiedIds);
      const dependentsIdsFiltered = dependentsIds.filter(id => !newAndModified.find(_ => _.id.isEqual(id)));
      const dependents = await this.getMany(dependentsIdsFiltered);
      newAndModified.push(...dependents);
    }
    return newAndModified;
  }
  async getComponentsUsingEnv(env, ignoreVersion = true, throwIfNotFound = false) {
    const allComps = await this.list();
    const allEnvs = await this.envs.createEnvironment(allComps);
    const foundEnvs = allEnvs.runtimeEnvs.filter(runtimeEnv => {
      if (runtimeEnv.id === env) return true;
      if (!ignoreVersion) return false;
      const envWithoutVersion = runtimeEnv.id.split('@')[0];
      return env === envWithoutVersion;
    });
    if (!foundEnvs.length && throwIfNotFound) {
      const availableEnvs = allEnvs.runtimeEnvs.map(runtimeEnv => runtimeEnv.id);
      throw new (_bitError().BitError)(`unable to find components that using "${env}" env.
the following envs are used in this workspace: ${availableEnvs.join(', ')}`);
    }
    return foundEnvs.map(runtimeEnv => runtimeEnv.components).flat();
  }
  async getMany(ids, loadOpts, throwOnFailure = true) {
    const {
      components
    } = await this.componentLoader.getMany(ids, loadOpts, throwOnFailure);
    return components;
  }
  getManyByLegacy(components, loadOpts) {
    return (0, _pMapSeries().default)(components, async component => {
      const id = component.id;
      return this.get(id, component, true, true, loadOpts);
    });
  }

  /**
   * don't throw an error if the component was not found, simply return undefined.
   */
  async getIfExist(componentId) {
    return this.componentLoader.getIfExist(componentId);
  }

  /**
   * whether a component exists in the workspace
   */
  exists(componentId, opts = {}) {
    const allIds = opts.includeDeleted ? this.listIdsIncludeRemoved() : this.consumer.bitmapIdsFromCurrentLane;
    return allIds.hasWithoutVersion(componentId);
  }
  getIdIfExist(componentId) {
    const id = this.consumer.bitmapIdsFromCurrentLane.find(_ => _.isEqualWithoutVersion(componentId));
    if (!id) return undefined;
    return componentId.changeVersion(id.version);
  }
  mergeBitmaps(bitmapContent, otherBitmapContent, opts = {}) {
    return this.bitMap.mergeBitmaps(bitmapContent, otherBitmapContent, opts);
  }

  /**
   * This will make sure to fetch the objects prior to load them
   * do not use it if you are not sure you need it.
   * It will influence the performance
   * currently it used only for get many of aspects
   * @param ids
   */
  async importAndGetMany(ids, reason, loadOpts, throwOnError = true) {
    if (!ids.length) return [];
    const lane = await this.importCurrentLaneIfMissing();
    await this.scope.import(ids, {
      reFetchUnBuiltVersion: shouldReFetchUnBuiltVersion(),
      preferDependencyGraph: true,
      // add the lane object although it was imported with all its ids previously.
      // in some cases, this import re-fetch existing ids whose their VersionHistory is incomplete, so it needs the Lane context.
      lane,
      reason
    });
    return this.getMany(ids, loadOpts, throwOnError);
  }
  async importCurrentLaneIfMissing() {
    const laneId = this.getCurrentLaneId();
    const laneObj = await this.scope.legacyScope.getCurrentLaneObject();
    if (laneId.isDefault() || laneObj) {
      return laneObj;
    }
    const lane = await this.getCurrentRemoteLane();
    if (!lane) {
      return undefined;
    }
    this.logger.info(`current lane ${laneId.toString()} is missing, importing it`);
    await this.scope.legacyScope.objects.writeObjectsToTheFS([lane]);
    const scopeComponentsImporter = _scopeComponentsImporter().default.getInstance(this.scope.legacyScope);
    const ids = _componentId().ComponentIdList.fromArray(this.getExportedFrom(lane.toBitIds()));
    await scopeComponentsImporter.importWithoutDeps(ids.toVersionLatest(), {
      cache: false,
      lane,
      includeVersionHistory: true,
      reason: 'latest of the current lane'
    });
    await scopeComponentsImporter.importMany({
      ids,
      lane,
      reason: 'for making sure the current lane has all '
    });
    return lane;
  }
  async use(aspectIdStr) {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.use(aspectIdStr);
  }
  async unuse(aspectIdStr) {
    const compId = await this.resolveComponentId(aspectIdStr);
    return this.cleanFromConfig([compId]);
  }
  async write(component, rootPath) {
    await Promise.all(component.filesystem.files.map(async file => {
      const pathToWrite = rootPath ? _path().default.join(this.path, rootPath, file.relative) : file.path;
      await _fsExtra().default.outputFile(pathToWrite, file.contents);
    }));
  }

  /**
   * @todo: the return path here is Linux when asking for relative and os-based when asking for absolute. fix this to be consistent.
   *
   * Get the component root dir in the file system (relative to workspace or full)
   * @param componentId
   * @param relative return the path relative to the workspace or full path
   */
  componentDir(componentId, bitMapOptions, options = {
    relative: false
  }) {
    return this.componentDirFromLegacyId(componentId, bitMapOptions, options);
  }

  /**
   * component's files in the workspace are symlinked to the node_modules, and a package.json file is generated on that
   * package directory to simulate a valid node package.
   * @returns the package directory inside the node_module.
   * by default the absolute path, unless `options.relative` was set
   */
  componentPackageDir(component, options = {
    relative: false
  }) {
    const packageName = this.componentPackageName(component);
    const packageDir = _path().default.join('node_modules', packageName);
    return options.relative ? packageDir : this.consumer.toAbsolutePath(packageDir);
  }
  componentPackageName(component) {
    return this.dependencyResolver.getPackageName(component);
  }
  componentDirFromLegacyId(bitId, bitMapOptions, options = {
    relative: false
  }) {
    const componentMap = this.consumer.bitMap.getComponent(bitId, bitMapOptions);
    const relativeComponentDir = componentMap.getComponentDir();
    if (options.relative) {
      return relativeComponentDir;
    }
    return _path().default.join(this.path, relativeComponentDir);
  }
  componentDirToAbsolute(relativeComponentDir) {
    return _path().default.join(this.path, relativeComponentDir);
  }
  async componentDefaultScope(componentId) {
    const relativeComponentDir = this.componentDir(componentId, {
      ignoreVersion: true
    }, {
      relative: true
    });
    return this.componentDefaultScopeFromComponentDirAndName(relativeComponentDir, componentId.fullName);
  }
  async componentDefaultScopeFromComponentDirAndName(relativeComponentDir, name) {
    const componentConfigFile = await this.componentConfigFileFromComponentDirAndName(relativeComponentDir, name);
    if (componentConfigFile && componentConfigFile.defaultScope) {
      return componentConfigFile.defaultScope;
    }
    const bitMapId = this.consumer.bitMap.getExistingBitId(name, false);
    const bitMapEntry = bitMapId ? this.consumer.bitMap.getComponent(bitMapId) : undefined;
    if (bitMapEntry && bitMapEntry.defaultScope) {
      return bitMapEntry.defaultScope;
    }
    return this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFileMemoized(relativeComponentDir, name);
  }
  get defaultScope() {
    return this.config.defaultScope;
  }
  async componentDefaultScopeFromComponentDirAndNameWithoutConfigFile(relativeComponentDir, name) {
    const variantConfig = this.variants.byRootDirAndName(relativeComponentDir, name);
    if (variantConfig && variantConfig.defaultScope) {
      return variantConfig.defaultScope;
    }
    const isVendor = this.isVendorComponentByComponentDir(relativeComponentDir);
    if (!isVendor) {
      return this.config.defaultScope;
    }
    return undefined;
  }

  /**
   * Calculate the component config based on:
   * the config property in the .bitmap file
   * the component.json file in the component folder
   * matching pattern in the variants config
   * defaults extensions from workspace config
   * extensions from the model.
   */
  async componentExtensions(componentId, componentFromScope, excludeOrigins = [], opts = {}) {
    const optsWithDefaults = Object.assign({
      loadExtensions: true
    }, opts);
    const mergeRes = await this.aspectsMerger.merge(componentId, componentFromScope, excludeOrigins);
    const envId = await this.envs.getEnvIdFromEnvsLegacyExtensions(mergeRes.extensions);
    if (optsWithDefaults.loadExtensions) {
      await this.loadComponentsExtensions(mergeRes.extensions, componentId);
      if (envId) {
        await this.warnAboutMisconfiguredEnv(envId);
      }
    }
    mergeRes.envId = envId;
    return mergeRes;
  }
  async warnAboutMisconfiguredEnv(envId) {
    if (!envId) return;
    if (this.envs.getCoreEnvsIds().includes(envId)) return;
    if (this.warnedAboutMisconfiguredEnvs.includes(envId)) return;
    let env;
    try {
      const parsedEnvId = await this.resolveComponentId(envId);
      env = await this.get(parsedEnvId);
    } catch (err) {
      return; // unable to get the component for some reason. don't sweat it. forget about the warning
    }
    if (!this.envs.isUsingEnvEnv(env)) {
      this.warnedAboutMisconfiguredEnvs.push(envId);
      this.logger.consoleWarning(`env "${envId}" is not of type env. (correct the env's type, or component config with "bit env set ${envId} teambit.envs/env")`);
    }
  }
  getConfigMergeFilePath() {
    return _path().default.join(this.path, _constants().MergeConfigFilename);
  }
  getConflictMergeFile() {
    return this.aspectsMerger.mergeConflictFile;
  }
  getDepsDataOfMergeConfig(id) {
    return this.aspectsMerger.getDepsDataOfMergeConfig(id);
  }

  /**
   * @deprecated
   * the workspace.jsonc conflicts are not written to the config-merge file anymore.
   * see https://github.com/teambit/bit/pull/8393 for more details.
   */
  getWorkspaceJsonConflictFromMergeConfig() {
    const configMergeFile = this.getConflictMergeFile();
    let data;
    let conflict = false;
    try {
      data = configMergeFile.getConflictParsed('WORKSPACE');
    } catch (err) {
      if (!(err instanceof _mergeConfigConflict().MergeConfigConflict)) {
        throw err;
      }
      conflict = true;
      this.logger.error(`unable to parse the config file for the workspace due to conflicts`);
    }
    return {
      data,
      conflict
    };
  }
  getWorkspaceIssues() {
    const errors = [];

    // since PR #8393, the workspace.jsonc conflicts are not written to the config-merge file anymore.
    // @todo remove this in the future. (maybe Q2 of 2024).
    const configMergeFile = this.getConflictMergeFile();
    try {
      configMergeFile.getConflictParsed('WORKSPACE');
    } catch (err) {
      if (err instanceof _mergeConfigConflict().MergeConfigConflict) {
        errors.push(err);
      }
    }
    return errors;
  }
  async listComponentsDuringMerge() {
    const unmergedComps = this.scope.legacyScope.objects.unmergedComponents.getComponents();
    const bitIds = unmergedComps.map(u => _componentId().ComponentID.fromObject(u.id));
    return this.resolveMultipleComponentIds(bitIds);
  }
  async getUnmergedComponent(componentId) {
    const unmerged = this.scope.legacyScope.objects.unmergedComponents.getEntry(componentId);
    if (unmerged?.head) {
      return this.scope.get(componentId.changeVersion(unmerged?.head.toString()));
    }
    return undefined;
  }
  async isModified(component) {
    const head = component.head;
    if (!head) {
      return false; // it's a new component
    }
    const consumerComp = component.state._consumer;
    if (typeof consumerComp._isModified === 'boolean') return consumerComp._isModified;
    const componentStatus = await this.getComponentStatusById(component.id);
    return componentStatus.modified === true;
  }
  async isModifiedOrNew(component) {
    const head = component.head;
    if (!head) {
      return true; // it's a new component
    }
    return this.isModified(component);
  }
  isExported(id) {
    return this.consumer.isExported(id);
  }
  getExportedFrom(ids) {
    const notExported = this.consumer.getNotExportedIds();
    return ids.filter(id => !notExported.hasWithoutVersion(id));
  }

  /**
   * filter the given component-ids and set default-scope only to the new ones.
   * returns the affected components.
   */
  async setDefaultScopeToComponents(componentIds, scopeName) {
    if (!(0, _legacyBitId().isValidScopeName)(scopeName)) {
      throw new (_legacyBitId().InvalidScopeName)(scopeName);
    }
    const newComponentIds = componentIds.filter(id => !id.hasVersion());
    if (!newComponentIds.length) {
      const compIdsStr = componentIds.map(compId => compId.toString()).join(', ');
      throw new (_bitError().BitError)(`unable to set default-scope for the following components, as they are not new:\n${compIdsStr}`);
    }
    newComponentIds.map(comp => this.bitMap.setDefaultScope(comp, scopeName));
    await this.bitMap.write('scope-set');
    return newComponentIds;
  }

  /**
   * @param scopeName
   * @param includeComponents whether to update new components in the workspace to use the new default-scope
   * this is relevant only for new components that were using the previous default-scope
   */
  async setDefaultScope(scopeName, includeComponents = true) {
    if (this.defaultScope === scopeName) {
      throw new Error(`the default-scope is already set as "${scopeName}", nothing to change`);
    }
    if (!(0, _legacyBitId().isValidScopeName)(scopeName)) {
      throw new (_legacyBitId().InvalidScopeName)(scopeName);
    }
    const workspaceConfig = this.getWorkspaceConfig();
    workspaceConfig.setExtension(_workspace().WorkspaceAspect.id, {
      defaultScope: scopeName
    }, {
      mergeIntoExisting: true,
      ignoreVersion: true
    });
    if (includeComponents) {
      // fix also comps using the old default-scope
      this.bitMap.updateDefaultScope(this.config.defaultScope, scopeName);
    }
    this.config.defaultScope = scopeName;
    await workspaceConfig.write({
      reasonForChange: `default-scope (${scopeName})`
    });
    await this.bitMap.write('scope-set');
  }
  async addSpecificComponentConfig(id, aspectId, config = {}, {
    shouldMergeWithExisting,
    shouldMergeWithPrevious
  } = {
    shouldMergeWithExisting: false,
    shouldMergeWithPrevious: false
  }) {
    const componentConfigFile = await this.componentConfigFile(id);
    if (componentConfigFile) {
      await componentConfigFile.addAspect(aspectId, config, this.resolveComponentId.bind(this), shouldMergeWithExisting);
      await componentConfigFile.write({
        override: true
      });
    } else {
      if (shouldMergeWithPrevious) {
        const existingConfig = await this.getAspectConfigForComponent(id, aspectId);
        config = existingConfig ? (0, _lodash().merge)(existingConfig, config) : config;
      }
      this.bitMap.addComponentConfig(id, aspectId, config, shouldMergeWithExisting);
    }
  }
  async removeSpecificComponentConfig(id, aspectId, markWithMinusIfNotExist = false) {
    const componentConfigFile = await this.componentConfigFile(id);
    if (componentConfigFile) {
      await componentConfigFile.removeAspect(aspectId, markWithMinusIfNotExist, this.resolveComponentId.bind(this));
      await componentConfigFile.write({
        override: true
      });
    } else {
      this.bitMap.removeComponentConfig(id, aspectId, markWithMinusIfNotExist);
    }
  }
  async getAspectIdFromConfig(componentId, aspectIdStr, ignoreAspectVersion = false) {
    const config = await this.getExtensionsFromScopeAndSpecific(componentId);
    const aspectEntry = config.findExtension(aspectIdStr, ignoreAspectVersion);
    return aspectEntry?.id.toString();
  }
  async getSpecificComponentConfig(id, aspectId) {
    const componentConfigFile = await this.componentConfigFile(id);
    if (componentConfigFile) {
      return componentConfigFile.aspects.get(aspectId)?.config;
    }
    return this.bitMap.getBitmapEntry(id, {
      ignoreVersion: true
    }).config?.[aspectId];
  }
  isVendorComponentByComponentDir(relativeComponentDir) {
    const vendorDir = this.config.vendor?.directory || DEFAULT_VENDOR_DIR;
    if ((0, _toolboxPath2().isPathInside)(relativeComponentDir, vendorDir)) {
      return true;
    }
    // TODO: implement
    return false;
  }

  /**
   * return the component config from its folder (component.json)
   * @param componentId
   */
  async componentConfigFile(id) {
    const relativeComponentDir = this.componentDir(id, {
      ignoreVersion: true
    }, {
      relative: true
    });
    return this.componentConfigFileFromComponentDirAndName(relativeComponentDir, id.fullName);
  }

  /**
   * @param componentPath can be relative or absolute. supports Linux and Windows
   */
  async getComponentIdByPath(componentPath) {
    const relativePath = _path().default.isAbsolute(componentPath) ? _path().default.relative(this.path, componentPath) : componentPath;
    const linuxPath = (0, _toolboxPath().pathNormalizeToLinux)(relativePath);
    const bitId = this.consumer.bitMap.getComponentIdByPath(linuxPath);
    if (bitId) {
      return bitId;
    }
    return undefined;
  }
  async componentConfigFileFromComponentDirAndName(relativeComponentDir, name) {
    let componentConfigFile;
    if (relativeComponentDir) {
      const absComponentDir = this.componentDirToAbsolute(relativeComponentDir);
      const defaultScopeFromVariantsOrWs = await this.componentDefaultScopeFromComponentDirAndNameWithoutConfigFileMemoized(relativeComponentDir, name);
      componentConfigFile = await _componentConfigFile().ComponentConfigFile.load(absComponentDir, this.createAspectList.bind(this), defaultScopeFromVariantsOrWs);
    }
    return componentConfigFile;
  }

  /**
   * load aspects from the workspace and if not exists in the workspace, load from the scope.
   * keep in mind that the graph may have circles.
   */
  async loadAspects(ids = [], throwOnError = false, neededFor, opts = {}) {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.loadAspects(ids, throwOnError, neededFor, opts);
  }
  async loadComponentsExtensions(extensions, originatedFrom, opts = {}) {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.loadComponentsExtensions(extensions, originatedFrom, opts);
  }

  /**
   * returns one graph that includes all dependencies types. each edge has a label of the dependency
   * type. the nodes content is the Component object.
   */
  async buildOneGraphForComponents(ids, ignoreIds, shouldLoadFunc, shouldThrowOnMissingDep = true) {
    const graphFromFsBuilder = new (_buildGraphFromFs().GraphFromFsBuilder)(this, this.logger, this.dependencyResolver, ignoreIds, shouldLoadFunc, shouldThrowOnMissingDep);
    return graphFromFsBuilder.buildGraph(ids);
  }
  async resolveAspects(runtimeName, componentIds, opts) {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.resolveAspects(runtimeName, componentIds, opts);
  }

  /**
   * Provides a cache folder, unique per key.
   * Return value may be undefined, if workspace folder is unconventional (bare-scope, no node_modules, etc)
   */
  getTempDir(
  /*
   * unique key, i.e. aspect or component id
   */
  id) {
    const PREFIX = 'bit';
    const cacheDir = _path().default.join(this.modulesPath, '.cache', PREFIX, id);

    // maybe should also check it's a folder and has write permissions
    if (!_fsExtra().default.existsSync(cacheDir)) {
      _fsExtra().default.mkdirSync(cacheDir, {
        recursive: true
      });
    }
    return cacheDir;
  }
  getWorkspaceAspectsLoader() {
    let resolveEnvsFromRoots = this.config.resolveEnvsFromRoots;
    if (resolveEnvsFromRoots === undefined) {
      const resolveEnvsFromRootsConfig = this.globalConfig.getSync(_constants().CFG_DEFAULT_RESOLVE_ENVS_FROM_ROOTS);
      const defaultResolveEnvsFromRoots =
      // @ts-ignore
      resolveEnvsFromRootsConfig === 'true' || resolveEnvsFromRootsConfig === true;
      resolveEnvsFromRoots = defaultResolveEnvsFromRoots;
    }
    const workspaceAspectsLoader = new (_workspaceAspectsLoader().WorkspaceAspectsLoader)(this, this.scope, this.aspectLoader, this.envs, this.dependencyResolver, this.logger, this.globalConfig, this.harmony, this.onAspectsResolveSlot, this.onRootAspectAddedSlot, this.config.resolveAspectsFromNodeModules, resolveEnvsFromRoots);
    return workspaceAspectsLoader;
  }
  getCapsulePath() {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.getCapsulePath();
  }
  shouldUseHashForCapsules() {
    const workspaceAspectsLoader = this.getWorkspaceAspectsLoader();
    return workspaceAspectsLoader.shouldUseHashForCapsules();
  }

  /**
  }
   /**
   * this should be rarely in-use.
   * it's currently used by watch extension as a quick workaround to load .bitmap and the components
   */
  async _reloadConsumer() {
    this.consumer = await (0, _consumer().loadConsumer)(this.path, true);
    // @ts-ignore todo: remove after deleting teambit.legacy
    this.bitMap = new (_bitMap().BitMap)(this.consumer.bitMap, this.consumer);
    await this.clearCache();
  }
  async getComponentPackagePath(component) {
    const relativePath = this.dependencyResolver.getRuntimeModulePath(component, {
      workspacePath: this.path,
      rootComponentsPath: this.rootComponentsPath,
      isInWorkspace: this.hasId(component.id)
    });
    return _path().default.join(this.path, relativePath);
  }

  // TODO: should we return here the dir as it defined (aka components) or with /{name} prefix (as it used in legacy)
  get defaultDirectory() {
    return this.config.defaultDirectory;
  }
  get legacyDefaultDirectory() {
    if (this.defaultDirectory && !this.defaultDirectory.includes('{name}')) {
      return `${this.defaultDirectory}/{name}`;
    }
    return this.defaultDirectory;
  }
  async resolveComponentIdFromPackageName(packageName) {
    if (!packageName.startsWith('@')) {
      throw new Error(`findComponentIdFromPackageName supports only packages that start with @, got ${packageName}`);
    }
    const errMsgPrefix = `unable to resolve a component-id from the package-name ${packageName}, `;
    const pkgJsonPath = _path().default.join(this.path, 'node_modules', packageName, 'package.json');
    let pkgJson;
    try {
      pkgJson = await _fsExtra().default.readJson(pkgJsonPath);
    } catch (err) {
      // never mind the reason. probably it's not there.
    }
    if (pkgJson) {
      const compId = pkgJson.componentId;
      if (!compId) {
        throw new (_bitError().BitError)(`${errMsgPrefix}the package.json file has no componentId field, it's probably not a component`);
      }
      return _componentId().ComponentID.fromObject(compId);
    }
    const url = `https://node-registry.bit.cloud/${packageName}`;
    const token = await this.globalConfig.get(_constants().CFG_USER_TOKEN_KEY);
    const headers = token ? (0, _http().getAuthHeader)(token) : {};
    const res = await (0, _http().fetchWithAgent)(url, {
      headers
    });
    if (!res.ok) {
      throw new (_bitError().BitError)(`${errMsgPrefix}got ${res.statusText} from the url: ${url}`);
    }
    const data = await res.json();
    const latest = data['dist-tags'].latest;
    if (!latest) throw new (_bitError().BitError)(`${errMsgPrefix}the "dist-tags" has no latest field`);
    const version = data.versions[latest];
    if (!version) throw new (_bitError().BitError)(`${errMsgPrefix}the "versions" is missing the latest "${latest}" field`);
    const compId = version.componentId;
    if (!compId) throw new (_bitError().BitError)(`${errMsgPrefix}the package.json of version "${latest}" has no componentId field, it's probably not a component`);
    return _componentId().ComponentID.fromObject(compId).changeVersion(undefined);
  }

  /**
   * Transform the id to ComponentId and get the exact id as appear in bitmap
   */
  async resolveComponentId(id) {
    if (id instanceof _legacyBitId().BitId && id.hasScope() && id.hasVersion()) {
      // an optimization to make it faster when BitId is passed
      return _componentId().ComponentID.fromLegacy(id);
    }
    if (id instanceof _componentId().ComponentID && id.hasVersion()) {
      return id;
    }
    if (typeof id === 'string' && id.startsWith('@')) {
      return this.resolveComponentIdFromPackageName(id);
    }
    const getDefaultScope = async (bitId, bitMapOptions) => {
      if (bitId.scope) {
        return bitId.scope;
      }
      const relativeComponentDir = this.componentDirFromLegacyId(bitId, bitMapOptions, {
        relative: true
      });
      const defaultScope = await this.componentDefaultScopeFromComponentDirAndName(relativeComponentDir, bitId.fullName);
      return defaultScope;
    };

    // This is required in case where you have in your workspace a component with the same name as a core aspect
    // let's say you have component called react-native (which is eventually my-org.my-scope/react-native)
    // and you set teambit.react/react-native as your env
    // bit will get here with the string teambit.react/react-native and will try to resolve it from the workspace
    // during this it will find the my-org.my-scope/react-native which is incorrect as the core one doesn't exist in the
    // workspace
    if (this.aspectLoader.isCoreAspect(id.toString())) {
      return _componentId().ComponentID.fromString(id.toString());
    }
    let legacyId = this.consumer.getParsedIdIfExist(id.toString(), true, true);
    if (legacyId) {
      const defaultScope = await getDefaultScope(legacyId);
      // only reason to strip the scopeName from the given id is when this id has the defaultScope, because .bitmap
      // doesn't have the defaultScope. if the given id doesn't have scope or has scope different than the default,
      // then don't ignore it. search with the scope-name.
      const shouldSearchWithoutScopeInProvidedId = id.toString().startsWith(`${defaultScope}/`);
      legacyId = this.consumer.getParsedIdIfExist(id.toString(), true, shouldSearchWithoutScopeInProvidedId);
      if (legacyId) {
        return _componentId().ComponentID.fromLegacy(legacyId._legacy, defaultScope);
      }
    }
    try {
      const idWithVersion = id.toString();
      const [idWithoutVersion, version] = id.toString().split('@');
      const _bitMapId = this.consumer.getParsedIdIfExist(idWithoutVersion, false, true);
      // This logic is very specific, and very sensitive for changes please do not touch this without consulting with @ran or @gilad
      // example (partial list) cases which should be handled are:
      // use case 1 - ws component provided with the local scope name:
      // source id        : my-scope1/my-name1
      // bitmap res (_id) : my-name1 (comp is tagged but not exported)
      // local scope name : my-scope1
      // scope content    : my-name1
      // expected result  : my-name1
      // use case 2 - component with same name exist in ws and scope (but with different scope name)
      // source id        : my-scope2/my-name1
      // bitmap res (_id) : my-name1 (comp exist in ws but it's actually different component)
      // local scope name : my-scope1
      // scope content    : my-scope2/my-name1
      // expected result  : my-scope2/my-name1
      // use case 3 - component with same name exist in ws and scope (but with different scope name) - source provided without scope name
      // source id        : my-name1
      // bitmap res (_id) : my-name1 (comp exist in ws but it's actually different component)
      // local scope name : my-scope1
      // scope content    : my-scope1/my-name1 and my-scope2/my-name1
      // expected result  : my-name1 (get the name from the bitmap)
      // use case 4 - component with the same name and different scope are imported into the ws
      // source id        : my-name1
      // bitmap res (_id) : my-scope2/my-name1 (comp exist in ws from different scope (imported))
      // local scope name : my-scope1
      // scope content    : my-scope2/my-name1
      // expected result  : my-scope2/my-name1 (get the name from the bitmap)

      // No entry in bitmap at all, search for the original input id
      if (!_bitMapId) {
        return await this.scope.resolveComponentId(id.toString());
      }
      const _bitMapIdWithoutVersion = _bitMapId.toStringWithoutVersion();
      const _bitMapIdWithVersion = _bitMapId.changeVersion(version).toString();
      // The id in the bitmap has prefix which is not in the source id - the bitmap entry has scope name
      // Handle use case 4
      if (_bitMapIdWithoutVersion.endsWith(idWithoutVersion) && _bitMapIdWithoutVersion !== idWithoutVersion) {
        return await this.scope.resolveComponentId(_bitMapIdWithVersion);
      }
      // Handle case when I tagged the component locally with a default scope which is not the local scope
      // but not exported it yet
      // now i'm trying to load it with source id contain the default scope prefix
      // we want to get it from the local first before assuming it's something coming from outside
      if (!_bitMapId.scope) {
        const defaultScopeForBitmapId = await getDefaultScope(_bitMapId, {
          ignoreVersion: true
        });
        const getFromBitmapAddDefaultScope = () => {
          let _bitmapIdWithVersionForSource = _bitMapId;
          if (version) {
            _bitmapIdWithVersionForSource = _bitMapId.changeVersion(version);
          }
          return _componentId().ComponentID.fromLegacy(_bitmapIdWithVersionForSource._legacy, defaultScopeForBitmapId);
        };
        // a case when the given id contains the default scope
        if (idWithVersion.startsWith(`${defaultScopeForBitmapId}/${_bitMapIdWithoutVersion}`)) {
          return getFromBitmapAddDefaultScope();
        }
        // a case when the given id does not contain the default scope
        const fromScope = await this.scope.resolveComponentId(idWithVersion);
        if (!fromScope._legacy.hasScope()) {
          return getFromBitmapAddDefaultScope();
        }
      }
      if (idWithoutVersion.endsWith(_bitMapIdWithoutVersion) && _bitMapIdWithoutVersion !== idWithoutVersion) {
        // The id in the bitmap doesn't have scope, the source id has scope
        // Handle use case 2 and use case 1
        if (id.toString().startsWith(this.scope.name)) {
          // Handle use case 1 - the provided id has scope name same as the local scope name
          // we want to send it as it appear in the bitmap
          return await this.scope.resolveComponentId(_bitMapIdWithVersion);
        }
        // Handle use case 2 - the provided id has scope which is not the local scope
        // we want to search by the source id
        return await this.scope.resolveComponentId(idWithVersion);
      }
      // Handle use case 3
      return await this.scope.resolveComponentId(idWithVersion);
    } catch (error) {
      return _componentId().ComponentID.fromString(id.toString());
    }
  }
  async resolveMultipleComponentIds(ids) {
    return Promise.all(ids.map(async id => this.resolveComponentId(id)));
  }

  /**
   * component-id coming from Scope don't have the defaultScope, the legacyComponentId.scope is always populated.
   * in the .bitmap we need to distinguish between the two, so the componentId needs to be corrected with the defaultScope.
   */
  resolveIdWithDefaultScope(componentId) {
    const isExported = !this.consumer.getNotExportedIds().searchWithoutVersion(componentId);
    const bitId = componentId._legacy.changeScope(isExported ? componentId.scope : undefined);
    return _componentId().ComponentID.fromLegacy(bitId, componentId.scope);
  }

  /**
   * This will mutate the original extensions list and resolve it's ids
   *
   * @param {ExtensionDataList} extensions
   * @returns {Promise<void[]>}
   * @memberof Workspace
   */
  resolveExtensionsList(extensions) {
    const resolveMergedExtensionsP = extensions.map(async extensionEntry => {
      if (extensionEntry.extensionId) {
        // const hasVersion = extensionEntry.extensionId.hasVersion();
        // const useBitmapVersion = !hasVersion;
        // const resolvedId = await this.resolveComponentId(extensionEntry.extensionId, true, useBitmapVersion);

        // Assuming extensionId always has scope - do not allow extension id without scope
        const resolvedId = await this.resolveComponentId(extensionEntry.extensionId);
        extensionEntry.extensionId = resolvedId;
      }
    });
    return Promise.all(resolveMergedExtensionsP);
  }

  /**
   * configure an environment to the given components in the .bitmap file, this configuration overrides other, such as
   * overrides in workspace.jsonc.
   */
  async setEnvToComponents(envId, componentIds, verifyEnv = true) {
    const envStrWithPossiblyVersion = await this.resolveEnvIdWithPotentialVersionForConfig(envId);
    if (verifyEnv) {
      const envComp = await this.get(_componentId().ComponentID.fromString(envStrWithPossiblyVersion));
      const isEnv = this.envs.isEnv(envComp);
      if (!isEnv) throw new (_bitError().BitError)(`the component ${envComp.id.toString()} is not an env`);
    }
    const envIdStrNoVersion = envId.toStringWithoutVersion();
    await this.unsetEnvFromComponents(componentIds);
    await Promise.all(componentIds.map(async componentId => {
      await this.addSpecificComponentConfig(componentId, envStrWithPossiblyVersion);
      await this.addSpecificComponentConfig(componentId, _envs().EnvsAspect.id, {
        env: envIdStrNoVersion
      });
    }));
    await this.bitMap.write(`env-set (${envId.toString()})`);
  }

  /**
   * helpful when a user provides an env-string to be set and this env has no version.
   * in the workspace config, a custom-env needs to be set with a version unless it's part of the workspace.
   * (inside envs/envs it's set without a version).
   */
  async resolveEnvIdWithPotentialVersionForConfig(envId) {
    const isCore = this.aspectLoader.isCoreAspect(envId.toStringWithoutVersion());
    const existsOnWorkspace = await this.hasId(envId);
    if (isCore || existsOnWorkspace) {
      // the env needs to be without version
      return envId.toStringWithoutVersion();
    }
    // the env must include a version
    if (envId.hasVersion()) {
      return envId.toString();
    }
    const extensions = this.harmony.get('teambit.harmony/config').extensions;
    const found = extensions?.findExtension(envId.toString(), true);
    if (found && found.extensionId?.version) {
      return found.extensionId.toString();
    }
    const comps = await this.importAndGetMany([envId], `to get the env ${envId.toString()}`);
    const comp = comps[0];
    if (!comp) throw new (_bitError().BitError)(`unable to find ${envId.toString()} in the workspace or in the remote`);
    return comp.id.toString();
  }

  /**
   * remove env configuration from the .bitmap file, so then other configuration, such as "variants" will take place
   */
  async unsetEnvFromComponents(ids) {
    const changed = [];
    const unchanged = [];
    await Promise.all(ids.map(async id => {
      const extensions = await this.getExtensionsFromScopeAndSpecific(id);
      const envsAspect = extensions.findCoreExtension(_envs().EnvsAspect.id)?.rawConfig;
      const currentEnv = envsAspect && envsAspect !== _config().REMOVE_EXTENSION_SPECIAL_SIGN ? envsAspect.env : null;
      if (!currentEnv) {
        unchanged.push(id);
        return;
      }
      // the env that gets saved in the .bitmap file config root can be with or without version.
      // e.g. when a custom env is in .bitmap, it's saved without version, but when asking the component for the
      // env by `this.getAspectIdFromConfig`, it returns the env with version.
      // to make sure we remove the env from the .bitmap, we need to remove both with and without version.
      const currentEnvWithPotentialVersion = await this.getAspectIdFromConfig(id, currentEnv, true);
      await this.removeSpecificComponentConfig(id, currentEnv);
      if (currentEnvWithPotentialVersion && currentEnvWithPotentialVersion.includes('@')) {
        await this.removeSpecificComponentConfig(id, currentEnvWithPotentialVersion);
      }
      await this.removeSpecificComponentConfig(id, _envs().EnvsAspect.id);
      changed.push(id);
    }));
    await this.bitMap.write(`env-unset`);
    return {
      changed,
      unchanged
    };
  }
  async updateEnvForComponents(envIdStr, pattern) {
    const allWsComps = await this.list();
    const allWsIds = await this.listIds();
    const isInWs = envId => allWsIds.find(id => id.isEqual(envId, {
      ignoreVersion: true
    }));
    const allEnvs = await this.envs.createEnvironment(allWsComps);
    const getEnvWithVersion = async envId => {
      if (envId.hasVersion()) return envId;
      if (isInWs(envId)) return envId;
      try {
        const fromRemote = await this.scope.getRemoteComponent(envId);
        return envId.changeVersion(fromRemote.id.version);
      } catch (err) {
        throw new (_bitError().BitError)(`unable to find ${envIdStr} in the remote`);
      }
    };
    const getEnvs = async () => {
      if (envIdStr) {
        const envCompId = await this.resolveComponentId(envIdStr);
        const envWithVer = await getEnvWithVersion(envCompId);
        return [envWithVer];
      }
      const allEnvsIds = allEnvs.runtimeEnvs.map(env => env.id);
      const allEnvsCompIds = await this.resolveMultipleComponentIds(allEnvsIds);
      // check whether the envId has version, otherwise, it's a core env.
      const nonCoreEnvs = allEnvsCompIds.filter(envId => envId.hasVersion());
      const envsWithVersions = await (0, _pMapSeries().default)(nonCoreEnvs, envId => getEnvWithVersion(envId.changeVersion(undefined)));
      return envsWithVersions;
    };
    const envsWithVerToUpdate = await getEnvs();
    const compIdsToUpdate = pattern ? await this.idsByPattern(pattern) : allWsIds;
    const compsToUpdate = await this.getMany(compIdsToUpdate);
    const alreadyUpToDate = [];
    const updated = {};
    await Promise.all(compsToUpdate.map(async comp => {
      const compEnvs = (0, _lodash().compact)(envsWithVerToUpdate.map(envId => comp.state.aspects.get(envId.toStringWithoutVersion())));
      if (!compEnvs.length) return;
      const compEnv = compEnvs[0]; // should not be more than one
      const envToUpdate = envsWithVerToUpdate.find(e => e.isEqual(compEnv.id, {
        ignoreVersion: true
      }));
      if (!envToUpdate) throw new Error(`unable to find ${compEnv.id.toString()} in the envs to update`);
      const envIsInWs = isInWs(envToUpdate);
      if (!envIsInWs && compEnv.id.version === envToUpdate.version) {
        // nothing to update
        alreadyUpToDate.push(comp.id);
        return;
      }
      if (envIsInWs && !(await this.getSpecificComponentConfig(comp.id, compEnv.id.toString()))) {
        // compEnv has version. If this id with version doesn't exist in .bitmap, either, it's not saved in .bitmap
        // (probably it's in the model) or it's in .bitmap without version (as expected). either way, nothing to update.
        alreadyUpToDate.push(comp.id);
        return;
      }
      // don't mark with minus if not exist in .bitmap. it's not needed. when the component is loaded, the
      // merge-operation of the aspects removes duplicate aspect-id with different versions.
      await this.removeSpecificComponentConfig(comp.id, compEnv.id.toString(), false);
      await this.addSpecificComponentConfig(comp.id, envIsInWs ? envToUpdate.toStringWithoutVersion() : envToUpdate.toString(), compEnv.config);
      (updated[envToUpdate.toString()] ||= []).push(comp.id);
    }));
    await this.bitMap.write('env-update');
    return {
      updated,
      alreadyUpToDate
    };
  }
  getComponentPathsRegExps() {
    return this.componentPathsRegExps;
  }
  async setComponentPathsRegExps() {
    const workspaceComponents = await this.list();
    const workspacePackageNames = workspaceComponents.map(c => this.componentPackageName(c));
    const packageManager = this.dependencyResolver.getPackageManagerName();
    const isPnpmEnabled = typeof packageManager === 'undefined' || packageManager.includes('pnpm');
    const pathsExcluding = [(0, _dependenciesModules().generateNodeModulesPattern)({
      packages: workspacePackageNames,
      target: _dependenciesModules().PatternTarget.WEBPACK,
      isPnpmEnabled
    })];
    this.componentPathsRegExps = pathsExcluding.map(stringPattern => new RegExp(stringPattern));
  }
  getInjectedDirs(component) {
    const relativeCompDir = this.componentDir(component.id, undefined, {
      relative: true
    });
    return this.dependencyResolver.getInjectedDirs(this.path, relativeCompDir, this.dependencyResolver.getPackageName(component));
  }
  async getAutoDetectOverrides(configuredExtensions, id, legacyFiles) {
    let policy = await this.dependencyResolver.mergeVariantPolicies(configuredExtensions, id, legacyFiles);
    // this is needed for "bit install" to install the dependencies from the merge config (see https://github.com/teambit/bit/pull/6849)
    const depsDataOfMergeConfig = this.getDepsDataOfMergeConfig(id);
    if (depsDataOfMergeConfig) {
      const policiesFromMergeConfig = _dependencyResolver().VariantPolicy.fromConfigObject(depsDataOfMergeConfig, {
        source: 'auto'
      });
      policy = _dependencyResolver().VariantPolicy.mergePolices([policy, policiesFromMergeConfig]);
    }
    return policy.toLegacyAutoDetectOverrides();
  }
  getAutoDetectConfigMerge(id) {
    const depsDataOfMergeConfig = this.getDepsDataOfMergeConfig(id);
    if (depsDataOfMergeConfig) {
      const policy = _dependencyResolver().VariantPolicy.fromConfigObject(depsDataOfMergeConfig, {
        source: 'auto'
      });
      return policy.toLegacyAutoDetectOverrides();
    }
    return undefined;
  }
  async getManyComponentsStatuses(ids) {
    return this.componentStatusLoader.getManyComponentsStatuses(ids);
  }
  async getComponentStatusById(id) {
    return this.componentStatusLoader.getComponentStatusById(id);
  }
  async setLocalOnly(ids) {
    const staged = (0, _lodash().compact)(await (0, _pMapSeries().default)(ids, async id => {
      const componentStatus = await this.getComponentStatusById(id);
      if (componentStatus.staged) return id;
    }));
    if (staged.length) {
      throw new (_bitError().BitError)(`unable to set the following component(s) as local-only because they have local snaps/tags: ${staged.join(', ')}`);
    }
    this.bitMap.setLocalOnly(ids);
    await this.bitMap.write('setLocalOnly');
  }
  async unsetLocalOnly(ids) {
    const successfullyUnset = this.bitMap.unsetLocalOnly(ids);
    await this.bitMap.write('unsetLocalOnly');
    return successfullyUnset;
  }
  listLocalOnly() {
    return _componentId().ComponentIdList.fromArray(this.bitMap.listLocalOnly());
  }
}

/**
 * this is a super hacky way to do it. problem is that loadAspect is running as onStart hook, where we don't
 * have the CLI fully loaded yet, so we can't get the command from the CLI aspect, we have to retrieve it from
 * process.argv.
 * in general, we don't want every command to try again and again fetching un-built versions. otherwise, every time
 * Bit loads (even bit --help), it'll fetch them and slow down everything.
 * instead, long-running commands and those that need the artifacts from the Version objects, should try to re-fetch.
 */
exports.Workspace = Workspace;
function shouldReFetchUnBuiltVersion() {
  const commandsToReFetch = ['import'];
  return commandsToReFetch.includes(process.argv[2]);
}
var _default = exports.default = Workspace;

//# sourceMappingURL=workspace.js.map