@andco/sdk

El núcleo: cliente, OAuth, sesión, REST, Intents, errores y almacenamiento.

El paquete base. No asume entorno: corre en cualquier runtime con fetch. Los constructores por entorno están en sus propias entradas: /browser, /server y /cli.

AndcoClient

Una instancia configurada y ligada a un entorno: configuración y capacidad, construida sin esperar a que una sesión se resuelva.

ts
new AndcoClient(options: AndcoClientOptions)
Miembro Qué es
config la configuración normalizada, como AndcoConfig
globals { fetch, crypto }: las capacidades de runtime que usa, no un fetch suelto
oauth el protocolo: autorización, intercambio, renovación
session el almacén de sesión, AndcoSessionStore
auth las operaciones de alto nivel, AndcoAuth
rest el cliente REST, con el token vigente de session
intents el ciclo de vida neutro de Intents
isAuthed false en esta instancia
with(credentials) devuelve un cliente ligado a una credencial explícita
authorized() la misma instancia, vista como autorizada

rest e intents ya existen sin sesión: session responde sin token hasta que hay una, y la API responde 401. No hace falta with() solo para tener a quién llamar.

ts
with(credentials: AndcoCredentials | AndcoSession | string): AndcoClientAuthed
authorized(): AndcoClientAuthed

with() acepta un token suelto, una sesión completa o cualquier implementación de AndcoCredentials, y devuelve un objeto nuevo con su propio rest/intents ligados a esa credencial — pensado para un servidor que liga credenciales distintas en cada petición, sin mutar la instancia compartida. authorized() no crea credenciales nuevas: expone la sesión propia de la instancia con isAuthed: true, compartiendo rest, intents, auth y session por referencia con la instancia original.

AndcoClientAuthed tiene la misma forma que AndcoClient, con isAuthed: true y accessTokenFor(resource).

AndcoRest

ts
class AndcoRest {
  readonly http: AndcoHttpClient;
  readonly raw: AndCoRestClient;
}

Ambos son clientes de openapi-fetch generados desde el contrato OpenAPI, y ambos resuelven el token por petición: uno vencido se renueva sin que quien llama se entere.

http es el que usas. Nunca lanza — cada llamada resuelve { data, error, response } — y su fallo es un AndcoAPIError. Además expone .throwOnError() por llamada, para quien prefiera un throw.

ts
const { data, error } = await andco.rest.http.GET("/accounts");
if (error) return report(error.code);

raw es el mismo cliente sin envolver: su error es el cuerpo que devuelve la API, tal cual. Úsalo solo si necesitas exactamente eso.

http sigue siendo asignable a un cliente de openapi-fetch, así que una biblioteca que pida uno lo acepta — es lo que permite pasárselo a openapi-react-query sin adaptadores. Nombra el mapa de rutas con AndCoRestPaths, de @andco/protocol/transport; TypeScript no lo deduce solo.

AndcoAuth

ts
signIn(options?: AndcoSignInOptions): Promise<Result<AndcoSession | null>>
signOut(): Promise<Result<void>>
onChange(listener: (session: AndcoSession | null) => void): () => void
getSession(): Promise<Result<AndcoSession | null>>

signIn devuelve null sin error cuando la persona cierra la ventana o cuando el documento se reemplaza por una redirección. onChange devuelve la función de baja y dispara la carga inicial.

AndcoSignInOptions incluye scopes, presentation ("popup" o "redirect"), redirectTo, authorizations, loginHint y state.

AndcoSessionStore

El único objeto con estado del SDK. Construirlo no hace nada; la carga empieza en la primera lectura.

ts
getSnapshot(): AndcoSession | null | undefined
getServerSnapshot(): AndcoSession | null | undefined
subscribe(listener: () => void): () => void
get ready: Promise<Result<void>>
accessTokenFor(resource: string): Promise<string | null>
set(session: AndcoSession | null): Promise<Result<void>>

getSnapshot es puro y nunca inicia la carga; subscribe, ready y accessTokenFor sí. Esa asimetría es lo que hace seguro construir una instancia dentro de un render que el framework puede descartar.

Los tres estados de getSnapshot son undefined (sin resolver), null (sin sesión) y la sesión.

AndcoOAuth

