"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ScopeMain = void 0;
function _globalConfig() {
  const data = require("@teambit/global-config");
  _globalConfig = function () {
    return data;
  };
  return data;
}
function _pMapSeries() {
  const data = _interopRequireDefault(require("p-map-series"));
  _pMapSeries = function () {
    return data;
  };
  return data;
}
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _graph() {
  const data = require("@teambit/graph.cleargraph");
  _graph = function () {
    return data;
  };
  return data;
}
function _semver() {
  const data = _interopRequireDefault(require("semver"));
  _semver = function () {
    return data;
  };
  return data;
}
function _multimatch() {
  const data = _interopRequireDefault(require("multimatch"));
  _multimatch = function () {
    return data;
  };
  return data;
}
function _builder() {
  const data = require("@teambit/builder");
  _builder = function () {
    return data;
  };
  return data;
}
function _aspectLoader() {
  const data = require("@teambit/aspect-loader");
  _aspectLoader = function () {
    return data;
  };
  return data;
}
function _cli() {
  const data = require("@teambit/cli");
  _cli = function () {
    return data;
  };
  return data;
}
function _component() {
  const data = require("@teambit/component");
  _component = function () {
    return data;
  };
  return data;
}
function _graphql() {
  const data = require("@teambit/graphql");
  _graphql = function () {
    return data;
  };
  return data;
}
function _harmony() {
  const data = require("@teambit/harmony");
  _harmony = function () {
    return data;
  };
  return data;
}
function _isolator() {
  const data = require("@teambit/isolator");
  _isolator = function () {
    return data;
  };
  return data;
}
function _logger() {
  const data = require("@teambit/logger");
  _logger = function () {
    return data;
  };
  return data;
}
function _express() {
  const data = require("@teambit/express");
  _express = function () {
    return data;
  };
  return data;
}
function _ui() {
  const data = require("@teambit/ui");
  _ui = function () {
    return data;
  };
  return data;
}
function _componentId() {
  const data = require("@teambit/component-id");
  _componentId = function () {
    return data;
  };
  return data;
}
function _objects() {
  const data = require("@teambit/legacy/dist/scope/objects");
  _objects = function () {
    return data;
  };
  return data;
}
function _scopeLoader() {
  const data = require("@teambit/legacy/dist/scope/scope-loader");
  _scopeLoader = function () {
    return data;
  };
  return data;
}
function _getDivergeData() {
  const data = require("@teambit/legacy/dist/scope/component-ops/get-diverge-data");
  _getDivergeData = function () {
    return data;
  };
  return data;
}
function _actions() {
  const data = require("@teambit/legacy/dist/scope/actions");
  _actions = function () {
    return data;
  };
  return data;
}
function _dependencyResolver() {
  const data = require("@teambit/dependency-resolver");
  _dependencyResolver = function () {
    return data;
  };
  return data;
}
function _scopeRemotes() {
  const data = require("@teambit/legacy/dist/scope/scope-remotes");
  _scopeRemotes = function () {
    return data;
  };
  return data;
}
function _workspaceModules() {
  const data = require("@teambit/workspace.modules.match-pattern");
  _workspaceModules = function () {
    return data;
  };
  return data;
}
function _scope() {
  const data = require("@teambit/legacy/dist/scope");
  _scope = function () {
    return data;
  };
  return data;
}
function _chokidar() {
  const data = _interopRequireDefault(require("chokidar"));
  _chokidar = function () {
    return data;
  };
  return data;
}
function _objectList() {
  const data = require("@teambit/legacy/dist/scope/objects/object-list");
  _objectList = function () {
    return data;
  };
  return data;
}
function _http() {
  const data = require("@teambit/legacy/dist/scope/network/http/http");
  _http = function () {
    return data;
  };
  return data;
}
function _legacy() {
  const data = require("@teambit/legacy.scope-api");
  _legacy = function () {
    return data;
  };
  return data;
}
function _bitError() {
  const data = require("@teambit/bit-error");
  _bitError = function () {
    return data;
  };
  return data;
}
function _exportScopeComponents() {
  const data = require("@teambit/legacy/dist/scope/component-ops/export-scope-components");
  _exportScopeComponents = function () {
    return data;
  };
  return data;
}
function _constants() {
  const data = require("@teambit/legacy/dist/constants");
  _constants = function () {
    return data;
  };
  return data;
}
function _legacyBitId() {
  const data = require("@teambit/legacy-bit-id");
  _legacyBitId = function () {
    return data;
  };
  return data;
}
function _config() {
  const data = require("@teambit/legacy/dist/consumer/config");
  _config = function () {
    return data;
  };
  return data;
}
function _envs() {
  const data = require("@teambit/envs");
  _envs = function () {
    return data;
  };
  return data;
}
function _lodash() {
  const data = require("lodash");
  _lodash = function () {
    return data;
  };
  return data;
}
function _config2() {
  const data = require("@teambit/legacy/dist/global-config/config");
  _config2 = function () {
    return data;
  };
  return data;
}
function _globalConfig2() {
  const data = require("@teambit/legacy/dist/api/consumer/lib/global-config");
  _globalConfig2 = function () {
    return data;
  };
  return data;
}
function _exceptions() {
  const data = require("./exceptions");
  _exceptions = function () {
    return data;
  };
  return data;
}
function _scope2() {
  const data = require("./scope.aspect");
  _scope2 = function () {
    return data;
  };
  return data;
}
function _scope3() {
  const data = require("./scope.graphql");
  _scope3 = function () {
    return data;
  };
  return data;
}
function _scope4() {
  const data = require("./scope.ui-root");
  _scope4 = function () {
    return data;
  };
  return data;
}
function _routes() {
  const data = require("./routes");
  _routes = function () {
    return data;
  };
  return data;
}
function _scopeComponentLoader() {
  const data = require("./scope-component-loader");
  _scopeComponentLoader = function () {
    return data;
  };
  return data;
}
function _scopeCmd() {
  const data = require("./scope-cmd");
  _scopeCmd = function () {
    return data;
  };
  return data;
}
function _stagedConfig() {
  const data = require("./staged-config");
  _stagedConfig = function () {
    return data;
  };
  return data;
}
function _noIdMatchPattern() {
  const data = require("./exceptions/no-id-match-pattern");
  _noIdMatchPattern = function () {
    return data;
  };
  return data;
}
function _scopeAspectsLoader() {
  const data = require("./scope-aspects-loader");
  _scopeAspectsLoader = function () {
    return data;
  };
  return data;
}
function _clearCacheAction() {
  const data = require("./clear-cache-action");
  _clearCacheAction = function () {
    return data;
  };
  return data;
}
function _catScopeCmd() {
  const data = require("./debug-commands/cat-scope-cmd");
  _catScopeCmd = function () {
    return data;
  };
  return data;
}
function _catComponentCmd() {
  const data = require("./debug-commands/cat-component-cmd");
  _catComponentCmd = function () {
    return data;
  };
  return data;
}
function _catObjectCmd() {
  const data = _interopRequireDefault(require("./debug-commands/cat-object-cmd"));
  _catObjectCmd = function () {
    return data;
  };
  return data;
}
function _catLaneCmd() {
  const data = _interopRequireDefault(require("./debug-commands/cat-lane-cmd"));
  _catLaneCmd = function () {
    return data;
  };
  return data;
}
function _runAction() {
  const data = require("./run-action/run-action.cmd");
  _runAction = function () {
    return data;
  };
  return data;
}
function _scopeGarbageCollector() {
  const data = require("./_scope-garbage-collector.cmd");
  _scopeGarbageCollector = 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); }
