# CommandRegistry

Source: /tuil/docs/reference/packages/core/api/command-registry
Locale: en

class exported by @mwillbanks/tuil-core.



{/* Generated by tooling/docs/generate-reference.ts. */}

## class [#class]

Public class exported by `@mwillbanks/tuil-core`.

```ts
export class CommandRegistry {
  readonly #commands = new Map<string, Command>();
  readonly #observers = new Set<(execution: CommandExecution) => void>();
  readonly #registryObservers = new Set<
    (change: CommandRegistryChange) => void
  >();

  constructor(readonly services: ServiceContainer) {}

  register(command: Command): Disposable {
    if (this.#commands.has(command.id)) {
      throw new Error(`Command "${command.id}" is already registered`);
    }
    this.#commands.set(command.id, command);
    this.#notifyRegistry({ type: "registered", command });
    let disposed = false;
    return toDisposable(() => {
      if (disposed) return;
      disposed = true;
      this.#commands.delete(command.id);
      this.#notifyRegistry({ type: "unregistered", command });
    });
  }

  get(id: string): Command | undefined {
    return this.#commands.get(id);
  }

  list(): readonly Command[] {
    return [...this.#commands.values()];
  }

  observe(observer: (execution: CommandExecution) => void): Disposable {
    this.#observers.add(observer);
    return toDisposable(() => {
      this.#observers.delete(observer);
    });
  }

  observeRegistry(
    observer: (change: CommandRegistryChange) => void,
  ): Disposable {
    this.#registryObservers.add(observer);
    return toDisposable(() => {
      this.#registryObservers.delete(observer);
    });
  }

  async execute<TResult = unknown>(
    id: string,
    options: {
      signal?: AbortSignal;
      source?: string;
      metadata?: Readonly<Record<string, unknown>>;
    } = {},
  ): Promise<TResult | undefined> {
    const command = this.#commands.get(id);
    if (!command) {
      throw new Error(`Command "${id}" is not registered`);
    }
    const startedAt = Date.now();
    const signal = options.signal ?? new AbortController().signal;
    const context: CommandContext = {
      services: this.services,
      signal,
      source: options.source,
      metadata: options.metadata ?? {},
    };
    if (signal.aborted) {
      this.#notify({
        commandId: id,
        startedAt,
        completedAt: Date.now(),
        status: "cancelled",
        error: signal.reason,
      });
      throw signal.reason;
    }
    if (command.enabled && !(await command.enabled(context))) {
      this.#notify({
        commandId: id,
        startedAt,
        completedAt: Date.now(),
        status: "disabled",
      });
      return undefined;
    }
    try {
      const result = (await command.execute(context)) as TResult;
      this.#notify({
        commandId: id,
        startedAt,
        completedAt: Date.now(),
        status: "succeeded",
      });
      return result;
    } catch (error) {
      const status = signal.aborted ? "cancelled" : "failed";
      this.#notify({
        commandId: id,
        startedAt,
        completedAt: Date.now(),
        status,
        error,
      });
      throw error;
    }
  }

  #notify(execution: CommandExecution): void {
    for (const observer of this.#observers) {
      observer(execution);
    }
  }

  #notifyRegistry(change: CommandRegistryChange): void {
    for (const observer of this.#registryObservers) {
      observer(change);
    }
  }
}
```

## Members [#members]

