Massive speed up during invitation creation at the expense of reliability. Document method for creating a reliability manager of some sort

This commit is contained in:
2026-05-30 21:21:34 +02:00
parent 0b848989a2
commit 17a41cf29a
3 changed files with 63 additions and 10 deletions

View File

@@ -155,3 +155,55 @@ xo-tui
# If not globally installed
npm run dev
```
## TODO
### Track invitation sync-server connectivity without blocking the UI
Each `Invitation` currently owns a `SyncServer` instance for its invitation
identifier. The invitation uses that instance to open an SSE connection, fetch
remote state, and publish local changes. Publish requests are intentionally
fire-and-forget so that invitation actions and the TUI stay responsive when the
sync server is slow or unavailable.
The tradeoff is that failed background requests and SSE connection changes are
not represented as application state. `SyncServer` already emits `connected`,
`disconnected`, and `error` events, and `Invitation` emits errors from failed
publishes, but there is no app-level owner that aggregates those events. The UI
therefore cannot reliably tell the user that an invitation may only be updated
locally and is not currently syncing with other participants.
Implement an app-owned `InvitationConnectivityService` (or similarly named
invitation watcher) with the following responsibilities:
- Register an invitation and its `SyncServer` when `AppService` creates or loads
it, and unregister it when the invitation is removed or stopped.
- Listen for each sync server's `connected`, `disconnected`, and `error` events,
plus invitation publish failures.
- Track connectivity separately from the invitation's business status
(`actionable`, `signed`, `ready`, and so on). Suggested transport states are
`connecting`, `online`, `offline`, and `degraded`, with the last error and
last successful connection timestamp available for diagnostics.
- Expose both per-invitation state and an aggregate app-level state such as
"one or more invitations are not syncing".
- Emit normalized connectivity-change events that the CLI can log and the TUI
can subscribe to without awaiting sync-server requests.
Keep local persistence and local invitation actions independent from remote
sync health. Failed sync attempts should not freeze normal wallet interaction.
The service should provide a retry path, or observe retry events from the SSE
client, and clear the warning after connectivity recovers. If publish retries
are added, make the retry policy explicit and preserve commit idempotency.
For UI integration, inject a small notification function or subscribe at the
app-context layer rather than having invitation instances render UI directly.
The first version can show an error dialog when the aggregate state becomes
unhealthy. A less intrusive version can expose the same state as a warning icon
or message in the TUI status bar and reserve dialogs for prolonged failures or
explicit user actions.
While making this change, consolidate invitation startup ownership. Startup is
currently triggered during `Invitation.create()` and again by
`AppService.createInvitation()`. The watcher should have one clear lifecycle
point so connections, listeners, retries, and cleanup are registered exactly
once.

View File

@@ -173,7 +173,7 @@ export class AppService extends EventEmitter<AppEventMap> {
// Attach listeners before SSE connects so updates are not missed.
await this.addInvitation(invitationInstance);
await invitationInstance.start();
invitationInstance.start();
return invitationInstance;
}

View File

@@ -224,12 +224,9 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
private async publishInvitation(
invitation: XOInvitation = this.data,
): Promise<void> {
try {
await this.syncServer.publishInvitation(invitation);
} catch (err) {
// Emit the error event. We might want to throw? but we need a better way of handling errors in the invitation system because we need the invitation to successfully initialize.
this.emit("error", err instanceof Error ? err : new Error(String(err)));
}
this.syncServer.publishInvitation(invitation).catch((error) => {
this.emit("error", error instanceof Error ? error : new Error(String(error)));
});
}
/**
@@ -374,9 +371,13 @@ export class Invitation extends EventEmitter<InvitationEventMap> {
* Update the status of the invitation and emit the new single-word status.
*/
private async updateStatus(): Promise<void> {
const status = await this.computeStatus();
this.status = status;
this.emit("invitation-status-changed", status);
this.computeStatus().then(status => {
this.status = status;
this.emit("invitation-status-changed", status);
}).catch((error) => {
this.status = `error (${error instanceof Error ? error.message : String(error)})`;
this.emit("error", error instanceof Error ? error : new Error(String(error)));
});
}
/**