# Changelog

## [1.2.0] — 2026-11-02

Versión menor: todo lo de la 1.1 sigue funcionando igual.

### Added

- Tipos generados de la spec (`public/openapi.json`) para el cuerpo, la query y la respuesta de cada operación, y los objetos `Quote`, `QuoteDetail`, `Invoice`, `InvoiceDetail`, `Client`, `Product`, `DomainEvent`, `WebhookEndpoint`, `Me`, `ElementsConfig`, `Collections` y `SetupPlan`. Las lecturas ya no devuelven `Record<string, unknown>`.
- Lo que faltaba de la API: `descuento`, `cupon` y `plan_pagos` al crear una cotización; `exemption_reason`, `clave_sat` y `clave_unidad_sat` en cada línea; todos los plazos de pago (`net7` a `net90`); `service_date`, `service_date_end`, `iva_incluido`, `document_mode`, `fx_buffer_pct`, `descuento`, `cupon`, `buyer_reference` y `purchase_order` al crear una factura; `desde` y `hasta` en `invoices.list()`; `factura_id` en `tasks.create()`.
- `invoices.finalize(id, { sin_comercio_exterior })`. La forma de la 1.1, `finalize(id, opciones)`, se sigue aceptando.
- `invoices.void()` devuelve una unión: la factura anulada o, con status 202, `cancelacion: 'pendiente'` mientras el receptor acepta la cancelación del CFDI.
- `workflows.invoke(id, { objeto, ids })`, `setup.propose(params)` y `setup.retrieve(id)`.
- `lastResponse` en cada objeto y página que devuelve el SDK: `requestId`, `status`, `headers`, `idempotentReplayed` y `apiVersion`. No es enumerable: no sale en `JSON.stringify`.
- Toda llamada acepta `{ idempotencyKey, timeoutMs, signal }`, también `retrieve`, `list`, `listAll`, `me`, `elements.config`, `collections.retrieve`, `webhookEndpoints.list` y los `testHelpers`.
- `listAll(params, opciones)` acepta todos los filtros de su `list` y `limit` como tamaño de página (default 200), y su resultado trae `toArray({ limit })`.
- Errores por clase, todas subclases de `CordError`: `CordAuthenticationError` (401), `CordPermissionError` (403), `CordInvalidRequestError` (400, 404 y 422, con `param` e `issues`), `CordRateLimitError` (429, con `retryAfter`), `CordIdempotencyError` (409 `idempotency_in_progress` y 422 `idempotency_key_reused`), `CordConnectionError` y `CordTimeoutError` (sin respuesta, status 0). Todo error trae `headers` y `cause`.
- `hooks.beforeRequest` y `hooks.afterResponse` por intento (método, ruta, status, request id, duración, intento e Idempotency-Key), `logger` y `logLevel`, y `CORD_LOG=debug`. El registro nunca incluye la llave ni el cuerpo.
- `verifySignature()` y `constructWorkflowDelivery()`, también en `cord.webhooks`: verifican `X-Cord-Signature-V1` o Standard Webhooks (`webhook-id`, `webhook-timestamp`, `webhook-signature`); con las dos, basta con que cuadre una. `constructWorkflowDelivery()` lee el POST de la acción "Mandar los datos a una URL" de Cord Workflows.
- `generateTestEvent(tipo, data?, opciones?)`: un evento completo con los datos de ejemplo de su tipo, firmado con las dos firmas, para probar tu endpoint sin llamar a la API.
- `dangerouslyAllowBrowser` para crear el cliente en pruebas con jsdom o happy-dom.
- `VERSION` y `Cord.VERSION`.
- Tipos del evento `quote.milestone_completed` y de `facturas` en las disputas, que la 1.1.0 no conocía; el sobre de cada evento trae `mode`, `api_version` y `test`, y el cliente trae `language`.
- `cliente_id` en `Quote` e `Invoice`, y `moneda` en `Product` y en la respuesta de `recordPayment()`.

