@customyai/core
v0.3.0
Published
Customy SDK core: fetch transport, typed errors, retries with Retry-After, idempotency, pagination, platform discovery and machine tokens. Runs on Node, edge runtimes and browsers.
Maintainers
Readme
@customyai/core
Base común de los SDK de Customy. Sin dependencias y sin APIs propias de un runtime: funciona en Node (20+), runtimes edge y navegador.
npm install @customyai/core- Transporte
createTransport({ baseUrl, accessToken })sobre elfetchestándar: URL segura (https;httpsolo a loopback conallowLoopbackHttp, o a un host privado —RFC 1918,*.internal, nombre de una etiqueta— conallowPrivateHttp, nunca a uno público; lo recomendado es el nombre público https), límite de tiempo por intento, límite de tamaño de la respuesta. - Respuestas: un 2xx se lee como JSON; un cuerpo que no lo es es
SDK_RESPONSE_INVALID(nunca texto colado como datos). Para texto,responseType: "text". - Por llamada:
signalytimeoutMs(CallOptions,callOptions()); el plazo por llamada manda sobre el del cliente.@customyai/accesslos acepta en todos sus métodos;send(envíos y creaciones) ybilling(usage.report), en sus operaciones de escritura. - Errores: todo fallo es un
CustomySdkError { code, status, service, requestId, retryAfterMs, body };codees estable y sale del sobre de error de Customy ({ error: { code, message, requestId } }) o es unSDK_*. - Reintentos ante errores de red,
408,425,429y5xxtransitorios, con backoff exponencial y jitter, respetandoRetry-After(segundos o fecha). UnRetry-Aftermayor quemaxRetryAfterMsno se espera: el error sale conretryAfterMs. - Idempotencia:
GET/PUT/DELETEse reintentan; unPOST/PATCHsolo si llevaidempotencyKey(propia otruepara generarla, oautoIdempotencyKeyen el transporte). La misma clave viaja en todos los intentos. - Paginación por cursor:
for await (const item of paginate(fetchPage)),collect(iterable, limit). - Discovery del entorno:
discoverPlatform(issuer)lee/.well-known/customy-configuration(issuer, JWKS, token endpoint y, por producto, URL y audiencia) y rechaza endpoints fuera del origen del issuer. - Tokens de máquina (solo servidor: usan el secreto de la app):
createMachineTokenProvider({ issuer, clientId, clientSecret, audience })pideclient_credentialsla primera vez, cachea hasta poco antes de caducar y agrupa las peticiones concurrentes;createMachineTokens({ …, platform })da un proveedor perezoso por audiencia (forProduct("send")). Si el servicio responde401, el transporte invalida el token y repite una vez.
import { createMachineTokens, createTransport, discoverPlatform } from "@customyai/core";
const platform = await discoverPlatform(process.env.CUSTOMY_ISSUER!);
const tokens = createMachineTokens({ issuer: platform.issuer, clientId, clientSecret, platform });
const send = createTransport({ baseUrl: platform.products.send!.baseUrl, service: "send", accessToken: tokens.forProduct("send") });
await send.post("/api/emails", message, { idempotencyKey: `welcome-${userId}` });