class ScopeMain {
  constructor(
  /**
   * private reference to the instance of Harmony.
   */
  harmony,
  /**
   * legacy scope
   */
  legacyScope,
  /**
   * component extension.
   */
  componentExtension,
  /**
   * slot registry for subscribing to build
   */

  config, postPutSlot, postDeleteSlot, postExportSlot, postObjectsPersist, preFetchObjects, OnCompAspectReCalcSlot, isolator, aspectLoader, logger, envs, dependencyResolver, globalConfig) {
    this.harmony = harmony;
    this.legacyScope = legacyScope;
    this.componentExtension = componentExtension;
    this.config = config;
    this.postPutSlot = postPutSlot;
    this.postDeleteSlot = postDeleteSlot;
    this.postExportSlot = postExportSlot;
    this.postObjectsPersist = postObjectsPersist;
    this.preFetchObjects = preFetchObjects;
    this.OnCompAspectReCalcSlot = OnCompAspectReCalcSlot;
    this.isolator = isolator;
    this.aspectLoader = aspectLoader;
    this.logger = logger;
    this.envs = envs;
    this.dependencyResolver = dependencyResolver;
    this.globalConfig = globalConfig;
    _defineProperty(this, "componentLoader", void 0);
    _defineProperty(this, "priority", void 0);
    _defineProperty(this, "localAspects", []);
    this.componentLoader = new (_scopeComponentLoader().ScopeComponentLoader)(this, this.logger);
  }
  /**
   * name of the scope
   */
  get name() {
    return this.legacyScope.name;
  }
  get icon() {
    return this.config.icon;
  }
  get backgroundIconColor() {
    return this.config.backgroundIconColor;
  }
  get description() {
    return this.config.description;
  }
  get path() {
    return this.legacyScope.path;
  }
  get isLegacy() {
    return this.legacyScope.isLegacy;
  }
  get isGlobalScope() {
    return this.path === _constants().GLOBAL_SCOPE;
  }
  get aspectsPackageManager() {
    return this.config.aspectsPackageManager;
  }
  get aspectsNodeLinker() {
    return this.config.aspectsNodeLinker;
  }

