refactor: share async computed channel status adapters

This commit is contained in:
Peter Steinberger
2026-03-22 23:26:03 +00:00
parent d9a2666ee1
commit f0ab31366c
4 changed files with 137 additions and 44 deletions

View File

@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import {
createAsyncComputedAccountStatusAdapter,
buildBaseAccountStatusSnapshot,
buildBaseChannelStatusSummary,
buildComputedAccountStatusSnapshot,
@@ -236,6 +237,50 @@ describe("createComputedAccountStatusAdapter", () => {
});
});
describe("createAsyncComputedAccountStatusAdapter", () => {
it("builds account snapshots from async computed account metadata and extras", async () => {
const status = createAsyncComputedAccountStatusAdapter<
{ accountId: string; enabled: boolean; profileUrl: string },
{ ok: boolean }
>({
defaultRuntime: createDefaultChannelRuntimeState("default"),
resolveAccountSnapshot: async ({ account, runtime, probe }) => ({
accountId: account.accountId,
enabled: account.enabled,
configured: true,
extra: {
profileUrl: account.profileUrl,
connected: runtime?.running ?? false,
probe,
},
}),
});
await expect(
status.buildAccountSnapshot?.({
account: { accountId: "default", enabled: true, profileUrl: "https://example.test" },
cfg: {} as never,
runtime: { accountId: "default", running: true },
probe: { ok: true },
}),
).resolves.toEqual({
accountId: "default",
name: undefined,
enabled: true,
configured: true,
running: true,
lastStartAt: null,
lastStopAt: null,
lastError: null,
probe: { ok: true },
lastInboundAt: null,
lastOutboundAt: null,
profileUrl: "https://example.test",
connected: true,
});
});
});
describe("buildRuntimeAccountStatusSnapshot", () => {
it("builds runtime lifecycle fields with defaults", () => {
expect(buildRuntimeAccountStatusSnapshot({})).toEqual({

View File

@@ -37,6 +37,9 @@ type ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit> = {
audit?: Audit;
};
type ComputedAccountStatusSnapshot<TExtra extends StatusSnapshotExtra = StatusSnapshotExtra> =
ComputedAccountStatusBase & { extra?: TExtra };
/** Create the baseline runtime snapshot shape used by channel/account status stores. */
export function createDefaultChannelRuntimeState<T extends Record<string, unknown>>(
accountId: string,
@@ -164,7 +167,7 @@ export function createComputedAccountStatusAdapter<
options: Omit<ChannelStatusAdapter<ResolvedAccount, Probe, Audit>, "buildAccountSnapshot"> & {
resolveAccountSnapshot: (
params: ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit>,
) => ComputedAccountStatusBase & { extra?: TExtra };
) => ComputedAccountStatusSnapshot<TExtra>;
},
): ChannelStatusAdapter<ResolvedAccount, Probe, Audit> {
return {
@@ -196,6 +199,48 @@ export function createComputedAccountStatusAdapter<
};
}
/** Async variant for channels that compute configured state or snapshot extras from I/O. */
export function createAsyncComputedAccountStatusAdapter<
ResolvedAccount,
Probe = unknown,
Audit = unknown,
TExtra extends StatusSnapshotExtra = StatusSnapshotExtra,
>(
options: Omit<ChannelStatusAdapter<ResolvedAccount, Probe, Audit>, "buildAccountSnapshot"> & {
resolveAccountSnapshot: (
params: ComputedAccountStatusAdapterParams<ResolvedAccount, Probe, Audit>,
) => Promise<ComputedAccountStatusSnapshot<TExtra>>;
},
): ChannelStatusAdapter<ResolvedAccount, Probe, Audit> {
return {
defaultRuntime: options.defaultRuntime,
buildChannelSummary: options.buildChannelSummary,
probeAccount: options.probeAccount,
formatCapabilitiesProbe: options.formatCapabilitiesProbe,
auditAccount: options.auditAccount,
buildCapabilitiesDiagnostics: options.buildCapabilitiesDiagnostics,
logSelfId: options.logSelfId,
resolveAccountState: options.resolveAccountState,
collectStatusIssues: options.collectStatusIssues,
buildAccountSnapshot: async (params) => {
const typedParams = params as ComputedAccountStatusAdapterParams<
ResolvedAccount,
Probe,
Audit
>;
const { extra, ...snapshot } = await options.resolveAccountSnapshot(typedParams);
return buildComputedAccountStatusSnapshot(
{
...snapshot,
runtime: typedParams.runtime,
probe: typedParams.probe,
},
extra,
);
},
};
}
/** Normalize runtime-only account state into the shared status snapshot fields. */
export function buildRuntimeAccountStatusSnapshot<TExtra extends StatusSnapshotExtra>(
params: {