import {
  eventKey,
  getEvents,
  isAllOf
} from "./chunk-PKQT6C53.mjs";
import {
  Service as Service2
} from "./chunk-KWP6PKIU.mjs";
import {
  Service
} from "./chunk-ZQO3UZ4R.mjs";
import {
  __export
} from "./chunk-J5LGTIGS.mjs";

// src/core/plugin-manager.ts
var plugin_manager_exports = {};
__export(plugin_manager_exports, {
  ManagerTypeId: () => ManagerTypeId,
  isManager: () => isManager,
  make: () => make5
});
import { Atom as Atom2, Registry } from "@effect-atom/atom-react";
import * as Array from "effect/Array";
import * as Cause from "effect/Cause";
import * as Deferred2 from "effect/Deferred";
import * as Duration from "effect/Duration";
import * as Effect2 from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as Function from "effect/Function";
import * as HashSet from "effect/HashSet";
import * as PubSub from "effect/PubSub";
import * as Ref from "effect/Ref";
import { runAndForwardErrors } from "@dxos/effect";
import { log as log2 } from "@dxos/log";

// src/core/capability-manager.ts
var capability_manager_exports = {};
__export(capability_manager_exports, {
  make: () => make2
});
import { Atom } from "@effect-atom/atom-react";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
import { invariant } from "@dxos/invariant";
import { log } from "@dxos/log";
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/capability-manager.ts";
var CapabilityManagerImpl = class {
  _registry;
  _capabilityEntries = Atom.family(() => {
    return Atom.make([]).pipe(Atom.keepAlive);
  });
  _capabilities = Atom.family((id) => {
    return Atom.make((get) => {
      const current = get(this._capabilityEntries(id));
      return current.map((c) => c.implementation);
    });
  });
  _capability = Atom.family((id) => {
    return Atom.make((get) => {
      const current = get(this._capabilities(id));
      invariant(current.length > 0, `No capability found for ${id}`, {
        F: __dxlog_file,
        L: 84,
        S: this,
        A: [
          "current.length > 0",
          "`No capability found for ${id}`"
        ]
      });
      return current[0];
    });
  });
  constructor({ registry }) {
    this._registry = registry;
  }
  contribute({ module: moduleId, interface: interfaceDef, implementation }) {
    const current = this._registry.get(this._capabilityEntries(interfaceDef.identifier));
    const isDuplicate = current.some((c) => c.moduleId === moduleId && c.implementation === implementation);
    if (isDuplicate) {
      log("capability already contributed, skipping", {
        id: interfaceDef.identifier,
        moduleId
      }, {
        F: __dxlog_file,
        L: 105,
        S: this,
        C: (f, a) => f(...a)
      });
      return;
    }
    const entry = {
      moduleId,
      implementation
    };
    this._registry.set(this._capabilityEntries(interfaceDef.identifier), [
      ...current,
      entry
    ]);
    log("capability contributed", {
      id: interfaceDef.identifier,
      moduleId,
      count: current.length
    }, {
      F: __dxlog_file,
      L: 111,
      S: this,
      C: (f, a) => f(...a)
    });
  }
  remove(interfaceDef, implementation) {
    const current = this._registry.get(this._capabilityEntries(interfaceDef.identifier));
    if (current.length === 0) {
      return;
    }
    const next = current.filter((c) => c.implementation !== implementation);
    if (next.length !== current.length) {
      this._registry.set(this._capabilityEntries(interfaceDef.identifier), next);
      log("capability removed", {
        id: interfaceDef.identifier,
        count: current.length
      }, {
        F: __dxlog_file,
        L: 127,
        S: this,
        C: (f, a) => f(...a)
      });
    } else {
      log.warn("capability not removed", {
        id: interfaceDef.identifier
      }, {
        F: __dxlog_file,
        L: 129,
        S: this,
        C: (f, a) => f(...a)
      });
    }
  }
  atom(interfaceDef) {
    return this._capabilities(interfaceDef.identifier);
  }
  getAll(interfaceDef) {
    return this._registry.get(this.atom(interfaceDef));
  }
  get(interfaceDef) {
    const capabilities = this.getAll(interfaceDef);
    invariant(capabilities.length > 0, `No capability found for ${interfaceDef.identifier}`, {
      F: __dxlog_file,
      L: 144,
      S: this,
      A: [
        "capabilities.length > 0",
        "`No capability found for ${interfaceDef.identifier}`"
      ]
    });
    return capabilities[0];
  }
  waitFor(interfaceDef) {
    return Effect.gen(this, function* () {
      const [capability] = this.getAll(interfaceDef);
      if (capability) {
        return capability;
      }
      const deferred = yield* Deferred.make();
      const cancel = this._registry.subscribe(this.atom(interfaceDef), (capabilities) => {
        if (capabilities.length > 0) {
          Effect.runSync(Deferred.succeed(deferred, capabilities[0]));
        }
      });
      const result = yield* Deferred.await(deferred);
      cancel();
      return result;
    });
  }
};
var make2 = (options) => new CapabilityManagerImpl(options);