  // We need to reload the aspects with their new version since:
  // during get many by legacy, we go load component which in turn go to getEnv
  // get env validates that the env written on the component is really exist by checking the envs slot registry
  // when we load here, it's env version in the aspect list already has the new version in case the env itself is being tagged
  // so we are search for the env in the registry with the new version number
  // but since the env only registered during the on load of the bit process (before the tag) it's version in the registry is only the old one
  // once we reload them we will have it registered with the new version as well
  async reloadAspectsWithNewVersion(components) {
    const host = this.componentExtension.getHost();

    // Return only aspects that defined on components but not in the root config file (workspace.jsonc/scope.jsonc)
    const getUserAspectsIdsWithoutRootIds = () => {
      const allUserAspectIds = this.aspectLoader.getUserAspects();
      const rootIds = Object.keys(this.harmony.config.toObject());
      const diffIds = (0, _lodash().difference)(allUserAspectIds, rootIds);
      return diffIds;
    };

    // Based on the list of components to be tagged return those who are loaded to harmony with their used version
    const getAspectsByPreviouslyUsedVersion = async () => {
      const harmonyIds = getUserAspectsIdsWithoutRootIds();
      const aspectsIds = [];
      const aspectsP = components.map(async component => {
        const newId = await host.resolveComponentId(component.id);
        if (component.previouslyUsedVersion && component.version && component.previouslyUsedVersion !== component.version) {
          const newIdWithPreviouslyUsedVersion = newId.changeVersion(component.previouslyUsedVersion);
          if (harmonyIds.includes(newIdWithPreviouslyUsedVersion.toString())) {
            aspectsIds.push(newId.toString());
          }
        }
      });
      await Promise.all(aspectsP);
      return aspectsIds;
    };
    const idsToLoad = await getAspectsByPreviouslyUsedVersion();
    await host.loadAspects(idsToLoad, false, 'scope.reloadAspectsWithNewVersion');
  }
  loadAspects(ids, throwOnError, neededFor, lane, opts) {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.loadAspects(ids, throwOnError, neededFor, lane, opts);
  }
  resolveAspects(runtimeName, componentIds, opts) {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.resolveAspects(runtimeName, componentIds, opts);
  }
  async getResolvedAspects(components, opts) {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.getResolvedAspects(components, opts);
  }
  async executeOnCompAspectReCalcSlot(component) {
    const envsData = await this.envs.calcDescriptor(component, {
      skipWarnings: false
    });
    const policy = await this.dependencyResolver.mergeVariantPolicies(component.config.extensions, component.id, component.state._consumer.files);
    const dependenciesList = await this.dependencyResolver.extractDepsFromLegacy(component, policy);
    const depResolverData = {
      packageName: this.dependencyResolver.calcPackageName(component),
      dependencies: dependenciesList.serialize(),
      policy: policy.serialize()
    };
    // Make sure we are adding the envs / deps data first because other on load events might depend on it
    await Promise.all([this.upsertExtensionData(component, _envs().EnvsAspect.id, envsData), this.upsertExtensionData(component, _dependencyResolver().DependencyResolverAspect.id, depResolverData)]);

    // We are updating the component state with the envs and deps data here, so in case we have other slots that depend on this data
    // they will be able to get it, as it's very common use case that during on load someone want to access to the component env for example
    const aspectListWithEnvsAndDeps = await this.createAspectListFromExtensionDataList(component.state._consumer.extensions);
    component.state.aspects = aspectListWithEnvsAndDeps;
    const entries = this.OnCompAspectReCalcSlot.toArray();
    await (0, _pMapSeries().default)(entries, async ([extension, onLoad]) => {
      const data = await onLoad(component);
      await this.upsertExtensionData(component, extension, data);
      // Update the aspect list to have changes happened during the on load slot (new data added above)
      component.state.aspects.upsertEntry(await this.resolveComponentId(extension), data);
    });
    return component;
  }
  getDependencies(component) {
    return this.dependencyResolver.getDependencies(component);
  }
  componentPackageName(component) {
    return this.dependencyResolver.getPackageName(component);
  }
  async upsertExtensionData(component, extension, data) {
    if (!data) return;
    const existingExtension = component.state._consumer.extensions.findExtension(extension);
    if (existingExtension) {
      // Only merge top level of extension data
      Object.assign(existingExtension.data, data);
      return;
    }
    component.state._consumer.extensions.push(await this.getDataEntry(extension, data));
  }
  async getDataEntry(extension, data) {
    // TODO: @gilad we need to refactor the extension data entry api.
    return new (_config().ExtensionDataEntry)(undefined, undefined, extension, undefined, data);
  }
  getIsolateAspectsOpts(opts) {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.getIsolateOpts(opts);
  }
  getAspectCapsulePath() {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.getAspectCapsulePath();
  }
  shouldUseHashForCapsules() {
    const scopeAspectsLoader = this.getScopeAspectsLoader();
    return scopeAspectsLoader.shouldUseHashForCapsules();
  }
  getManyByLegacy(components) {
    return (0, _pMapSeries().default)(components, async component => this.getFromConsumerComponent(component));
  }
  getScopeAspectsLoader() {
    const scopeAspectsLoader = new (_scopeAspectsLoader().ScopeAspectsLoader)(this, this.aspectLoader, this.envs, this.isolator, this.logger, this.globalConfig);
    return scopeAspectsLoader;
  }
  async clearCache() {
    this.logger.debug('clearing the components and the legacy cache');
    this.componentLoader.clearCache();
    await this.legacyScope.objects.clearCache();
  }
  builderDataMapToLegacyOnTagResults(builderDataComponentMap) {
    const builderDataToLegacyExtension = (component, builderData) => {
      const existingBuilder = component.state.aspects.get(_builder().BuilderAspect.id)?.legacy;
      const builderExtension = existingBuilder || new (_config().ExtensionDataEntry)(undefined, undefined, _builder().BuilderAspect.id);
      builderExtension.data = builderData;
      return builderExtension;
    };
    return builderDataComponentMap.toArray().map(([component, builderData]) => ({
      id: component.id,
      builderData: builderDataToLegacyExtension(component, builderData)
    }));
  }

  /**
   * register to the post-export slot.
   */
  onPostPut(postPutFn) {
    this.postPutSlot.register(postPutFn);
    return this;
  }

  /**
   * register to the post-delete slot.
   */
  onPostDelete(postDeleteFn) {
    this.postDeleteSlot.register(postDeleteFn);
    return this;
  }

  /**
   * register to the post-export slot.
   */
  registerOnPostExport(postExportFn) {
    this.postExportSlot.register(postExportFn);
    return this;
  }
  registerOnPreFetchObjects(preFetchObjectsFn) {
    this.preFetchObjects.register(preFetchObjectsFn);
    return this;
  }
  registerOnCompAspectReCalc(compAspectReCalcSlotFn) {
    this.OnCompAspectReCalcSlot.register(compAspectReCalcSlotFn);
  }
  registerOnPostObjectsPersist(postObjectsPersistFn) {
    this.postObjectsPersist.register(postObjectsPersistFn);
    return this;
  }

  /**
   * Will fetch a list of components into the current scope.
   * This will only fetch the object and won't write the files to the actual FS
   */
  fetch(ids) {} // eslint-disable-line @typescript-eslint/no-unused-vars

  async delete({
    ids,
    force,
    lanes
  }, headers) {
    const authData = (0, _http().getAuthDataFromHeader)(headers?.authorization);
    const result = await (0, _legacy().remove)({
      path: this.path,
      ids,
      force,
      lanes
    });
    const fns = this.postDeleteSlot.values();
    const metadata = {
      auth: authData,
      headers
    };
    const componentIds = lanes ? [] : ids.map(id => _componentId().ComponentID.fromString(id));
    await (0, _pMapSeries().default)(fns, async fn => {
      try {
        await fn({
          ids: componentIds
        }, metadata);
      } catch (err) {
        this.logger.error('failed to run delete slot', err);
      }
    });
    return result;
  }
  isExported(id) {
    return this.legacyScope.isExported(id);
  }

