[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [local‑first/Evolu](https://evolu.dev/docs/api-reference/common/local-first/Evolu) › EvoluApp

Defined in: [packages/common/src/local-first/Evolu.ts:2277](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2277)

An app whose AppOwners Evolu creates and keeps on this device. Each tab opens
one AppOwner, starting with the one opened most recently. An AppOwner's
instance stays open until the app is disposed.

## Extends

- [`AsyncDisposable`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html#using-declarations-and-explicit-resource-management)

## Methods

<a id="asyncdispose"></a>

### \[asyncDispose\]()

```ts
[asyncDispose](): PromiseLike<void>;
```

Defined in: node\_modules/@typescript/old/lib/lib.esnext.disposable.d.ts:38

#### Inherited from

`AsyncDisposable.[asyncDispose]`

## Properties

<a id="add"></a>

### add

```ts
readonly add: (secret: (
  | Uint8Array<ArrayBufferLike> & Brand<"Entropy"> & Brand<"Length16">
  | Uint8Array<ArrayBufferLike> & Brand<"Entropy"> & Brand<"Length24">
  | Uint8Array<ArrayBufferLike> & Brand<"Entropy"> & Brand<"Length32">
  | Uint8Array<ArrayBufferLike> & Brand<"Entropy"> & Brand<"Length20">
  | Uint8Array<ArrayBufferLike> & Brand<"Entropy"> & Brand<"Length28">) & Brand<"OwnerSecret">, name?: string & Brand<"Trimmed"> & Brand<"MinLength1"> & Brand<"MaxLength100">) => Promise<Result<string & Brand<"Id"> & Brand<"OwnerId">,
  | UnknownError
| EvoluDisposedError>>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2316](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2316)

Adds an AppOwner, a new one or one restored from its secret, and opens it
once the device database stores it, unless this tab opened or added another
meanwhile. Adding a secret that is already there updates its entry. An add
made while a removal runs waits for it, so an AppOwner added after its
removal opens an empty database on this device. It fails when the write, or
the read of the AppOwners after it, fails, when the device database is
refused, or when the app is disposed; a failed add can still have taken
effect, and adding again is safe.

---

<a id="appowners"></a>

### appOwners

```ts
readonly appOwners: ReadonlyStore<readonly AppOwnerEntry[]>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2285](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2285)

The AppOwners the app can open on this device, the one opened most recently
first, then the oldest.

---

<a id="current"></a>

### current

```ts
readonly current: ReadonlyStore<
  | CurrentAppOwner<S>
| null>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2296](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2296)

The AppOwner this tab has open, or `null` while it has none. It starts with
the one opened most recently, or as [EvoluAppConfig.startAppOwnerId](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluAppConfig#startappownerid)
says. A removal of the open AppOwner, in any tab, leaves the tab with none,
as does its disappearing from [EvoluApp.appOwners](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluApp#appowners), until the app
opens one. The tab never opens another AppOwner on its own, and a deletion
of the open one's database, with [Evolu.deleteDatabase](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#deletedatabase), opens it
again, empty, with a new promise.

---

<a id="device"></a>

### device

```ts
readonly device: DeviceEvolu<D>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2388](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2388)

The device database's instance with the app's own device tables.

---

<a id="open"></a>

### open

```ts
readonly open: (id: string & Brand<"Id"> & Brand<"OwnerId">) => void;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2304](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2304)

Opens an AppOwner in this tab and records when, so the next start opens it
too. Other tabs keep theirs. An AppOwner that isn't listed, such as one
another tab just removed, opens nothing, and one this tab still lists after
another tab removed it leaves this tab with none open.

---

<a id="remove"></a>

### remove

```ts
readonly remove: (id: string & Brand<"Id"> & Brand<"OwnerId">) => Promise<Result<void, RemoveAppOwnerError>>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2385](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2385)

Deletes an AppOwner's data from this device and removes it from this
device's list, so its changes not yet synced are lost for good, while its
data on relays stays. Adding the AppOwner again syncs that data back. An
AppOwner that isn't listed removes nothing. It works also when the
AppOwner's database doesn't open, such as a corrupt one. The tabs that had
the AppOwner open have none open afterwards. When deleting fails, such as
when another context holds the database's files, the AppOwner stays with
the data its database stored, and the tabs that had it open can be left
with none open. After a `DatabaseHeldError`, its data may already be gone,
and removing it again finishes the removal. When the app's last tab closes
during the removal, the AppOwner can stay listed without its local data,
which syncs again when it's opened, and removing it again finishes the
removal. It fails with the refusal when the device database is refused. The
app's disposal ends the call with `EvoluDisposedError`, and the removal
still finishes.

To start over with a new AppOwner, add it first, then remove the old one,
so a crash between the two leaves two AppOwners rather than none.

### Example

```ts
import {
  AppName,
  assertEqual,
  assertOk,
  createEvoluApp,
  createOwnerSecret,
  id,
  NonEmptyTrimmedString100,
} from "@evolu/common";
import { createEvoluDeps, createRun } from "@evolu/web";

const Schema = {
  todo: { id: id("Todo"), title: NonEmptyTrimmedString100 },
};
const run = createRun(createEvoluDeps());

await using app = await run.orThrow(
  createEvoluApp(Schema, {
    appName: AppName.orThrow("StartOver"),
    auth: { type: "InstantStart" },
    createFirstAppOwner: true,
    transports: [],
  }),
);
const [old] = app.appOwners.get();

// Start over: add the new AppOwner first, which opens it, then remove the
// old one.
const added = await app.add(createOwnerSecret(run.deps));
assertOk(added);
assertOk(await app.remove(old.id));
assertEqual(
  app.appOwners.get().map(({ id }) => id),
  [added.value],
);
assertEqual(app.current.get()?.appOwner.id, added.value);
```

---

<a id="rename"></a>

### rename

```ts
readonly rename: (id: string & Brand<"Id"> & Brand<"OwnerId">, name:
  | string & Brand<"Trimmed"> & Brand<"MinLength1"> & Brand<"MaxLength100">
  | null) => void;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2322](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2322)

Names an AppOwner on this device, or removes its name with `null`.