ts
createAuthorizationRequest(options?): Promise<Result<AndcoAuthorizationRequest>>
exchangeCallback({ callbackUrl, request }): Promise<Result<AndcoSession>>
resolveAuthorizationUrl(parameters: URLSearchParams): Promise<Result<URL>>
refresh(refreshToken: string): Promise<Result<AndcoSession>>
userInfo(accessToken: string): Promise<Result<AndcoUser>>
createDeviceAuthorizationRequest(options?): Promise<Result<AndcoDeviceAuthorization>>
awaitDeviceAuthorization(device, options?): Promise<Result<AndcoSession>>
configuration(): Promise<oidc.Configuration>
get isConfidential: boolean

createAuthorizationRequest decide sola entre una petición directa y una PAR: usa PAR cuando la URL sería demasiado larga o cuando lleva parámetros que no deben quedar en el historial, como login_hint o authorization_details.

configuration() lee la descripción que el servidor publica de sí mismo y la memoriza. Existe porque openid-client/passport la recibe directamente.

AndcoIntents

Domain-neutral únicamente: crea y lee Intents bajo el Grant vigente, y observa los hechos que producen. Nada específico de un Resource Server en particular vive aquí.

ts
create<T>(type: string, input, options?): Promise<Result<T>>
get<T>(intentId): Promise<Result<T>>
execute(intentId): Promise<Result<void>>
events(intentId, after?, signal?): Promise<Result<AndcoEventPage>>
subscribe<E>(subject, read, decode, handler, options): () => void

createDeposit, createWithdrawal, createAutomaticCharge, closeDeposit, getAccountEvents y los listeners tipados (onIntentDepositEvent, onIntentWithdrawalEvent, onDepositEvent, onWithdrawalEvent) son específicos del dominio bancario: viven en BankIntents, que compone create, events y subscribe de aquí exactamente como lo haría un Resource Server de terceros. Ver @andco/bank-sdk.

AndcoConfig

ts
AndcoConfig.from(input: AndcoConfigInput): AndcoConfig

Normaliza y valida una vez. Lanza si algo está mal, porque un valor inválido es un defecto de arranque y no un error en tiempo de ejecución.

Miembro Qué es
clientId el cliente OAuth
endpoints auth, api, widget, ya como URL
redirectTo la URL registrada de retorno
initialScopes los scopes por omisión
locale, theme, themeMode presentación
allowedPopupOrigins orígenes que pueden participar en el mensaje de la ventana emergente
authURL(path), apiURL(), widgetURL(release) construcción de URLs, para que nadie las una a mano
with(overrides) deriva una variante sin mutar esta

Errores

ts
class AndcoError extends Error {
  readonly code: string;
  readonly status: number | undefined;
  readonly details: Readonly<Record<string, unknown>> | undefined;
}

class AndcoAPIError extends AndcoError {
  readonly error: string;
  readonly response: Response;
  static fromOpenAPIFetch(payload: unknown, response: Response): AndcoAPIError;
}

type Result<T> = { data: T; error: null } | { data: null; error: AndcoError };

function ok<T>(data: T): Result<T>;
function fail(code: string, options?): Result<never>;

AndcoError cubre todo lo que el SDK reporta. AndcoAPIError es el subconjunto que viene de un intercambio con la API: siempre trae el response — cuando la petición nunca llegó, es Response.error(), y entonces status queda en undefined.

Ramifica por code. error es el mismo identificador con el nombre que le da el contrato, y existe para que el cliente envuelto siga encajando donde se espera uno de openapi-fetch. Los campos que el contrato declara en un fallo (required_scope, required_action) quedan tanto en la raíz como en details.

Almacenamiento y bloqueo

ts
interface AndcoStorage {
  getItem(key: string): string | null | Promise<string | null>;
  setItem(key: string, value: string): void | Promise<void>;
  removeItem(key: string): void | Promise<void>;
}

interface AndcoLock {
  acquire<T>(key: string, work: () => Promise<T>): Promise<T>;
}

Implementaciones incluidas: MemoryStorage, WebStorage e InProcessLock. El almacenamiento puede ser asíncrono a propósito: un llavero del sistema o un almacén cifrado también son almacenamiento.

Entrega tu propio AndcoLock cuando varios procesos compartan una sesión: los refresh tokens rotan y dos renovaciones simultáneas se invalidan entre sí.

Credenciales

ts
interface AndcoCredentials {
  accessTokenFor(resource: string): Promise<string | null>;
}

Esa es toda la interfaz de autorización del SDK. Cualquier cosa que resuelva un token puede ligarse con with().