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 const ConnectionStorageOperation = Schema.Literals([ "open", "read", "write", "remove", "load", "save", "delete", "decode", "encode", "migrate",]);export type ConnectionStorageOperation = typeof ConnectionStorageOperation.Type; export const ConnectionStorageBackend = Schema.Literals([ "indexed-db", "desktop-secure-storage", "mobile-secure-storage", "legacy-migration", "schema",]);export type ConnectionStorageBackend = typeof ConnectionStorageBackend.Type; export class ConnectionStorageOperationError extends Schema.TaggedErrorClass<ConnectionStorageOperationError>()( "ConnectionStorageOperationError", { operation: ConnectionStorageOperation, backend: ConnectionStorageBackend, storeName: Schema.optionalKey(Schema.String), key: Schema.optionalKey(Schema.Unknown), cause: Schema.Defect(), },) { override get message(): string { return `Could not ${this.operation} local connection data.`; }} export class IndexedDbUnavailableError extends Schema.TaggedErrorClass<IndexedDbUnavailableError>()( "IndexedDbUnavailableError", {},) { override get message(): string { return "IndexedDB is unavailable in this browser context."; }} export class DesktopSecureStorageUnavailableError extends Schema.TaggedErrorClass<DesktopSecureStorageUnavailableError>()( "DesktopSecureStorageUnavailableError", {},) { override get message(): string { return "Desktop secure storage is unavailable in this system context."; }} export const ConnectionStorageFailure = Schema.Union([ ConnectionStorageOperationError, IndexedDbUnavailableError, DesktopSecureStorageUnavailableError,]);export type ConnectionStorageFailure = typeof ConnectionStorageFailure.Type; export class ConnectionTransientError extends Schema.TaggedErrorClass<ConnectionTransientError>()( "ConnectionTransientError", { reason: ConnectionTransientReason, detail: Schema.String, traceId: Schema.optionalKey(Schema.String), cause: Schema.optional(Schema.Defect()), },) { static fromStorageFailure(cause: ConnectionStorageFailure): ConnectionTransientError { return new ConnectionTransientError({ reason: "remote-unavailable", detail: cause.message, cause, }); } override get message(): string { return `Connection attempt failed (${this.reason}).`; }} export class ConnectionBlockedError extends Schema.TaggedErrorClass<ConnectionBlockedError>()( "ConnectionBlockedError", { reason: ConnectionBlockedReason, detail: Schema.String, connectionId: Schema.optionalKey(Schema.String), expectedEnvironmentId: Schema.optionalKey(EnvironmentId), actualEnvironmentId: Schema.optionalKey(EnvironmentId), traceId: Schema.optionalKey(Schema.String), cause: Schema.optional(Schema.Defect()), },) { override get message(): string { return `Connection attempt blocked (${this.reason}).`; }} export type ConnectionAttemptError = ConnectionTransientError | ConnectionBlockedError; export type PreparedHttpAuthorization = | { readonly _tag: "Bearer"; readonly token: string; } | { readonly _tag: "Dpop"; readonly accessToken: string; }; 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,});
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 const ConnectionStorageOperation = Schema.Literals([
"open",
"read",
"write",
"remove",
"load",
"save",
"delete",
"decode",
"encode",
"migrate",
]);
export type ConnectionStorageOperation = typeof ConnectionStorageOperation.Type;
export const ConnectionStorageBackend = Schema.Literals([
"indexed-db",
"desktop-secure-storage",
"mobile-secure-storage",
"legacy-migration",
"schema",
]);
export type ConnectionStorageBackend = typeof ConnectionStorageBackend.Type;
export class ConnectionStorageOperationError extends Schema.TaggedErrorClass<ConnectionStorageOperationError>()(
"ConnectionStorageOperationError",
{
operation: ConnectionStorageOperation,
backend: ConnectionStorageBackend,
storeName: Schema.optionalKey(Schema.String),
key: Schema.optionalKey(Schema.Unknown),
cause: Schema.Defect(),
},
) {
override get message(): string {
return `Could not ${this.operation} local connection data.`;
}
}
export class IndexedDbUnavailableError extends Schema.TaggedErrorClass<IndexedDbUnavailableError>()(
"IndexedDbUnavailableError",
{},
) {
override get message(): string {
return "IndexedDB is unavailable in this browser context.";
}
}
export class DesktopSecureStorageUnavailableError extends Schema.TaggedErrorClass<DesktopSecureStorageUnavailableError>()(
"DesktopSecureStorageUnavailableError",
{},
) {
override get message(): string {
return "Desktop secure storage is unavailable in this system context.";
}
}
export const ConnectionStorageFailure = Schema.Union([
ConnectionStorageOperationError,
IndexedDbUnavailableError,
DesktopSecureStorageUnavailableError,
]);
export type ConnectionStorageFailure = typeof ConnectionStorageFailure.Type;
export class ConnectionTransientError extends Schema.TaggedErrorClass<ConnectionTransientError>()(
"ConnectionTransientError",
{
reason: ConnectionTransientReason,
detail: Schema.String,
traceId: Schema.optionalKey(Schema.String),
cause: Schema.optional(Schema.Defect()),
},
) {
static fromStorageFailure(cause: ConnectionStorageFailure): ConnectionTransientError {
return new ConnectionTransientError({
reason: "remote-unavailable",
detail: cause.message,
cause,
});
}
override get message(): string {
return `Connection attempt failed (${this.reason}).`;
}
}
export class ConnectionBlockedError extends Schema.TaggedErrorClass<ConnectionBlockedError>()(
"ConnectionBlockedError",
{
reason: ConnectionBlockedReason,
detail: Schema.String,
connectionId: Schema.optionalKey(Schema.String),
expectedEnvironmentId: Schema.optionalKey(EnvironmentId),
actualEnvironmentId: Schema.optionalKey(EnvironmentId),
traceId: Schema.optionalKey(Schema.String),
cause: Schema.optional(Schema.Defect()),
},
) {
override get message(): string {
return `Connection attempt blocked (${this.reason}).`;
}
}
export type ConnectionAttemptError = ConnectionTransientError | ConnectionBlockedError;
export type PreparedHttpAuthorization =
| {
readonly _tag: "Bearer";
readonly token: string;
}
| {
readonly _tag: "Dpop";
readonly accessToken: string;
};
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,
});