  /**
   * for long-running processes, such as `bit start` or `bit watch`, it's important to keep the following data up to date:
   * 1. scope-index (.bit/index.json file).
   * 2. remote-refs (.bit/refs/*).
   * 3. global config. so for example if a user logs in or out it would be reflected.
   * it's possible that other commands (e.g. `bit import`) modified these files, while these processes are running.
   * Because these data are kept in memory, they're not up to date anymore.
   */
  async watchScopeInternalFiles() {
    const scopeIndexFile = this.legacyScope.objects.scopeIndex.getPath();
    const remoteLanesDir = this.legacyScope.objects.remoteLanes.basePath;
    const globalConfigFile = (0, _config2().getGlobalConfigPath)();
    const pathsToWatch = [scopeIndexFile, remoteLanesDir, globalConfigFile];
    const watcher = _chokidar().default.watch(pathsToWatch);
    watcher.on('ready', () => {
      this.logger.debug(`watchSystemFiles has started, watching ${pathsToWatch.join(', ')}`);
    });
    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    watcher.on('change', async filePath => {
      if (filePath === scopeIndexFile) {
        this.logger.debug('scope index file has been changed, reloading it');
        await this.legacyScope.objects.reLoadScopeIndex();
      } else if (filePath.startsWith(remoteLanesDir)) {
        this.legacyScope.objects.remoteLanes.removeFromCacheByFilePath(filePath);
      } else if (filePath === globalConfigFile) {
        this.logger.debug('global config file has been changed, invalidating its cache');
        (0, _globalConfig2().invalidateCache)();
      } else {
        this.logger.error('unknown file has been changed, please check why it is watched by scope.watchSystemFiles', filePath);
      }
    });
  }
  async toObjectList(types) {
    const objects = await this.legacyScope.objects.list(types);
    return _objectList().ObjectList.fromBitObjects(objects);
  }