| Member               | Type                                                                                                                                                                             | Required | Description                                                                                                                                                                                                              | Related types                                                                                                                                      |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `#commands`          | `Map<string, Command<unknown>>`                                                                                                                                                  | Yes      | The `#commands` member uses the `Map<string, Command<unknown>>` contract.                                                                                                                                                | [`Command`](/tuil/docs/reference/packages/core/api/command)                                                                                             |
| `#observers`         | `Set<(execution: CommandExecution) => void>`                                                                                                                                     | Yes      | The `#observers` member uses the `Set<(execution: CommandExecution) => void>` contract.                                                                                                                                  | [`CommandExecution`](/tuil/docs/reference/packages/core/api/command-execution)                                                                          |
| `#registryObservers` | `Set<(change: CommandRegistryChange) => void>`                                                                                                                                   | Yes      | The `#registryObservers` member uses the `Set<(change: CommandRegistryChange) => void>` contract.                                                                                                                        | [`CommandRegistryChange`](/tuil/docs/reference/packages/core/api/command-registry-change)                                                               |
| `__constructor`      | `any`                                                                                                                                                                            | Yes      | The `__constructor` member uses the `any` contract.                                                                                                                                                                      | —                                                                                                                                                  |
| `register`           | `(command: Command) => Disposable`                                                                                                                                               | Yes      | The `register` member uses the `(command: Command) => Disposable` contract.                                                                                                                                              | [`Command`](/tuil/docs/reference/packages/core/api/command), [`Disposable`](/tuil/docs/reference/packages/core/api/disposable)                               |
| `get`                | `(id: string) => Command \| undefined`                                                                                                                                           | Yes      | The `get` member uses the `(id: string) => Command \| undefined` contract.                                                                                                                                               | [`Command`](/tuil/docs/reference/packages/core/api/command)                                                                                             |
| `list`               | `() => readonly Command[]`                                                                                                                                                       | Yes      | The `list` member uses the `() => readonly Command[]` contract.                                                                                                                                                          | [`Command`](/tuil/docs/reference/packages/core/api/command)                                                                                             |
| `observe`            | `(observer: (execution: CommandExecution) => void) => Disposable`                                                                                                                | Yes      | The `observe` member uses the `(observer: (execution: CommandExecution) => void) => Disposable` contract.                                                                                                                | [`CommandExecution`](/tuil/docs/reference/packages/core/api/command-execution), [`Disposable`](/tuil/docs/reference/packages/core/api/disposable)            |
| `observeRegistry`    | `(observer: (change: CommandRegistryChange) => void) => Disposable`                                                                                                              | Yes      | The `observeRegistry` member uses the `(observer: (change: CommandRegistryChange) => void) => Disposable` contract.                                                                                                      | [`CommandRegistryChange`](/tuil/docs/reference/packages/core/api/command-registry-change), [`Disposable`](/tuil/docs/reference/packages/core/api/disposable) |
| `execute`            | `<TResult = unknown>(id: string, options?: &#123; signal?: AbortSignal; source?: string; metadata?: Readonly<Record<string, unknown>>; &#125;) => Promise<TResult \| undefined>` | Yes      | The `execute` member uses the `<TResult = unknown>(id: string, options?: &#123; signal?: AbortSignal; source?: string; metadata?: Readonly<Record<string, unknown>>; &#125;) => Promise<TResult \| undefined>` contract. | —                                                                                                                                                  |
| `#notify`            | `(execution: CommandExecution) => void`                                                                                                                                          | Yes      | The `#notify` member uses the `(execution: CommandExecution) => void` contract.                                                                                                                                          | [`CommandExecution`](/tuil/docs/reference/packages/core/api/command-execution)                                                                          |
| `#notifyRegistry`    | `(change: CommandRegistryChange) => void`                                                                                                                                        | Yes      | The `#notifyRegistry` member uses the `(change: CommandRegistryChange) => void` contract.                                                                                                                                | [`CommandRegistryChange`](/tuil/docs/reference/packages/core/api/command-registry-change)                                                               |

## Parameters [#parameters]

This declaration has no public members.

## Returns [#returns]

This declaration does not return a value.

## Throws [#throws]

No thrown errors are documented for this declaration.

## Related types [#related-types]

* [`Command`](/tuil/docs/reference/packages/core/api/command)
* [`CommandContext`](/tuil/docs/reference/packages/core/api/command-context)
* [`CommandExecution`](/tuil/docs/reference/packages/core/api/command-execution)
* [`CommandRegistryChange`](/tuil/docs/reference/packages/core/api/command-registry-change)
* [`Disposable`](/tuil/docs/reference/packages/core/api/disposable)
* [`ServiceContainer`](/tuil/docs/reference/packages/core/api/service-container)

## Source [#source]

[View the secondary source reference](https://github.com/mwillbanks/tuil/blob/main/packages/core/src/commands.ts)

## Package [#package]

[@mwillbanks/tuil-core](/tuil/docs/reference/packages/core)
