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

```ts
function syncStateToOwnerSyncStatus(
  state: SyncState | null,
  name: string & Brand<"UrlSafeString"> & Brand<"Name">,
  ownerId: string & Brand<"Id"> & Brand<"OwnerId">,
): OwnerSyncStatus;
```

Defined in: [packages/common/src/local-first/Shared.ts:1028](https://github.com/evoluhq/evolu/blob/f082fdd974e25fa0eb1134e4b34f15b740af8fe2/packages/common/src/local-first/Shared.ts#L1028)

Tells what an app shows about syncing an owner in a database: `Error` when a
relay has one, holding the newest failure of any relay or, without one, the
newest skipped change; otherwise the first of `Syncing`, `Synced`, and
`Offline` that a relay has, or `NoRelays`. Accepts null, the store's value
before the first snapshot. See [OwnerSyncStatus](https://evolu.dev/docs/api-reference/common/local-first/Shared/type-aliases/OwnerSyncStatus) for what to show.

The same status is the same object: statuses other than `Error` are shared
constants, and an `Error` status is the same object for the same error
object, which [SyncStateDep.syncState](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/SyncStateDep#syncstate) keeps between snapshots while it
is unchanged. So compare statuses with `===`.

### Example

```ts
import {
  assertEqual,
  assertSame,
  createId,
  createUnknownError,
  Millis,
  testCreateDeps,
  testName,
} from "@evolu/common";
import {
  syncStateToOwnerSyncStatus,
  testAppOwner,
  type PendingSyncRoute,
  type SyncConnection,
  type SyncRoute,
  type SyncState,
  type SyncTransportId,
} from "@evolu/common/local-first";

const deps = testCreateDeps();
const primaryId = createId<"SyncTransport">(deps);
const backupId = createId<"SyncTransport">(deps);

const stateOf = (
  connections: ReadonlyArray<SyncConnection>,
  routes: ReadonlyArray<SyncRoute>,
): SyncState => ({
  transports: connections.map((connection, index) => ({
    type: "WebSocket",
    id: index === 0 ? primaryId : backupId,
    label: "wss://relay.example",
    connection,
  })),
  tenants: [
    {
      type: "Active",
      name: testName,
      owners: [{ type: "Writable", ownerId: testAppOwner.id, routes }],
    },
  ],
});
const pending = (transportId: SyncTransportId): PendingSyncRoute => ({
  type: "Pending",
  transportId,
  failure: null,
  skippedError: null,
  completeAt: null,
  lastSentAt: null,
  lastReceivedAt: null,
});
const statusOf = (state: SyncState | null) =>
  syncStateToOwnerSyncStatus(state, testName, testAppOwner.id);

// Before the first snapshot, nothing syncs the owner.
assertEqual(statusOf(null), { type: "NoRelays" });

// A first connection is syncing; a lost one is offline.
const connecting: SyncConnection = { type: "Connecting" };
assertEqual(statusOf(stateOf([connecting], [pending(primaryId)])), {
  type: "Syncing",
});
const disconnected: SyncConnection = {
  type: "Disconnected",
  disconnectedAt: Millis.orThrow(2000),
  openedAt: Millis.orThrow(1000),
  error: null,
};
assertEqual(statusOf(stateOf([disconnected], [pending(primaryId)])), {
  type: "Offline",
});

// A quota failure on one relay shows before a newer skipped change on
// another, because the app can act on it.
const quotaError = {
  type: "ProtocolQuotaError",
  ownerId: testAppOwner.id,
  at: Millis.orThrow(3000),
} as const;
const open: SyncConnection = {
  type: "Open",
  openedAt: Millis.orThrow(3400),
  error: null,
};
assertEqual(
  statusOf(
    stateOf(
      [disconnected, open],
      [
        { ...pending(primaryId), failure: quotaError },
        {
          type: "Settled",
          transportId: backupId,
          skippedError: {
            type: "DecryptWithXChaCha20Poly1305Error",
            error: createUnknownError(new Error("invalid tag")),
            at: Millis.orThrow(4000),
          },
          completeAt: null,
          lastSentAt: Millis.orThrow(3500),
          lastReceivedAt: Millis.orThrow(4000),
        },
      ],
    ),
  ),
  { type: "Error", error: quotaError },
);

// The same error gives the same status object.
const quotaState = stateOf(
  [disconnected],
  [{ ...pending(primaryId), failure: quotaError }],
);
assertSame(statusOf(quotaState), statusOf(quotaState));
```