  // TODO: temporary compiler workaround - discuss this with david.
  toJs(str) {
    if (str.endsWith('.ts')) return str.replace('.ts', '.js');
    return str;
  }
  async getGraph(ids) {
    if (!ids || !ids.length) ids = (await this.list()).map(comp => comp.id) || [];
    const components = await this.getMany(ids);
    const allFlattened = components.map(component => component.state._consumer.getAllFlattenedDependencies()).flat();
    const allFlattenedUniq = _componentId().ComponentIdList.uniqFromArray(allFlattened);
    await this.legacyScope.scopeImporter.importWithoutDeps(allFlattenedUniq, {
      cache: true,
      reason: `which are unique flattened dependencies to get the graph of ${ids.length} ids`
    });
    const dependencies = await this.getMany(allFlattenedUniq);
    const allComponents = [...components, ...dependencies];

    // build the graph
    const graph = new (_graph().Graph)();
    allComponents.forEach(comp => graph.setNode(new (_graph().Node)(comp.id.toString(), comp)));
    await Promise.all(allComponents.map(async comp => {
      const deps = await this.dependencyResolver.getComponentDependencies(comp);
      deps.forEach(dep => {
        const depCompId = dep.componentId;
        if (!graph.hasNode(depCompId.toString())) {
          throw new Error(`scope.getGraph: missing node of ${depCompId.toString()}`);
        }
        graph.setEdge(new (_graph().Edge)(comp.id.toString(), depCompId.toString(), dep.lifecycle));
      });
    }));
    return graph;
  }
  async getGraphIds(ids) {
    if (!ids || !ids.length) ids = (await this.list()).map(comp => comp.id) || [];
    const components = await this.getMany(ids);
    const graph = new (_graph().Graph)();
    const componentsWithoutSavedGraph = [];

    // try to add from saved graph
    await Promise.all(components.map(async component => {
      const compGraph = await this.getSavedGraphOfComponentIfExist(component);
      if (!compGraph) {
        componentsWithoutSavedGraph.push(component);
        return;
      }
      graph.merge([compGraph]);
    }));
    if (!componentsWithoutSavedGraph.length) {
      return graph;
    }

    // there are components that don't have the graph saved, create the graph by using Version objects of all flattened
    const lane = await this.legacyScope.getCurrentLaneObject();
    await this.import(componentsWithoutSavedGraph.map(c => c.id), {
      reFetchUnBuiltVersion: false,
      lane,
      reason: `to build graph-ids from the scope`
    });
    const allFlattened = componentsWithoutSavedGraph.map(component => component.state._consumer.getAllFlattenedDependencies()).flat();
    const allFlattenedUniq = _componentId().ComponentIdList.uniqFromArray(allFlattened.concat(ids));
    const addEdges = (compId, dependencies, label) => {
      dependencies.get().forEach(dep => {
        const depId = dep.id;
        graph.setNode(new (_graph().Node)(depId.toString(), depId));
        graph.setEdge(new (_graph().Edge)(compId.toString(), depId.toString(), label));
      });
    };
    const componentsAndVersions = await this.legacyScope.getComponentsAndVersions(allFlattenedUniq);
    componentsAndVersions.forEach(({
      component,
      version,
      versionStr
    }) => {
      const compId = component.toComponentId().changeVersion(versionStr);
      graph.setNode(new (_graph().Node)(compId.toString(), compId));
      addEdges(compId, version.dependencies, 'prod');
      addEdges(compId, version.devDependencies, 'dev');
      addEdges(compId, version.extensionDependencies, 'ext');
    });
    return graph;
  }
  async getFlattenedEdges(id) {
    let versionObj;
    try {
      versionObj = await this.legacyScope.getVersionInstance(id);
    } catch (err) {
      return undefined;
    }
    const flattenedEdges = await versionObj.getFlattenedEdges(this.legacyScope.objects);
    return flattenedEdges;
  }
  async getSavedGraphOfComponentIfExist(component) {
    const consumerComponent = component.state._consumer;
    const flattenedEdges = await this.getFlattenedEdges(component.id);
    if (!flattenedEdges) throw new Error('getSavedGraphOfComponentIfExist failed to get flattenedEdges, it must not be undefined because the version object exists');
    if (!flattenedEdges.length && consumerComponent.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 edges = flattenedEdges.map(edge => _objectSpread(_objectSpread({}, edge), {}, {
      source: edge.source,
      target: edge.target
    }));
    const nodes = consumerComponent.flattenedDependencies;
    const graph = new (_graph().Graph)();
    graph.setNode(new (_graph().Node)(component.id.toString(), component.id));
    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;
  }

  /**
   * import components into the scope.
   */
  async import(ids, {
    useCache = true,
    reFetchUnBuiltVersion = true,
    preferDependencyGraph = true,
    includeUpdateDependents = false,
    lane,
    reason
  } = {}) {
    const withoutOwnScopeAndLocals = ids.filter(id => {
      return id.scope !== this.name && id._legacy.hasScope();
    });
    await this.legacyScope.scopeImporter.importMany({
      ids: _componentId().ComponentIdList.fromArray(withoutOwnScopeAndLocals),
      cache: useCache,
      throwForDependencyNotFound: false,
      reFetchUnBuiltVersion,
      lane,
      preferDependencyGraph,
      includeUpdateDependents,
      reason
    });
  }
  async get(id, useCache = true, importIfMissing = true) {
    return this.componentLoader.get(id, importIfMissing, useCache);
  }
  async getFromConsumerComponent(consumerComponent) {
    return this.componentLoader.getFromConsumerComponent(consumerComponent);
  }

  /**
   * get a component from a remote without importing it
   */
  async getRemoteComponent(id) {
    return this.componentLoader.getRemoteComponent(id);
  }

  /**
   * get a component from a remote without importing it
   */
  async getManyRemoteComponents(ids) {
    return this.componentLoader.getManyRemoteComponents(ids);
  }

  /**
   * list all components in the scope.
   */
  async list(filter, includeCache = false, includeFromLanes = false, includeDeleted = false) {
    const patternsWithScope = filter?.namespaces && filter?.namespaces.map(pattern => `**/${pattern || '**'}`) || undefined;
    const componentsIds = await this.listIds(includeCache, includeFromLanes, patternsWithScope);
    const comps = await this.getMany(filter && filter.limit ? (0, _lodash().slice)(componentsIds, filter.offset, filter.offset + filter.limit) : componentsIds);
    return includeDeleted ? comps : comps.filter(comp => !comp.isDeleted());
  }

  /**
   * for now, list of invalid components are mostly useful for the workspace.
   * in the future, this can return components that failed to load in the scope due to objects file
   * corruption or similar issues.
   */
  async listInvalid() {
    return [];
  }

  /**
   * get ids of all scope components.
   * @param includeCache whether or not include components that their scope-name is different than the current scope-name
   */
  async listIds(includeCache = false, includeFromLanes = false, patterns) {
    const allModelComponents = await this.legacyScope.list();
    const filterByCacheAndLanes = modelComponent => {
      const cacheFilter = includeCache ? true : this.exists(modelComponent);
      const lanesFilter = includeFromLanes ? true : modelComponent.hasHead();
      return cacheFilter && lanesFilter;
    };
    const modelComponentsToList = allModelComponents.filter(filterByCacheAndLanes);
    let ids = modelComponentsToList.map(component => _componentId().ComponentID.fromLegacy(component.toBitIdWithLatestVersion(), component.scope || this.name));
    if (patterns && patterns.length > 0) {
      ids = ids.filter(id => patterns?.some(pattern => (0, _workspaceModules().isMatchNamespacePatternItem)(id.toStringWithoutVersion(), pattern).match));
    }
    this.logger.debug(`scope listIds: total ${ids.length} components after filter scope`);
    return ids;
  }

  /**
   * Check if a specific id exist in the scope
   * @param componentId
   */
  async hasId(componentId, includeCache = false) {
    if (!includeCache && componentId.scope !== this.name) return false;
    const opts = {
      includeVersion: true
    };
    return this.legacyScope.hasId(componentId, opts);
  }
  async hasIdNested(componentId, includeCache = false) {
    return this.hasId(componentId, includeCache);
  }

  /**
   * determine whether a component exists in the scope.
   */
  exists(modelComponent) {
    return modelComponent.scope === this.name;
  }
  async getMany(ids, throwIfNotExist = false) {
    const idsWithoutEmpty = (0, _lodash().compact)(ids);
    const componentsP = (0, _pMapSeries().default)(idsWithoutEmpty, async id => {
      return throwIfNotExist ? this.getOrThrow(id) : this.get(id);
    });
    const components = await componentsP;
    return (0, _lodash().compact)(components);
  }

  /**
   * important! you probably want to use `getMany`, which returns the components from the scope.
   * this method loads all aspects of the loaded components. (which hurts performance)
   */
  async loadMany(ids, lane, opts = {
    loadApps: true,
    loadEnvs: true,
    loadCompAspects: true,
    loadCustomEnvs: false
  }) {
    const components = await (0, _pMapSeries().default)(ids, id => this.load(id, lane, opts));
    return (0, _lodash().compact)(components);
  }
  async loadManyCompsAspects(components, lane) {
    const loadedComponents = await (0, _pMapSeries().default)(components, id => this.loadCompAspects(id, lane));
    return (0, _lodash().compact)(loadedComponents);
  }

  /**
   * get a component and throw an exception if not found.
   * @param id component id
   */
  async getOrThrow(id) {
    const component = await this.get(id);
    if (!component) throw new (_exceptions().ComponentNotFound)(id);
    return component;
  }

  /**
   * returns a specific state of a component.
   * @param id component ID.
   * @param hash state hash.
   */
  async getState(id, hash) {
    return this.componentLoader.getState(id, hash);
  }
  async getSnap(id, hash) {
    const modelComponent = await this.legacyScope.getModelComponent(id);
    const ref = modelComponent.getRef(hash);
    if (!ref) throw new Error(`ref was not found: ${id.toString()} with tag ${hash}`);
    return this.componentLoader.getSnap(id, ref.toString());
  }

  /**
   * get component log sorted by the timestamp in ascending order (from the earliest to the latest)
   */
  async getLogs(id, shortHash = false, startsFrom, throwIfMissing = false) {
    return this.legacyScope.loadComponentLogs(id, shortHash, startsFrom, throwIfMissing);
  }
  async getStagedConfig() {
    const currentLaneId = this.legacyScope.getCurrentLaneId();
    return _stagedConfig().StagedConfig.load(this.path, this.logger, currentLaneId);
  }

  /**
   * whether a component is soft-removed.
   * the version is required as it can be removed on a lane. in which case, the version is the head in the lane.
   */
  async isComponentRemoved(id) {
    const version = id.version;
    if (!version) throw new Error(`isComponentRemoved expect to get version, got ${id.toString()}`);
    const modelComponent = await this.legacyScope.getModelComponent(id);
    const versionObj = await modelComponent.loadVersion(version, this.legacyScope.objects);
    return versionObj.isRemoved();
  }

  /**
   * whether the id with the specified version exits in the local scope.
   */
  async isComponentInScope(id) {
    return this.legacyScope.isComponentInScope(id);
  }

  /**
   * resolve a component ID.
   * @param id component ID.
   */
  async resolveComponentId(id) {
    if (id instanceof _componentId().ComponentID) return id;
    if (id instanceof _legacyBitId().BitId) return this.resolveComponentIdFromBitId(id);
    const idStr = id.toString();
    const component = await this.legacyScope.loadModelComponentByIdStr(idStr);
    const getIdToCheck = () => {
      if (component) return idStr; // component exists in the scope with the scope-name.
      if (idStr.startsWith(`${this.name}/`)) {
        // component with the full name doesn't exist in the scope, it might be locally tagged
        return idStr.replace(`${this.name}/`, '');
      }
      return idStr;
    };
    const IdToCheck = getIdToCheck();
    const legacyId = await this.legacyScope.getParsedId(IdToCheck);
    return legacyId;
  }
  resolveComponentIdFromBitId(id) {
    return id.hasScope() ? _componentId().ComponentID.fromLegacy(id) : _componentId().ComponentID.fromLegacy(id, this.name);
  }
  async resolveMultipleComponentIds(ids) {
    return Promise.all(ids.map(async id => this.resolveComponentId(id)));
  }

  /**
   * @deprecated use `this.idsByPattern` instead for consistency, which supports also negation and list of patterns.
   */
  async byPattern(patterns, scope = '**') {
    const patternsWithScope = patterns.map(pattern => `${scope}/${pattern || '**'}`);
    const ids = await this.listIds(true, false, patternsWithScope);
    const components = await this.getMany(ids);
    return components;
  }

  /**
   * get component-ids matching the given pattern. a pattern can have multiple patterns separated by a comma.
   * it uses multimatch (https://www.npmjs.com/package/multimatch) package for the matching algorithm, which supports
   * (among others) negate character "!" to exclude ids. See the package page for more supported characters.
   */
  async idsByPattern(pattern, throwForNoMatch = true) {
    if (!pattern.includes('*') && !pattern.includes(',')) {
      // 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);
      const exists = await this.hasId(id, true);
      if (exists) return [id];
      if (throwForNoMatch) throw new (_bitError().BitError)(`unable to find "${pattern}" in the scope`);
      return [];
    }
    const ids = await this.listIds(true);
    return this.filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch);
  }

