API reference › @evolu/common › local‑first/Evolu › EvoluApp

Defined in: packages/common/src/local-first/Evolu.ts:2277

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

Methods

[asyncDispose]()

[asyncDispose](): PromiseLike<void>;

Defined in: node_modules/@typescript/old/lib/lib.esnext.disposable.d.ts:38

Inherited from

AsyncDisposable.[asyncDispose]

Properties

add

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

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.


appOwners

readonly appOwners: ReadonlyStore<readonly AppOwnerEntry[]>;

Defined in: packages/common/src/local-first/Evolu.ts:2285

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


current

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

Defined in: packages/common/src/local-first/Evolu.ts:2296

The AppOwner this tab has open, or null while it has none. It starts with the one opened most recently, or as EvoluAppConfig.startAppOwnerId says. A removal of the open AppOwner, in any tab, leaves the tab with none, as does its disappearing from 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, opens it again, empty, with a new promise.


device

readonly device: DeviceEvolu<D>;

Defined in: packages/common/src/local-first/Evolu.ts:2388

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


open

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

Defined in: packages/common/src/local-first/Evolu.ts:2304

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.


remove

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

Defined in: packages/common/src/local-first/Evolu.ts:2385

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

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);

rename

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

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