import type {
AuthAccessSnapshot,
AuthAccessStreamEvent,
AuthAccessStreamSnapshotEvent,
} from "@t3tools/contracts";
import { WS_METHODS } from "@t3tools/contracts";
import * as Stream from "effect/Stream";
import { Atom } from "effect/unstable/reactivity";
import type { EnvironmentRegistry } from "../connection/registry.ts";
import { subscribe } from "../rpc/client.ts";
import { createEnvironmentSubscriptionAtomFamily } from "./runtime.ts";
export const EMPTY_AUTH_ACCESS_SNAPSHOT: AuthAccessSnapshot = {
pairingLinks: [],
clientSessions: [],
};
function upsertByKey(
values: ReadonlyArray,
next: A,
key: (value: A) => string,
): ReadonlyArray {
const nextKey = key(next);
return [...values.filter((value) => key(value) !== nextKey), next];
}
export function applyAuthAccessStreamEvent(
current: AuthAccessSnapshot,
event: AuthAccessStreamEvent,
): AuthAccessSnapshot {
switch (event.type) {
case "snapshot":
return event.payload;
case "pairingLinkUpserted":
return {
...current,
pairingLinks: upsertByKey(current.pairingLinks, event.payload, (value) => value.id),
};
case "pairingLinkRemoved":
return {
...current,
pairingLinks: current.pairingLinks.filter((value) => value.id !== event.payload.id),
};
case "clientUpserted":
return {
...current,
clientSessions: upsertByKey(
current.clientSessions,
event.payload,
(value) => value.sessionId,
),
};
case "clientRemoved":
return {
...current,
clientSessions: current.clientSessions.filter(
(value) => value.sessionId !== event.payload.sessionId,
),
};
}
}
function projectAuthAccessSnapshot(
current: AuthAccessSnapshot,
event: AuthAccessStreamEvent,
): readonly [AuthAccessSnapshot, ReadonlyArray] {
const snapshot = applyAuthAccessStreamEvent(current, event);
const projected: AuthAccessStreamSnapshotEvent = {
version: 1,
revision: event.revision,
type: "snapshot",
payload: snapshot,
};
return [snapshot, [projected]];
}
export function createAuthEnvironmentAtoms(
runtime: Atom.AtomRuntime,
) {
return {
accessChanges: createEnvironmentSubscriptionAtomFamily(runtime, {
label: "environment-data:server:auth-access-changes",
subscribe: (_input: null) =>
subscribe(WS_METHODS.subscribeAuthAccess, {}).pipe(
Stream.mapAccum(() => EMPTY_AUTH_ACCESS_SNAPSHOT, projectAuthAccessSnapshot),
),
}),
};
}