  // todo: move this to somewhere else (where?)
  async filterIdsFromPoolIdsByPattern(pattern, ids, throwForNoMatch = true, filterByStateFunc) {
    const patterns = pattern.split(',').map(p => p.trim());
    if (patterns.every(p => p.startsWith('!'))) {
      // otherwise it'll never match anything. don't use ".push()". it must be the first item in the array.
      patterns.unshift('**');
    }
    // check also as legacyId.toString, as it doesn't have the defaultScope
    const idsToCheck = id => [id._legacy.toStringWithoutVersion(), id.toStringWithoutVersion()];
    const [statePatterns, nonStatePatterns] = (0, _lodash().partition)(patterns, p => p.startsWith('$') || p.includes(' AND '));
    const nonStatePatternsNoVer = nonStatePatterns.map(p => p.split('@')[0]); // no need for the version
    const idsFiltered = nonStatePatternsNoVer.length ? ids.filter(id => (0, _multimatch().default)(idsToCheck(id), nonStatePatternsNoVer).length) : [];
    const idsStateFiltered = await (0, _pMapSeries().default)(statePatterns, async statePattern => {
      if (!filterByStateFunc) {
        throw new Error(`filter by a state (${statePattern}) is currently supported on the workspace only`);
      }
      if (statePattern.includes(' AND ')) {
        let filteredByAnd = ids;
        const patternSplit = statePattern.split(' AND ').map(p => p.trim());
        for await (const onePattern of patternSplit) {
          filteredByAnd = onePattern.startsWith('$') ? await filterByStateFunc(onePattern.replace('$', ''), filteredByAnd) : filteredByAnd.filter(id => (0, _multimatch().default)(idsToCheck(id), [onePattern.split('@')[0]]).length);
        }
        return filteredByAnd;
      }
      return filterByStateFunc(statePattern.replace('$', ''), ids);
    });
    const idsStateFilteredFlat = idsStateFiltered.flat();
    const combineFilteredIds = () => {
      if (!idsStateFiltered) return idsFiltered;
      const allIds = [...idsFiltered, ...idsStateFilteredFlat];
      return _componentId().ComponentIdList.uniqFromArray(allIds);
    };
    const allIdsFiltered = combineFilteredIds();
    if (throwForNoMatch && !allIdsFiltered.length) {
      throw new (_noIdMatchPattern().NoIdMatchPattern)(pattern);
    }
    return allIdsFiltered;
  }
  async getSnapDistance(id, throws = true) {
    const modelComp = await this.legacyScope.getModelComponent(id);
    await modelComp.setDivergeData(this.legacyScope.objects, throws);
    return modelComp.getDivergeData();
  }
  /**
   * get the distance for a component between two lanes. for example, lane-b forked from lane-a and lane-b added some new snaps
   * @param componentId
   * @param sourceHead head on the source lane. leave empty if the source is main
   * @param targetHead head on the target lane. leave empty if the target is main
   * @returns
   */
  async getSnapsDistanceBetweenTwoSnaps(componentId, sourceHead, targetHead, throws) {
    if (!sourceHead && !targetHead) {
      throw new Error(`getDivergeData got sourceHead and targetHead empty. at least one of them should be populated`);
    }
    const modelComponent = await this.legacyScope.getModelComponent(componentId);
    return (0, _getDivergeData().getDivergeData)({
      modelComponent,
      repo: this.legacyScope.objects,
      sourceHead: sourceHead ? _objects().Ref.from(sourceHead) : modelComponent.head || null,
      targetHead: targetHead ? _objects().Ref.from(targetHead) : modelComponent.head || null,
      throws
    });
  }
  async getExactVersionBySemverRange(id, range) {
    const modelComponent = await this.legacyScope.getModelComponent(id);
    const versions = modelComponent.listVersions();
    return _semver().default.maxSatisfying(versions, range, {
      includePrerelease: true
    })?.toString();
  }
  async resumeExport(exportId, remotes) {
    return (0, _exportScopeComponents().resumeExport)(this.legacyScope, exportId, remotes);
  }