### Changed

- Respeta el header `Cord-Should-Retry`: con `false` no reintenta esa respuesta aunque sea un 5xx (una emisión que el proveedor fiscal rechazó ya no se repite); con `true` reintenta aunque el status no esté en la lista.
- `Retry-After` se lee también como fecha HTTP; el tope sigue en 60 s. Un `Retry-After: 0` reintenta de inmediato.
- `AbortSignal`: una señal que ya llega abortada no llama a Cord, abortar durante la espera entre reintentos no hace el siguiente intento, y el timeout de cada intento cubre también la lectura del cuerpo.
- El proxy de Elements (`createElementsProxy`) sanea la cotización con la misma función que el servidor aplica a una llave publicable: ya no descarta `cupon`, `exemption_reason`, `clave_sat` ni `clave_unidad_sat`. Reenvía `limit`, `offset` y `cursor` (y `q` y `email` en clientes) junto con `meta`, reenvía `If-None-Match` y responde 304 con el `ETag` de Cord, y conserva `Retry-After` e `issues` en los errores.
- `const Cord = require('@flouviahq/node')` devuelve la clase, con las demás exportaciones como propiedades; `const { Cord } = require(...)` y el ESM siguen igual.
- Los `testHelpers` con una llave en vivo devuelven una promesa rechazada en lugar de lanzar al llamarlos.
- Sin secreto, la verificación de webhooks responde `missing_secret` (antes `missing_signature`).
- `testHelpers.webhooks.trigger()` acepta solo los tipos del catálogo de eventos.
- Los tipos generados son más estrictos que los de la 1.1: `cliente.fiscal` pide `country` y `tax_id`, `clients.create()` pide `empresa` y `products.create()` pide `nombre`. Lo que ahora no compila ya lo rechazaba el servidor.
- La versión del `User-Agent` sale de `package.json` al compilar.
- El `homepage` del paquete apunta al resumen de desarrolladores de las docs (`https://docs.cordhq.app/docs/desarrolladores/empezar/resumen`).

## [1.1.0] — 2026-10-07

- `quotes.listAll()`, `clients.listAll()` y `products.listAll()` recorren por cursor: no saltan ni repiten registros aunque haya escrituras mientras paginas.
- `list()` de cotizaciones, clientes y productos acepta `cursor`, y `meta` trae `next_cursor` (con cursor, `offset` llega `null`). Pedir por `offset` sigue funcionando igual.
- `clients.list()` acepta `q` y `email`.

## [1.0.0] — 2026-10-05

Primera versión. Reemplaza a `@flouviahq/elements/server`.

- API v1 completa: cotizaciones (crear, enviar, aprobar, rechazar, marcar pagada, borrar, borrador desde texto), clientes, productos, facturas (crear, emitir, enviar, anular, registrar pago, nota de crédito), cobranza, eventos, tareas, endpoints de webhook y configuración de Elements.
- Autopaginación por offset y por cursor (`listAll()` con `for await`).
- `Idempotency-Key` automática en toda mutación, la misma en cada reintento; reintentos con backoff y `Retry-After`; timeouts; errores con `code`, `requestId` y `docUrl`.
- `constructEvent` con WebCrypto (Node, Bun, Deno, edge). Exige la firma V1 con timestamp; la legacy solo con `allowLegacySignature`.
- `createElementsProxy` para el modo `proxyUrl` de Elements, incluido el streaming de IA.
- Acepta llaves restringidas (`rk_live_`, `rk_test_`); los errores `insufficient_permissions`, `ip_not_allowed` y `key_expired` llegan como `CordError` con su `code`.
- `testHelpers` (solo `sk_test_`): resultado forzado de la próxima emisión fiscal, cliente abriendo el link, vencimiento y disparo de cualquier webhook.
- Manda `Cord-Version` (default `CORD_API_VERSION`, configurable con `apiVersion`).
- Tipos de los 41 eventos de webhook desde el contrato que usa Cord para emitirlos.
