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>()(
"PrimaryConnectionTarget",
{
...ConnectionTargetBase,
httpBaseUrl: Schema.String,
wsBaseUrl: Schema.String,
},
) {}
export class BearerConnectionTarget extends Schema.TaggedClass<BearerConnectionTarget>()(
"BearerConnectionTarget",
{
...ConnectionTargetBase,
connectionId: Schema.String,
},
) {}
export class RelayConnectionTarget extends Schema.TaggedClass<RelayConnectionTarget>()(
"RelayConnectionTarget",
{
...ConnectionTargetBase,
},
) {}
export class SshConnectionTarget extends Schema.TaggedClass<SshConnectionTarget>()(
"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.TaggedErrorClass<ConnectionTransientError>()(
"ConnectionTransientError",
{
reason: ConnectionTransientReason,
detail: Schema.String,
traceId: Schema.optionalKey(Schema.String),
},
) {
override get message(): string {
return this.detail;
}
}
export class ConnectionBlockedError extends Schema.TaggedErrorClass<ConnectionBlockedError>()(
"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,
});