  /**
   * @deprecated use `this.resolveComponentId` instead.
   */
  async resolveId(id) {
    return this.resolveComponentId(id);
  }

  // TODO: add new API for this
  async _legacyRemotes() {
    return (0, _scopeRemotes().getScopeRemotes)(this.legacyScope);
  }

  /**
   * list all component ids from a remote-scope
   */
  async listRemoteScope(scopeName) {
    const remotes = await this._legacyRemotes();
    const remote = await remotes.resolve(scopeName, this.legacyScope);
    const results = await remote.list();
    return results.map(({
      id
    }) => id);
  }
  async getLegacyMinimal(id) {
    try {
      return await this.legacyScope.getConsumerComponent(id);
    } catch (err) {
      // in case the component is missing locally, this.get imports it.
      return (await this.get(id))?.state._consumer;
    }
  }

  /**
   * ModelComponent is of type `BitObject` which gets saved into the local scope as an object file.
   * It has data about the tags and the component head. It doesn't have any data about the source-files/artifacts etc.
   */
  async getBitObjectModelComponent(id, throwIfNotExist = false) {
    return throwIfNotExist ? this.legacyScope.getModelComponent(id) : this.legacyScope.getModelComponentIfExist(id);
  }

  /**
   * Version BitObject holds the data of the source files and build artifacts of a specific snap/tag.
   */
  async getBitObjectVersion(modelComponent, version, throwIfNotExist = false) {
    return modelComponent.loadVersion(version, this.legacyScope.objects, throwIfNotExist);
  }
  async getBitObjectVersionById(id, throwIfNotExist = false) {
    const modelComponent = await this.getBitObjectModelComponent(id, throwIfNotExist);
    if (!modelComponent) return undefined;
    return this.getBitObjectVersion(modelComponent, id.version, throwIfNotExist);
  }

  /**
   * get a component and load its aspect
   */
  async load(id, lane, opts = {
    loadApps: true,
    loadEnvs: true,
    loadCompAspects: true,
    loadCustomEnvs: false
  }) {
    const component = await this.get(id);
    if (!component) return undefined;
    const optsWithDefaults = _objectSpread({
      loadApps: true,
      loadEnvs: true,
      loadCompAspects: true
    }, opts);
    return this.loadCompAspects(component, lane, optsWithDefaults);
  }
  async loadCompAspects(component, lane, opts = {
    loadApps: true,
    loadEnvs: true,
    loadCompAspects: true,
    loadCustomEnvs: false
  }) {
    const optsWithDefaults = _objectSpread({
      loadApps: true,
      loadEnvs: true,
      loadCompAspects: true
    }, opts);
    const aspectIds = optsWithDefaults.loadCompAspects ? component.state.aspects.ids : [];
    // load components from type aspects as aspects.
    // important! previously, this was running for any aspect, not only apps. (the if statement was `this.aspectLoader.isAspectComponent(component)`)
    // Ran suggests changing it and if it breaks something, we'll document is and fix it.
    if (optsWithDefaults.loadApps) {
      const appData = component.state.aspects.get('teambit.harmony/application');
      if (appData?.data?.appName) {
        aspectIds.push(component.id.toString());
      }
    }
    if (optsWithDefaults.loadEnvs) {
      const envsData = component.state.aspects.get(_envs().EnvsAspect.id);
      if (envsData?.data?.services || envsData?.data?.self || envsData?.data?.type === 'env') {
        aspectIds.push(component.id.toString());
      }
    }
    if (aspectIds && aspectIds.length) {
      await this.loadAspects(aspectIds, true, component.id.toString(), lane, {
        loadCustomEnvs: optsWithDefaults.loadCustomEnvs
      });
    }
    return component;
  }
  async loadComponentsAspect(component) {
    const aspectIds = component.state.aspects.ids;
    await this.loadAspects(aspectIds, true, component.id.toString());
  }
  async addAspectsFromConfigObject(component, configObject) {
    const extensionsFromConfigObject = _config().ExtensionDataList.fromConfigObject(configObject);
    const extensionDataList = _config().ExtensionDataList.mergeConfigs([extensionsFromConfigObject, component.state._consumer.extensions]).filterRemovedExtensions();
    component.state._consumer.extensions = extensionDataList;
  }
  async createAspectListFromExtensionDataList(extensionDataList) {
    const entries = await Promise.all(extensionDataList.map(entry => this.extensionDataEntryToAspectEntry(entry)));
    return this.componentExtension.createAspectListFromEntries(entries);
  }
  async extensionDataEntryToAspectEntry(dataEntry) {
    return new (_component().AspectEntry)(await this.resolveComponentId(dataEntry.id), dataEntry);
  }
  getLastMergedPath() {
    return _path().default.join(this.path, 'last-merged');
  }
  async isModified() {
    return false;
  }
  async write() {
    // no-op (it's relevant for the workspace only)
  }
  async hasObjects(hashes) {
    const refs = hashes.map(h => _objects().Ref.from(h));
    const results = await this.legacyScope.objects.hasMultiple(refs);
    return results.map(r => r.hash);
  }