// src/core/plugin-manager.ts
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-framework/src/core/plugin-manager.ts";
var ManagerTypeId = Symbol.for("@dxos/app-framework/Manager");
var isManager = (value) => {
  return typeof value === "object" && value !== null && ManagerTypeId in value;
};
var ManagerImpl = class {
  [ManagerTypeId] = ManagerTypeId;
  activation = Effect2.runSync(PubSub.unbounded());
  capabilities;
  registry;
  _pluginsAtom;
  _coreAtom;
  _enabledAtom;
  _modulesAtom;
  _activeAtom;
  _eventsFiredAtom;
  _pendingResetAtom;
  _pluginLoader;
  _capabilities = /* @__PURE__ */ new Map();
  _moduleMemoMap = /* @__PURE__ */ new Map();
  _moduleSemaphores = /* @__PURE__ */ new Map();
  _activatingEvents = Effect2.runSync(Ref.make([]));
  _activatingModules = Effect2.runSync(Ref.make([]));
  constructor({ pluginLoader, plugins = [], core = plugins.map(({ meta }) => meta.id), enabled = [], registry }) {
    this.registry = registry ?? Registry.make();
    this.capabilities = make2({
      registry: this.registry
    });
    this._pluginLoader = pluginLoader;
    this._pluginsAtom = Atom2.make(plugins).pipe(Atom2.keepAlive);
    this._coreAtom = Atom2.make(core).pipe(Atom2.keepAlive);
    this._enabledAtom = Atom2.make(enabled).pipe(Atom2.keepAlive);
    this._modulesAtom = Atom2.make([]).pipe(Atom2.keepAlive);
    this._activeAtom = Atom2.make([]).pipe(Atom2.keepAlive);
    this._eventsFiredAtom = Atom2.make([]).pipe(Atom2.keepAlive);
    this._pendingResetAtom = Atom2.make([]).pipe(Atom2.keepAlive);
    plugins.forEach((plugin) => this._addPlugin(plugin));
    void Effect2.all([
      ...core,
      ...enabled
    ].map((id) => this.enable(id))).pipe(runAndForwardErrors);
  }
  get plugins() {
    return this._pluginsAtom;
  }
  get core() {
    return this._coreAtom;
  }
  /**
  * Ids of plugins that are currently enabled.
  */
  get enabled() {
    return this._enabledAtom;
  }
  /**
  * Modules of plugins which are currently enabled.
  */
  get modules() {
    return this._modulesAtom;
  }
  /**
  * Ids of modules which are currently active.
  */
  get active() {
    return this._activeAtom;
  }
  /**
  * Ids of events which have been fired.
  */
  get eventsFired() {
    return this._eventsFiredAtom;
  }
  /**
  * Ids of modules which are pending reset.
  */
  get pendingReset() {
    return this._pendingResetAtom;
  }
  getPlugins() {
    return this._get(this._pluginsAtom);
  }
  getCore() {
    return this._get(this._coreAtom);
  }
  getEnabled() {
    return this._get(this._enabledAtom);
  }
  getModules() {
    return this._get(this._modulesAtom);
  }
  getActive() {
    return this._get(this._activeAtom);
  }
  getEventsFired() {
    return this._get(this._eventsFiredAtom);
  }
  getPendingReset() {
    return this._get(this._pendingResetAtom);
  }
  /**
  * Adds a plugin to the manager via the plugin loader.
  * @param id The id of the plugin.
  */
  add(id) {
    return Effect2.gen(this, function* () {
      log2("add plugin", {
        id
      }, {
        F: __dxlog_file2,
        L: 210,
        S: this,
        C: (f, a) => f(...a)
      });
      const plugin = yield* this._pluginLoader(id);
      this._addPlugin(plugin);
      return yield* this.enable(id);
    });
  }
  /**
  * Enables a plugin.
  * @param id The id of the plugin.
  */
  enable(id) {
    return Effect2.gen(this, function* () {
      log2("enable plugin", {
        id
      }, {
        F: __dxlog_file2,
        L: 223,
        S: this,
        C: (f, a) => f(...a)
      });
      const plugin = this._getPlugin(id);
      if (!plugin) {
        return false;
      }
      this._update(this._enabledAtom, (enabled) => enabled.includes(id) ? enabled : [
        ...enabled,
        id
      ]);
      plugin.modules.forEach((module) => {
        this._addModule(module);
        this._setPendingResetByModule(module);
      });
      log2("pending reset", {
        events: [
          ...this.getPendingReset()
        ]
      }, {
        F: __dxlog_file2,
        L: 236,
        S: this,
        C: (f, a) => f(...a)
      });
      yield* Effect2.all(this.getPendingReset().map((event) => this.activate(event)), {
        concurrency: "unbounded"
      });
      return true;
    });
  }
  /**
  * Removes a plugin from the manager.
  * @param id The id of the plugin.
  */
  remove(id) {
    log2("remove plugin", {
      id
    }, {
      F: __dxlog_file2,
      L: 251,
      S: this,
      C: (f, a) => f(...a)
    });
    const result = this.disable(id);
    if (!result) {
      return false;
    }
    this._removePlugin(id);
    return true;
  }
  /**
  * Disables a plugin.
  * @param id The id of the plugin.
  */
  disable(id) {
    return Effect2.gen(this, function* () {
      log2("disable plugin", {
        id
      }, {
        F: __dxlog_file2,
        L: 267,
        S: this,
        C: (f, a) => f(...a)
      });
      if (this._get(this._coreAtom).includes(id)) {
        return false;
      }
      const plugin = this._getPlugin(id);
      if (!plugin) {
        return false;
      }
      const enabledIndex = this._get(this._enabledAtom).findIndex((enabled) => enabled === id);
      if (enabledIndex !== -1) {
        this._update(this._enabledAtom, (enabled) => enabled.filter((item) => item !== id));
        yield* this.deactivate(id);
        plugin.modules.forEach((module) => {
          this._removeModule(module.id);
        });
      }
      return true;
    });
  }
  /**
  * Activates plugins based on the activation event.
  * @param event The activation event.
  * @returns Whether the activation was successful.
  */
  activate(event, params) {
    const key = typeof event === "string" ? event : eventKey(event);
    return Effect2.gen(this, function* () {
      log2("activating", {
        key,
        ...params
      }, {
        F: __dxlog_file2,
        L: 302,
        S: this,
        C: (f, a) => f(...a)
      });
      yield* Ref.update(this._activatingEvents, (activating) => Array.append(activating, key));
      const pendingIndex = this._get(this._pendingResetAtom).findIndex((event2) => event2 === key);
      if (pendingIndex !== -1) {
        this._update(this._pendingResetAtom, (pending) => pending.filter((event2) => event2 !== key));
      }
      const activatingEvents = yield* this._activatingEvents;
      const activatingModules = yield* this._activatingModules;
      const modules = this._getInactiveModulesByEvent(key).filter((module) => {
        const allOf = isAllOf(module.activatesOn);
        if (!allOf) {
          return true;
        }
        const events = getEvents(module.activatesOn).filter((event2) => eventKey(event2) !== key);
        return events.every((event2) => this._get(this._eventsFiredAtom).includes(eventKey(event2)) || activatingEvents.includes(eventKey(event2))) && !activatingModules.includes(module.id);
      });
      yield* Ref.update(this._activatingModules, (activating) => Array.appendAll(activating, modules.map((module) => module.id)));
      if (modules.length === 0) {
        log2("no modules to activate", {
          key
        }, {
          F: __dxlog_file2,
          L: 337,
          S: this,
          C: (f, a) => f(...a)
        });
        if (!this._get(this._eventsFiredAtom).includes(key)) {
          this._update(this._eventsFiredAtom, (events) => [
            ...events,
            key
          ]);
        }
        return false;
      }
      log2("activating modules", {
        key,
        modules: modules.map((module) => module.id)
      }, {
        F: __dxlog_file2,
        L: 344,
        S: this,
        C: (f, a) => f(...a)
      });
      yield* PubSub.publish(this.activation, {
        event: key,
        state: "activating"
      });
      const beforeEvents = Function.pipe(modules, Array.flatMap((module) => module.activatesBefore ?? []), HashSet.fromIterable, HashSet.toValues, Array.filter((event2) => !activatingEvents.includes(eventKey(event2))));
      yield* Function.pipe(beforeEvents, Array.map((event2) => this.activate(event2, {
        before: key
      })), Effect2.allWith({
        concurrency: "unbounded"
      }), together(Effect2.sleep(Duration.seconds(10)).pipe(Effect2.andThen(Effect2.sync(() => log2.warn("activatesBefore is taking a long time", {
        event: key,
        beforeEvents: beforeEvents.map(eventKey)
      }, {
        F: __dxlog_file2,
        L: 363,
        S: this,
        C: (f, a) => f(...a)
      }))))));
      const getCapabilities = yield* Function.pipe(modules, Array.map((mod) => this._loadModule(mod)), Effect2.allWith({
        concurrency: "unbounded"
      }), Effect2.catchAll((error) => {
        return Effect2.gen(this, function* () {
          yield* PubSub.publish(this.activation, {
            event: key,
            state: "error",
            error
          });
          return yield* Effect2.fail(error);
        });
      }));
      yield* Function.pipe(
        modules,
        Array.zip(getCapabilities),
        Array.map(([module, capabilities]) => this._contributeCapabilities(module, capabilities)),
        Effect2.all
      );
      const afterEvents = Function.pipe(modules, Array.flatMap((module) => module.activatesAfter ?? []), HashSet.fromIterable, HashSet.toValues, Array.filter((event2) => !activatingEvents.includes(eventKey(event2))));
      yield* Function.pipe(afterEvents, Array.map((event2) => this.activate(event2, {
        after: key
      })), Effect2.allWith({
        concurrency: "unbounded"
      }), together(Effect2.sleep(Duration.seconds(10)).pipe(Effect2.andThen(Effect2.sync(() => log2.warn("activatesAfter is taking a long time", {
        event: key,
        afterEvents: afterEvents.map(eventKey)
      }, {
        F: __dxlog_file2,
        L: 412,
        S: this,
        C: (f, a) => f(...a)
      }))))));
      yield* Ref.update(this._activatingEvents, (activating) => Array.filter(activating, (event2) => event2 !== key));
      yield* Ref.update(this._activatingModules, (activating) => Array.filter(activating, (module) => !modules.map((module2) => module2.id).includes(module)));
      if (!this._get(this._eventsFiredAtom).includes(key)) {
        this._update(this._eventsFiredAtom, (events) => [
          ...events,
          key
        ]);
      }
      yield* PubSub.publish(this.activation, {
        event: key,
        state: "activated"
      });
      log2("activated", {
        key
      }, {
        F: __dxlog_file2,
        L: 432,
        S: this,
        C: (f, a) => f(...a)
      });
      return true;
    }).pipe(together(Effect2.sleep(Duration.seconds(15)).pipe(Effect2.andThen(Effect2.sync(() => log2.warn("event activation is taking a long time", {
      event: key
    }, {
      F: __dxlog_file2,
      L: 438,
      S: this,
      C: (f, a) => f(...a)
    }))))));
  }
  /**
  * Deactivates all of the modules for a plugin.
  * @param id The id of the plugin.
  * @returns Whether the deactivation was successful.
  */
  deactivate(id) {
    return Effect2.gen(this, function* () {
      const plugin = this._getPlugin(id);
      if (!plugin) {
        return false;
      }
      const modules = plugin.modules;
      const results = yield* Effect2.all(modules.map((module) => this._deactivateModule(module)), {
        concurrency: "unbounded"
      });
      return results.every((result) => result);
    });
  }
  /**
  * Re-activates the modules that were activated by the event.
  * @param event The activation event.
  * @returns Whether the reset was successful.
  */
  reset(event) {
    return Effect2.gen(this, function* () {
      const key = typeof event === "string" ? event : eventKey(event);
      log2("reset", {
        key
      }, {
        F: __dxlog_file2,
        L: 473,
        S: this,
        C: (f, a) => f(...a)
      });
      const modules = this._getActiveModulesByEvent(key);
      const results = yield* Effect2.all(modules.map((module) => this._deactivateModule(module)), {
        concurrency: "unbounded"
      });
      if (results.every((result) => result)) {
        return yield* this.activate(key);
      } else {
        return false;
      }
    });
  }
  _get(atom) {
    return this.registry.get(atom);
  }
  _set(atom, value) {
    this.registry.set(atom, value);
  }
  _update(atom, updater) {
    this._set(atom, updater(this._get(atom)));
  }
  _addPlugin(plugin) {
    log2("add plugin", {
      id: plugin.meta.id
    }, {
      F: __dxlog_file2,
      L: 501,
      S: this,
      C: (f, a) => f(...a)
    });
    this._update(this._pluginsAtom, (plugins) => plugins.includes(plugin) ? plugins : [
      ...plugins,
      plugin
    ]);
  }
  _removePlugin(id) {
    log2("remove plugin", {
      id
    }, {
      F: __dxlog_file2,
      L: 507,
      S: this,
      C: (f, a) => f(...a)
    });
    this._update(this._pluginsAtom, (plugins) => plugins.filter((plugin) => plugin.meta.id !== id));
  }
  _addModule(module) {
    log2("add module", {
      id: module.id
    }, {
      F: __dxlog_file2,
      L: 512,
      S: this,
      C: (f, a) => f(...a)
    });
    this._update(this._modulesAtom, (modules) => modules.includes(module) ? modules : [
      ...modules,
      module
    ]);
  }
  _removeModule(id) {
    log2("remove module", {
      id
    }, {
      F: __dxlog_file2,
      L: 518,
      S: this,
      C: (f, a) => f(...a)
    });
    this._update(this._modulesAtom, (modules) => modules.filter((module) => module.id !== id));
  }
  _getPlugin(id) {
    return this._get(this._pluginsAtom).find((plugin) => plugin.meta.id === id);
  }
  _getActiveModules() {
    const active = this._get(this._activeAtom);
    return this._get(this._modulesAtom).filter((module) => active.includes(module.id));
  }
  _getInactiveModules() {
    const active = this._get(this._activeAtom);
    return this._get(this._modulesAtom).filter((module) => !active.includes(module.id));
  }
  _getActiveModulesByEvent(key) {
    return this._getActiveModules().filter((module) => getEvents(module.activatesOn).map(eventKey).includes(key));
  }
  _getInactiveModulesByEvent(key) {
    return this._getInactiveModules().filter((module) => getEvents(module.activatesOn).map(eventKey).includes(key));
  }
  _setPendingResetByModule(module) {
    const activationEvents = getEvents(module.activatesOn).map(eventKey).filter((key) => this._get(this._eventsFiredAtom).includes(key));
    const pendingReset = Array.fromIterable(new Set(activationEvents)).filter((event) => {
      const pending = this._get(this._pendingResetAtom);
      return !pending.includes(event);
    });
    if (pendingReset.length > 0) {
      log2("pending reset", {
        events: pendingReset
      }, {
        F: __dxlog_file2,
        L: 558,
        S: this,
        C: (f, a) => f(...a)
      });
      this._update(this._pendingResetAtom, (current) => [
        ...current,
        ...pendingReset
      ]);
    }
  }
  _getModuleSemaphore(moduleId) {
    let semaphore = this._moduleSemaphores.get(moduleId);
    if (!semaphore) {
      semaphore = Effect2.runSync(Effect2.makeSemaphore(1));
      this._moduleSemaphores.set(moduleId, semaphore);
    }
    return semaphore;
  }
  _loadModule = (module) => Effect2.gen(this, function* () {
    const semaphore = this._getModuleSemaphore(module.id);
    const deferredToAwait = yield* Effect2.gen(this, function* () {
      const existing = this._moduleMemoMap.get(module.id);
      if (existing) {
        return existing;
      }
      const deferred = yield* Deferred2.make();
      this._moduleMemoMap.set(module.id, deferred);
      const loadEffect = Effect2.gen(this, function* () {
        log2("loading module", {
          module: module.id
        }, {
          F: __dxlog_file2,
          L: 588,
          S: this,
          C: (f, a) => f(...a)
        });
        const [duration, capabilities] = yield* module.activate().pipe(Effect2.provideService(Service, this.capabilities), Effect2.provideService(Service2, this), Effect2.timed);
        const normalized = capabilities == null ? [] : Array.isArray(capabilities) ? capabilities : [
          capabilities
        ];
        log2("loaded module", {
          module: module.id,
          elapsed: Duration.toMillis(duration),
          failed: false
        }, {
          F: __dxlog_file2,
          L: 597,
          S: this,
          C: (f, a) => f(...a)
        });
        return normalized;
      }).pipe(Effect2.withSpan("PluginManager._loadModule"), together(Effect2.sleep(Duration.seconds(10)).pipe(Effect2.andThen(Effect2.sync(() => log2.warn(`module is taking a long time to activate`, {
        module: module.id
      }, {
        F: __dxlog_file2,
        L: 608,
        S: this,
        C: (f, a) => f(...a)
      }))))));
      yield* Effect2.forkDaemon(loadEffect.pipe(Effect2.tap((result) => Deferred2.succeed(deferred, result)), Effect2.catchAllCause((cause) => {
        const error = Cause.squash(cause);
        log2.error("module failed to activate", {
          module: module.id,
          error: error instanceof Error ? error.message : String(error),
          stack: error instanceof Error ? error.stack : void 0,
          isDefect: !Cause.isFailure(cause)
        }, {
          F: __dxlog_file2,
          L: 620,
          S: this,
          C: (f, a) => f(...a)
        });
        return Deferred2.fail(deferred, error instanceof Error ? error : new Error(String(error)));
      })));
      return deferred;
    }).pipe(semaphore.withPermits(1));
    return yield* Deferred2.await(deferredToAwait);
  });
  _contributeCapabilities(module, capabilities) {
    return Effect2.gen(this, function* () {
      capabilities.forEach((capability) => {
        this.capabilities.contribute({
          module: module.id,
          ...capability
        });
      });
      this._update(this._activeAtom, (active) => [
        ...active,
        module.id
      ]);
      this._capabilities.set(module.id, capabilities);
    });
  }
  _deactivateModule(module) {
    return Effect2.gen(this, function* () {
      const id = module.id;
      log2("deactivating", {
        id
      }, {
        F: __dxlog_file2,
        L: 654,
        S: this,
        C: (f, a) => f(...a)
      });
      this._moduleMemoMap.delete(id);
      const capabilities = this._capabilities.get(id);
      if (capabilities) {
        for (const capability of capabilities) {
          this.capabilities.remove(capability.interface, capability.implementation);
          const program = capability.deactivate?.() ?? Effect2.succeed(void 0);
          yield* program;
        }
        this._capabilities.delete(id);
      }
      const activeIndex = this._get(this._activeAtom).findIndex((event) => event === id);
      if (activeIndex !== -1) {
        this._update(this._activeAtom, (active) => active.filter((event) => event !== id));
      }
      log2("deactivated", {
        id
      }, {
        F: __dxlog_file2,
        L: 672,
        S: this,
        C: (f, a) => f(...a)
      });
      return true;
    });
  }
};
var make5 = (options) => new ManagerImpl(options);
var together = (togetherEffect) => (effect) => Effect2.gen(function* () {
  const togetherFiber = yield* Effect2.fork(togetherEffect);
  const result = yield* effect;
  yield* Fiber.interrupt(togetherFiber);
  return result;
});

export {
  capability_manager_exports,
  ManagerTypeId,
  isManager,
  make5 as make,
  plugin_manager_exports
};
//# sourceMappingURL=chunk-WGWEEBUV.mjs.map
