# @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`](/docs/reference/andco-sdk-browser), [`/server`](/docs/reference/andco-sdk-server) y [`/cli`](/docs/reference/andco-sdk-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`](https://openapi-ts.dev/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`](/docs/reference/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()`.