  /**
   * declare the slots of scope extension.
   */

  static async provider([componentExt, ui, graphql, cli, isolator, aspectLoader, express, loggerMain, envs, depsResolver, globalConfig], config, [postPutSlot, postDeleteSlot, postExportSlot, postObjectsPersistSlot, preFetchObjectsSlot, OnCompAspectReCalcSlot], harmony) {
    const bitConfig = harmony.config.get('teambit.harmony/bit');
    const debugCommands = [new (_catScopeCmd().CatScopeCmd)(), new (_catComponentCmd().CatComponentCmd)(), new (_catObjectCmd().default)(), new (_catLaneCmd().default)()];
    const allCommands = [new (_scopeCmd().ScopeCmd)(), ...debugCommands, new (_runAction().RunActionCmd)()];
    const legacyScope = await (0, _scopeLoader().loadScopeIfExist)(bitConfig?.cwd);
    if (!legacyScope) {
      cli.register(...allCommands);
      return undefined;
    }
    const logger = loggerMain.createLogger(_scope2().ScopeAspect.id);
    const scope = new ScopeMain(harmony, legacyScope, componentExt, config, postPutSlot, postDeleteSlot, postExportSlot, postObjectsPersistSlot, preFetchObjectsSlot, OnCompAspectReCalcSlot, isolator, aspectLoader, logger, envs, depsResolver, globalConfig);
    cli.register(...allCommands, new (_scopeGarbageCollector().ScopeGarbageCollectorCmd)(scope));
    cli.registerOnStart(async hasWorkspace => {
      if (hasWorkspace) return;
      await scope.loadAspects(aspectLoader.getNotLoadedConfiguredExtensions(), undefined, 'scope.cli.registerOnStart');
    });
    const onPutHook = async (ids, lanes, authData) => {
      logger.debug(`onPutHook, started. (${ids.length} components)`);
      scope.componentLoader.clearCache();
      const componentIds = await scope.resolveMultipleComponentIds(ids);
      const fns = postPutSlot.values();
      const data = {
        ids: componentIds,
        lanes
      };
      const metadata = {
        auth: authData
      };
      await Promise.all(fns.map(async fn => fn(data, metadata)));
      logger.debug(`onPutHook, completed. (${ids.length} components)`);
    };
    const getAuthData = () => {
      const token = _http().Http.getToken();
      return token ? {
        type: _http().DEFAULT_AUTH_TYPE,
        credentials: token
      } : undefined;
    };
    const onPostExportHook = async (ids, lanes) => {
      logger.debug(`onPostExportHook, started. (${ids.length} components)`);
      const fns = postExportSlot.values();
      const data = {
        ids,
        lanes
      };
      const metadata = {
        auth: getAuthData()
      };
      await Promise.all(fns.map(async fn => fn(data, metadata)));
      logger.debug(`onPostExportHook, completed. (${ids.length} components)`);
    };
    const onPostObjectsPersistHook = async () => {
      logger.debug(`onPostObjectsPersistHook, started`);
      const fns = postObjectsPersistSlot.values();
      const metadata = {
        auth: getAuthData()
      };
      await Promise.all(fns.map(async fn => fn(undefined, metadata)));
      logger.debug(`onPostObjectsPersistHook, completed`);
    };
    _actions().ExportPersist.onPutHook = onPutHook;
    _actions().PostSign.onPutHook = onPutHook;
    _scope().Scope.onPostExport = onPostExportHook;
    _objects().Repository.onPostObjectsPersist = onPostObjectsPersistHook;
    _legacy().ExternalActions.externalActions.push(new (_clearCacheAction().ClearCacheAction)(scope));
    express.register([new (_routes().PutRoute)(scope, postPutSlot), new (_routes().FetchRoute)(scope, logger), new (_routes().ActionRoute)(scope), new (_routes().DeleteRoute)(scope)]);
    // @ts-ignore - @ran to implement the missing functions and remove it
    ui.registerUiRoot(new (_scope4().ScopeUIRoot)(scope));
    graphql.register((0, _scope3().scopeSchema)(scope));
    componentExt.registerHost(scope);
    return scope;
  }
}
exports.ScopeMain = ScopeMain;
_defineProperty(ScopeMain, "slots", [_harmony().Slot.withType(), _harmony().Slot.withType(), _harmony().Slot.withType(), _harmony().Slot.withType(), _harmony().Slot.withType(), _harmony().Slot.withType()]);
_defineProperty(ScopeMain, "runtime", _cli().MainRuntime);
_defineProperty(ScopeMain, "dependencies", [_component().ComponentAspect, _ui().UIAspect, _graphql().GraphqlAspect, _cli().CLIAspect, _isolator().IsolatorAspect, _aspectLoader().AspectLoaderAspect, _express().ExpressAspect, _logger().LoggerAspect, _envs().EnvsAspect, _dependencyResolver().DependencyResolverAspect, _globalConfig().GlobalConfigAspect]);
_defineProperty(ScopeMain, "defaultConfig", {
  httpTimeOut: 600000
});
_scope2().ScopeAspect.addRuntime(ScopeMain);

//# sourceMappingURL=scope.main.runtime.js.map