# RegistryClient

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

class exported by @mwillbanks/tuil-registry.



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

## class [#class]

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

```ts
export class RegistryClient {
  constructor(readonly sources: readonly RegistrySource[]) {
    if (sources.length === 0) {
      throw new Error("Registry client requires at least one source");
    }
  }

  async get(name: string, signal?: AbortSignal): Promise<RegistryItem> {
    const qualified = name.match(/^@([^/]+)\/(.+)$/);
    const selectedSources = qualified
      ? this.sources.filter((source) => source.id === qualified[1])
      : this.sources;
    const itemName = qualified?.[2] ?? name;
    for (const source of selectedSources) {
      const item = await source.get(itemName, signal);
      if (item) {
        return Object.freeze({
          ...item,
          registryName: qualified ? name : item.name,
          sourceId: source.id,
        });
      }
    }
    throw new Error(`Registry item "${name}" was not found`);
  }

  async list(signal?: AbortSignal): Promise<readonly RegistryIndexEntry[]> {
    const results = await Promise.allSettled(
      this.sources.map((source) => source.list(signal)),
    );
    const entries = results
      .filter(
        (
          result,
        ): result is PromiseFulfilledResult<readonly RegistryIndexEntry[]> =>
          result.status === "fulfilled",
      )
      .map((result) => result.value);
    if (entries.length === 0) {
      throw new AggregateError(
        results
          .filter(
            (result): result is PromiseRejectedResult =>
              result.status === "rejected",
          )
          .map((result) => result.reason),
        "Every registry source failed",
      );
    }
    const unique = new Map<string, RegistryIndexEntry>();
    for (const entry of entries.flat()) {
      if (!unique.has(entry.name)) {
        unique.set(entry.name, entry);
      }
    }
    return [...unique.values()];
  }

  async search(
    query: string,
    signal?: AbortSignal,
  ): Promise<readonly RegistryIndexEntry[]> {
    const normalized = query.trim().toLowerCase();
    return (await this.list(signal)).filter((entry) =>
      `${entry.name} ${entry.title} ${entry.description}`
        .toLowerCase()
        .includes(normalized),
    );
  }

  async resolvePlan(
    names: readonly string[],
    signal?: AbortSignal,
  ): Promise<readonly RegistryItem[]> {
    const visiting = new Set<string>();
    const resolved = new Map<string, RegistryItem>();
    const visit = async (
      name: string,
      path: readonly string[],
    ): Promise<void> => {
      if (resolved.has(name)) return;
      if (visiting.has(name)) {
        throw new Error(
          `Registry dependency cycle: ${[...path, name].join(" → ")}`,
        );
      }
      visiting.add(name);
      const item = await this.get(name, signal);
      const source = name.match(/^@([^/]+)\//)?.[1];
      for (const dependency of item.registryDependencies ?? []) {
        const qualified =
          source && !dependency.startsWith("@")
            ? `@${source}/${dependency}`
            : dependency;
        await visit(qualified, [...path, name]);
      }
      visiting.delete(name);
      resolved.set(name, item);
    };
    for (const name of names) {
      await visit(name, []);
    }
    return [...resolved.values()];
  }
}
```

## Members [#members]

| Member          | Type                                                                                   | Required | Description                                                                                                                        | Related types                                                                      |
| --------------- | -------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| `__constructor` | `any`                                                                                  | Yes      | The `__constructor` member uses the `any` contract.                                                                                | —                                                                                  |
| `get`           | `(name: string, signal?: AbortSignal) => Promise<RegistryItem>`                        | Yes      | The `get` member uses the `(name: string, signal?: AbortSignal) => Promise<RegistryItem>` contract.                                | [`RegistryItem`](/tuil/docs/reference/packages/registry/api/registry-item)              |
| `list`          | `(signal?: AbortSignal) => Promise<readonly RegistryIndexEntry[]>`                     | Yes      | The `list` member uses the `(signal?: AbortSignal) => Promise<readonly RegistryIndexEntry[]>` contract.                            | [`RegistryIndexEntry`](/tuil/docs/reference/packages/registry/api/registry-index-entry) |
| `search`        | `(query: string, signal?: AbortSignal) => Promise<readonly RegistryIndexEntry[]>`      | Yes      | The `search` member uses the `(query: string, signal?: AbortSignal) => Promise<readonly RegistryIndexEntry[]>` contract.           | [`RegistryIndexEntry`](/tuil/docs/reference/packages/registry/api/registry-index-entry) |
| `resolvePlan`   | `(names: readonly string[], signal?: AbortSignal) => Promise<readonly RegistryItem[]>` | Yes      | The `resolvePlan` member uses the `(names: readonly string[], signal?: AbortSignal) => Promise<readonly RegistryItem[]>` contract. | [`RegistryItem`](/tuil/docs/reference/packages/registry/api/registry-item)              |

## 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]

* [`RegistryIndexEntry`](/tuil/docs/reference/packages/registry/api/registry-index-entry)
* [`RegistryItem`](/tuil/docs/reference/packages/registry/api/registry-item)
* [`RegistrySource`](/tuil/docs/reference/packages/registry/api/registry-source)

## Source [#source]

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

## Package [#package]

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