import { EnvironmentId } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; const ConnectionTargetBase = { environmentId: EnvironmentId, label: Schema.String, }; export class PrimaryConnectionTarget extends Schema.TaggedClass()( "PrimaryConnectionTarget", { ...ConnectionTargetBase, httpBaseUrl: Schema.String, wsBaseUrl: Schema.String, }, ) {} export class BearerConnectionTarget extends Schema.TaggedClass()( "BearerConnectionTarget", { ...ConnectionTargetBase, connectionId: Schema.String, }, ) {} export class RelayConnectionTarget extends Schema.TaggedClass()( "RelayConnectionTarget", { ...ConnectionTargetBase, }, ) {} export class SshConnectionTarget extends Schema.TaggedClass()( "SshConnectionTarget", { ...ConnectionTargetBase, connectionId: Schema.String, }, ) {} export const ConnectionTarget = Schema.Union([ PrimaryConnectionTarget, BearerConnectionTarget, RelayConnectionTarget, SshConnectionTarget, ]); export type ConnectionTarget = typeof ConnectionTarget.Type; export const PersistedConnectionTarget = Schema.Union([ BearerConnectionTarget, RelayConnectionTarget, SshConnectionTarget, ]); export type PersistedConnectionTarget = typeof PersistedConnectionTarget.Type; export type ConnectionTargetKind = ConnectionTarget["_tag"]; export type NetworkStatus = "unknown" | "offline" | "online"; export const ConnectionTransientReason = Schema.Literals([ "network", "timeout", "transport", "endpoint-unavailable", "relay-unavailable", "remote-unavailable", ]); export type ConnectionTransientReason = typeof ConnectionTransientReason.Type; export const ConnectionBlockedReason = Schema.Literals([ "authentication", "configuration", "permission", "unsupported", ]); export type ConnectionBlockedReason = typeof ConnectionBlockedReason.Type; export class ConnectionTransientError extends Schema.TaggedError()( "ConnectionTransientError", { reason: ConnectionTransientReason, detail: Schema.String, traceId: Schema.optionalKey(Schema.String), }, ) { override get message(): string { return this.detail; } } export class ConnectionBlockedError extends Schema.TaggedError()( "ConnectionBlockedError", { reason: ConnectionBlockedReason, detail: Schema.String, traceId: Schema.optionalKey(Schema.String), }, ) { override get message(): string { return this.detail; } } export type ConnectionAttemptError = ConnectionTransientError | ConnectionBlockedError; export const DPOP_ACCESS_TOKEN_REFRESH_SKEW_MS = 60_000; export type PreparedHttpAuthorization = | { readonly _tag: "Bearer"; readonly token: string; } | { readonly _tag: "Dpop"; readonly accessToken: string; readonly expiresAtEpochMs: number; }; export interface PreparedConnection { readonly environmentId: EnvironmentId; readonly label: string; readonly httpBaseUrl: string; readonly socketUrl: string; readonly httpAuthorization: PreparedHttpAuthorization | null; readonly target: ConnectionTarget; } export type SupervisorConnectionPhase = | "available" | "offline" | "connecting" | "backoff" | "connected" | "blocked"; export type ConnectionAttemptStage = "preparing" | "opening" | "synchronizing"; export interface SupervisorConnectionState { readonly desired: boolean; readonly network: NetworkStatus; readonly phase: SupervisorConnectionPhase; readonly stage: ConnectionAttemptStage | null; readonly attempt: number; readonly generation: number; readonly lastFailure: ConnectionAttemptError | null; readonly retryAt: number | null; } export type ConnectionProjectionPhase = "disconnected" | "synchronizing" | "ready"; export function connectionProjectionPhase( state: SupervisorConnectionState, ): ConnectionProjectionPhase { switch (state.phase) { case "connecting": return "synchronizing"; case "connected": return "ready"; case "available": case "offline": case "backoff": case "blocked": return "disconnected"; } } export const AVAILABLE_CONNECTION_STATE: SupervisorConnectionState = Object.freeze({ desired: false, network: "unknown", phase: "available", stage: null, attempt: 0, generation: 0, lastFailure: null, retryAt: null, });