Registro de Cambios
Registro de Cambios
Sección titulada «Registro de Cambios»Todos los cambios notables de Veloce-TS están documentados en este archivo.
El formato está basado en Keep a Changelog, y este proyecto sigue el Versionado Semántico.
[Unreleased]
Sección titulada «[Unreleased]»[3.5.0] - 2026-09-07
Sección titulada «[3.5.0] - 2026-09-07»Corregido — mezclar especificadores de import rompía los decoradores en silencio
Sección titulada «Corregido — mezclar especificadores de import rompía los decoradores en silencio»veloce-ts y veloce-ts/plugins eran bundles separados, así que importar de ambos metía dos copias del framework en un mismo proceso. Las claves de metadata eran Symbol() a secas, único por instancia de módulo — así que una clase decorada a través de un especificador era invisible para el código cargado por el otro, sin error en ninguna parte.
Cómo se veía: el template fullstack importaba GraphQLPlugin de veloce-ts/plugins mientras su resolver usaba decoradores de veloce-ts. El plugin no encontraba metadata de resolvers, generaba un esquema vacío, y la primera query moría en buildSchema('') con “Syntax Error: Unexpected <EOF>” — un error que apunta a la query, que estaba bien. Rutas y guards registrados cruzando esa frontera habrían fallado igual.
- La build ESM ahora usa code splitting, así que todos los subpaths comparten una copia del framework. La identidad de clases vuelve a funcionar —
instanceofentre especificadores tenía el mismo problema. Deduplicar entre los 15 entrypoints además bajó el paquete de 0.45 MB a 0.39 MB. - Cada clave de metadata y token de DI es
Symbol.for('veloce-ts:…'), resuelto por el registro global. CommonJS no se puede dividir, así que esto es lo que lo arregla para consumidores conrequire().
Un esquema vacío ahora se explica solo en vez de fallar dentro de graphql-js, y menciona esto como causa probable.
Añadido — CLI
Sección titulada «Añadido — CLI»veloce new --installejecuta el gestor de paquetes (Bun si está, npm si no);--gitinicializa un repositorio con el primer commit, y se salta el paso si el destino ya está dentro de uno.veloce generate gateway <nombre>genera un gateway@WebSocketyveloce generate listener <nombre>una clase de listeners@On. Ambas superficies existían sin scaffolding detrás.- Los proyectos generados traen
.env.examplecon las variables que el código lee de verdad. Los templates configuran CORS desdeCORS_ORIGINSdesde la 3.1.0, documentado en ningún sitio donde alguien fuera a mirar.
Cambiado — ahora los templates enseñan el framework
Sección titulada «Cambiado — ahora los templates enseñan el framework»- Las dependencias se inyectan, no se construyen. El controlador y el resolver de ejemplo piden un
UserServicecon@Inject(UserService); cambiar el array en memoria por una base de datos no toca a ningún llamador. app.listen()se espera conawait. Es async desde que existe la rama de Node, y sin el await un fallo al enlazar el puerto se volvía una promesa rechazada sin capturar en vez del error que el template imprime.- Los templates de GraphQL y fullstack activan las suscripciones y demuestran una de extremo a extremo con
PubSub. - Los templates de GraphQL ahora dependen de
graphql. Es un peer opcional, así que nada se ejecutaba sin él — y su ausencia no es un error de tipos, solo un 501 en la primera query. - El README coincide con lo que se escribió: estructura por template, endpoints reales, tabla de scripts, y el hecho de que el proyecto corre tanto en Node como en Bun. Antes mostraba un árbol genérico y apuntaba a
/docs.html, que no es una ruta que se sirva. veloce build --sourcemapviene desactivado por defecto.- Un proyecto GraphQL ya no trae un
src/controllers/vacío.
Cambiado — el smoke test de templates era demasiado débil para cazar nada de esto
Sección titulada «Cambiado — el smoke test de templates era demasiado débil para cazar nada de esto»Solo comprobaba que una ruta respondiera con un estado no-5xx, y el endpoint de GraphQL devuelve 200 con un payload de errors cuando falta el paquete graphql — así que un template publicado sin esa dependencia parecía sano. Ahora ejecuta una mutación y una query reales, corre la comprobación exactamente una vez (reintentarla aplicaba la mutación dos veces), y se niega a arrancar si el puerto 3000 ya está ocupado.
[3.4.0] - 2026-09-07
Sección titulada «[3.4.0] - 2026-09-07»Añadido — las suscripciones GraphQL se ejecutan de verdad
Sección titulada «Añadido — las suscripciones GraphQL se ejecutan de verdad»Las suscripciones se generaban en el SDL y nada más: no había transporte de ejecución, así que subscribe no hacía nada y esta documentación lo decía. Era el hueco más grande que le quedaba al framework.
GraphQLPlugin las sirve ahora sobre graphql-transport-ws — el subprotocolo que hablan Apollo Client, urql, GraphiQL y el cliente graphql-ws — en Bun, Deno y Node.
const pubsub = new PubSub();
@Resolver()class ChatResolver { @GQLSubscription() @Returns(MessageSchema, { name: 'ChatMessage' }) messageAdded() { return pubsub.subscribe('MESSAGE_ADDED'); }
@GQLMutation() async send(@Arg('input', SendMessage) input: SendMessage) { await pubsub.publish('MESSAGE_ADDED', input); return input; }}
app.usePlugin(new GraphQLPlugin({ resolvers: [ChatResolver], subscriptions: { // Un WebSocket de navegador no puede enviar cabecera Authorization, así que // las credenciales llegan en connectionParams. onConnect: ({ connectionParams }) => verify(connectionParams?.token), },}));- Desactivado por defecto. Abrir un endpoint WebSocket debería ser una decisión deliberada;
subscriptions: truetoma los valores por defecto. - Comparte la ruta de GraphQL, que es lo que asumen los clientes — un upgrade y un POST pueden convivir ahí porque son métodos distintos.
PubSubes la fuente:publish(topic, payload)/subscribe(topic), devolviendo elAsyncIterableIteratorque quiere elsubscribe()degraphql. Está construido sobreEventBus, así que pasarglobalEventshace que un listener@On('user.created')y una suscripción GraphQL reaccionen al mismo publish. Un resolver también puede devolver un async generator normal.- La contrapresión está acotada.
maxQueueSize(1000) condrop-oldest,drop-newestoerrordecide qué cede cuando un suscriptor no da abasto. - No se filtra nada. Terminar una operación, cerrar el socket y
app.shutdown()ejecutan todos el teardown del iterador, que desengancha su listener del bus. - Necesita el paquete
graphql, ahora declarado como peer dependency opcional. En Node hace falta además@hono/node-ws, y la app debe servirse conapp.listen().
La implementación del protocolo es propia del framework — sin dependencia en runtime de graphql-ws — así que la interoperabilidad se verifica en vez de darse por hecha: CI corre el servidor contra el cliente oficial graphql-ws bajo Node. Los códigos de cierre siguen la especificación: 4400 petición inválida, 4401 no autorizado, 4403 prohibido, 4408 timeout de init, 4409 suscriptor duplicado, 4429 demasiados init.
Corregido
Sección titulada «Corregido»app.listen(0)informaba puerto 0. La instancia devuelta llevaba el puerto solicitado, así que quien le pedía uno libre al sistema no tenía forma de saber cuál le tocó — el spread que debía recogerlo copia propiedades propias enumerables, yportvive en el prototipo del servidor tanto en Bun como en Node. Corregido en los tres runtimes.- Dos plugins WebSocket en una misma app Node se destruían las conexiones.
WebSocketPluginy el endpoint de suscripciones creaban cada uno su instancia de@hono/node-ws, y cada instancia engancha un listener de'upgrade'que termina cualquier socket para el que no encuentra dueño. Ahora comparten un adaptador por aplicación. - Un conjunto de resolvers sin queries producía un esquema inservible. GraphQL exige una raíz de query en todo esquema, así que solo con suscripciones o mutaciones la primera operación fallaba con
Query root type must be provided. Se emite unaQueryde relleno cuando nada más declara una. - El
validate()degraphqllanza en vez de devolver errores cuando el esquema en sí es inválido. Dentro del manejador de mensajes de un socket eso tumbaba el proceso; ahora llega al cliente como mensajeerror. - Los resolvers GraphQL no se podían resolver sobre un WebSocket. Se resolvían siempre en scope
requestcontra uncontext.requestque no existe en una conexión que sobrevive a cualquier petición. HTTP conserva el scope de petición; un socket resuelve como singleton.
Cambiado
Sección titulada «Cambiado»- Los resolvers de suscripción se enganchan al esquema ejecutable, así que la introspección describe ahora un tipo Subscription cuyos campos sí pueden correr. Antes se dejaban fuera a propósito.
- Un resolver de suscripción que devuelve algo que no es un async iterable falla con un mensaje que nombra la clase y el método, en vez del genérico “Subscription field must return Async Iterable” de graphql-js.
[3.3.0] - 2026-09-07
Sección titulada «[3.3.0] - 2026-09-07»Añadido — el decorador @On('event')
Sección titulada «Añadido — el decorador @On('event')»Listeners de eventos declarativos, lo último que esta documentación listaba como ausente del bus de eventos. Una clase con métodos @On se registra con app.include() igual que un controlador, y su instancia sale del contenedor de DI — así que un listener inyecta los mismos servicios que un controlador.
class NotificationListeners { constructor(@Inject(Mailer) private mailer: Mailer) {}
@On('user.created') async welcome(payload: { email: string }) { await this.mailer.send(payload.email, '¡Bienvenido!'); }
@On('app.ready', { once: true }) warmCaches() { /* … */ }}
app.include(NotificationListeners);- Las suscripciones se enganchan en
compile()y se sueltan enshutdown(). Sin eso, los listeners atados alglobalEventsdel proceso sobreviven a la app que los creó, y cada app construida en una suite de tests responde al mismo evento. - Una clase puede ser controlador y listener a la vez —
include()ya no se detiene en la primera coincidencia. new VeloceTS({ eventBus })acepta un bus propio; por defecto usaglobalEvents.app.getEventBus()devuelve el que está en uso.- Un
@Onque apunta a algo que no es un método falla encompile(), con la clase y la propiedad en el mensaje, en vez de en silencio al emitir.
Ver Event Bus.
Cambiado — metodología de benchmarks, y una corrección
Sección titulada «Cambiado — metodología de benchmarks, y una corrección»La afirmación publicada de que Veloce-TS superaba a Hono crudo era falsa. La página de benchmarks y la documentación decían que Veloce-TS era más rápido que Hono crudo parseando cuerpos JSON (+16.7%) y validando con Zod (+43.8%). Esas cifras venían de una sola pasada cronometrada por framework, y una sola pasada no es una medición: tres corridas consecutivas del escenario idéntico pusieron a Hono adelante por 2%, luego 17%, luego empatado — y una cuarta puso a Veloce adelante por 14%.
Con rondas repetidas y mediana, Hono lidera en todos los escenarios, que es la única respuesta sensata para una capa construida sobre Hono. Coste medido de esa capa: 2–11%. Contra Express 4 es ~1.8× más rápido (el README decía 10×) y ~1.3–1.4× más rápido que Fastify 5.
El harness ahora corre 7 rondas, reporta una mediana con su rango intercuartílico, calibra el número de iteraciones contra tiempo de reloj, y marca los resultados demasiado ruidosos para compararse. La suite HTTP rota el orden de los servidores entre rondas para que el calentamiento no favorezca al que corrió primero.
Cambiado — limpieza de la CLI
Sección titulada «Cambiado — limpieza de la CLI»- Los fallbacks de versión decían
0.3.0.veloce --versiony, peor, el rango de dependencia queveloce newescribe en elpackage.jsongenerado caían a una versión fija que llevaba mucho sin ser la actual.veloce newademás consultaba primero elpackage.jsondel directorio actual — el proyecto en el que estuvieras parado, que no es veloce-ts. veloce newpodía colgarse en la consulta a npm. La petición al registry no tenía timeout; ahora se rinde a los 5 segundos y genera con la versión instalada, avisándolo.
Corregido
Sección titulada «Corregido»- Un archivo inexistente se servía como 200 con cuerpo vacío bajo Bun.
Bun.file()es perezoso y nunca lanza para una ruta que no existe, así queResponse.file()lo pasaba directo a la respuesta. La rama de Node, que hace stat primero, devolvía 404 correctamente — la misma llamada respondía distinto según el runtime.
Añadido — tests
Sección titulada «Añadido — tests»La cobertura sobre src/ pasó de 50.17% a ~57%, concentrada en código que casi no tenía y que carga riesgo real: los stores respaldados por Redis (caché, sesión, blacklist de tokens, rate limit, antes ~4%), WebSocketConnection, la contención de raíz de Response.file(), y request-context. El gate de cobertura en CI sube de 50% a 55% — estaba puesto exactamente en el valor medido, lo que lo hacía un cable trampa en vez de un trinquete.
[3.2.0] - 2026-09-07
Sección titulada «[3.2.0] - 2026-09-07»Añadido — los WebSockets funcionan en Node
Sección titulada «Añadido — los WebSockets funcionan en Node»WebSocketPlugin lanzaba al arrancar en Node (“requires Bun or Deno runtime”), lo que dejaba inutilizables ahí los decoradores @WebSocket / @OnConnect / @OnMessage — y los templates websocket y fullstack. Era la limitación número uno documentada del framework.
Node ahora hace el upgrade a través de @hono/node-ws, una peer dependency opcional. Bun y Deno quedan intactos: siguen haciendo el upgrade nativamente y no necesitan paquete extra.
npm install @hono/node-ws # solo hace falta en NodeToda la superficie del gateway se verifica de extremo a extremo en Node en CI: handshake del upgrade, @OnConnect, @OnMessage, salas, broadcast() y @OnDisconnect. El smoke test de templates ahora arranca los cuatro en vez de saltarse los dos que no podían correr.
authorizeUpgrade() sigue ejecutándose, con una diferencia que conviene saber: en Bun y Deno un cliente no autorizado se rechaza durante el handshake con 401, mientras que @hono/node-ws es dueño de la ruta y entrega el socket ya abierto — así que en Node la conexión se acepta y luego se cierra con 1008 Policy Violation.
El plugin engancha el manejador WebSocket en onStart(), porque necesita el http.Server real, que solo existe después de app.listen(). Sirve con app.listen(); si le pasas app.fetch a un servidor que creaste tú, el plugin se queda sin nada a lo que engancharse.
Corregido
Sección titulada «Corregido»WebSocketConnectionleíaWebSocket.OPENdel global, que solo se estabilizó en Node 22 — en Node 20, queenginessigue soportando, tocarlo lanzaba unReferenceError. Ahora usa la constante numérica que fija el estándar.- Los parámetros de socket se tipan estructuralmente (
WebSocketLike) en vez de contra elWebSocketdel DOM, así que un socket de Bun, uno de Deno y elWSContextde node-ws encajan todos.
Añadido
Sección titulada «Añadido»VeloceTS.getServer()expone el servidor en ejecución, yServerInstance.rawlleva el objeto propio del runtime.
[3.1.2] - 2026-09-07
Sección titulada «[3.1.2] - 2026-09-07»Cambiado — el paquete publicado bajó 72%
Sección titulada «Cambiado — el paquete publicado bajó 72%»Las builds de producción ya no emiten sourcemaps. Bun incrusta el texto completo de cada archivo fuente en el mapa vía sourcesContent, y con una build ESM y otra CJS eso significaba publicar todo el código TypeScript dos veces: los sourcemaps eran el 73% del tarball. Cada instalación lo pagaba para servir el caso raro de entrar a las tripas del framework con un depurador — clona el repo para eso.
| Empaquetado | Descomprimido | |
|---|---|---|
| 3.1.1 | 1.47 MB | 6.9 MB |
| 3.1.2 | 0.41 MB | 1.73 MB |
Las builds de desarrollo conservan sourcemaps inline, que es donde se usan de verdad.
Los mapas de declaración tampoco se emiten ya. Los 256 archivos .d.ts.map resolvían a ../../src/*.ts, y files no publica src/ — así que eran 351 KB de mapas apuntando a nada en un paquete instalado.
[3.1.1] - 2026-09-07
Sección titulada «[3.1.1] - 2026-09-07»Corregido — los tipos publicados eran inservibles en node16/nodenext
Sección titulada «Corregido — los tipos publicados eran inservibles en node16/nodenext»- Ningún especificador relativo de las declaraciones llevaba extensión, así que cualquier consumidor con
moduleResolution: node16onodenextsolo obteníaTS2834(“Relative import paths need explicit file extensions”) desdenode_modules/veloce-ts, y perdía todos los tipos. Los 332 especificadores relativos desrc/llevan ahora un.jsexplícito. - TypeScript rechazaba
require('veloce-ts')conTS1479aunque funciona en runtime. Un solo árbol de declaraciones servía a ambas condiciones, y TypeScript infiere el tipo de módulo de un.d.tsdesde elpackage.jsonmás cercano — la raíz dice"type": "module", así que los tipos declaraban ESM mientrasrequireresolvía adist/cjs. La build ahora emite un árbol de declaraciones CommonJS aparte y cada condición deexportslleva sus propiostypes. WebSocketManager.handleMessageBuntipaba un parámetro comoBuffer, lo que obligaba a los consumidores a instalar@types/nodesolo para typechequear un import del paquete. Ahora esUint8Array.
[3.1.0] - 2026-09-07
Sección titulada «[3.1.0] - 2026-09-07»Paridad con Node en la CLI. Todo lo que genera veloce new ahora se crea, compila, construye y ejecuta con Node puro, no solo con Bun.
Corregido — la CLI generaba proyectos que no arrancaban
Sección titulada «Corregido — la CLI generaba proyectos que no arrancaban»veloce newgeneraba una app que reventaba al arrancar. Los templatesrestyfullstackemitíancors: { origin: '*', credentials: true }, la combinación que la 3.0.0 empezó a rechazar al construir, así que un proyecto recién creado moría antes de servir una sola petición.veloce buildfallaba en Node conReferenceError: Bun is not defined: llamaba aBun.buildsin condiciones. Ahora compila con eltscdel propio proyecto cuando no corre bajo Bun.veloce devsolo lanzababun. Ahora detecta el runtime y recurre atsx, que transpila los decoradores Stage-2 que el type stripping propio de Node no maneja.- Node no podía cargar la salida compilada. Los templates importaban módulos relativos sin extensión, que
tscemite tal cual y el cargador ESM de Node rechaza:node dist/index.jsmoría conERR_MODULE_NOT_FOUND. Los templates y la salida develoce generateahora escriben la extensión.js, que Bun resuelve de vuelta al fuente.ts. - Los templates
websocketyfullstackpasaban una opciónhandlersque no existe enWebSocketPluginConfig, así que esos proyectos no pasaban el type-check. Los gateways se registran conapp.include(), igual que los controladores. - A los proyectos generados les faltaba
@hono/node-server, así queapp.listen()lanzaba en Node.
Añadido
Sección titulada «Añadido»--runtime <auto|bun|node>enveloce devyveloce build.autoprefiere Bun cuando es utilizable y si no toma el camino de Node.- Los proyectos generados son agnósticos del runtime: los scripts pasan por el binario
veloceen vez de fijarbun,engines.nodees>=20,tsxviene como devDependency para el servidor de desarrollo en Node, y se incluye un scripttypecheck. veloce newavisa al generar un template con WebSocket si Bun no está instalado, en vez de dejarte una app que lanza al primer arranque.- Smoke test de templates en CI (
npm run test:templates): empaqueta el árbol de trabajo, genera los cuatro templates contra ese tarball y luego instala, compila, construye y arranca cada uno con Node. Es justo la comprobación que faltaba cuando se publicaron los templates rotos.
Bun.build es una API en proceso, así que el camino de build con Bun exige que la propia CLI corra bajo Bun. npm run build en una máquina con Bun instalado igual toma el camino de Node, porque npm arranca la CLI con Node.
[3.0.1] - 2026-09-05
Sección titulada «[3.0.1] - 2026-09-05»Corregido
Sección titulada «Corregido»MemoryRateLimitStoreyRedisRateLimitStoreno se exportaban. La 3.0.0 introdujo el store de rate limit intercambiable y lo documentó, pero ninguna de las dos clases se reexportaba desdeveloce-tsni desdeveloce-ts/middleware, así que un consumidor no podía pasarRateLimitOptions.store. Ahora ambas se exportan desde los dos puntos de entrada; los tiposRateLimitStoreyRateLimitHitya eran accesibles víaveloce-ts/types.
[3.0.0] - 2026-09-05
Sección titulada «[3.0.0] - 2026-09-05»Revisión completa del código. Actualiza si usas @Auth(), @Session(), @RequireCSRF() o la familia @CanAccess — esos guards nunca se aplicaron en 2.x, así que las rutas que debían proteger respondían a peticiones anónimas. Consulta MIGRATION.md.
Seguridad
Sección titulada «Seguridad»- Los guards de ruta nunca se aplicaban.
AuthPlugin,SessionPluginyPermissionPluginreemplazabanapp.compiledesde dentro de su propioinstall(), peroinstall()se ejecuta dentro delcompile()que ya está corriendo, así que el reemplazo nunca se ejecutaba. En la práctica una ruta con@Auth()sin parámetro@CurrentUser()respondía 200 a peticiones anónimas,@Auth({ roles: ['admin'] })admitía a cualquier usuario autenticado, y@Session({ required: true })/@RequireCSRF()no hacían nada. Los tres plugins ahora inyectan su guard en el middleware de la ruta duranteinstall(), igual que ya hacíaRBACPlugin.@Roles()no se vio afectado: RBAC era el único plugin bien conectado. - Las cookies de sesión ahora se firman con
SessionConfig.secret(HMAC-SHA256, comparación en tiempo constante). Antes la cookie llevaba el id de sesión en crudo, así que cualquiera podía sustituirlo por otro y recibir esa sesión.secretahora es obligatorio, y las cookies usansecure: truepor defecto conNODE_ENV=production. - La rotación de refresh tokens es atómica.
refreshAccessToken()verificaba primero y ponía en lista negra después, así que dos peticiones concurrentes con el mismo token tenían éxito las dos y cada una recibía un par válido. Ahora el token viejo se reclama antes de emitir el par nuevo; la perdedora falla. - El cuerpo de las peticiones tiene límite de tamaño. El nuevo
VeloceTSConfig.bodyLimit(1 MiB por defecto,0lo desactiva) rechaza cuerpos grandes con 413 antes de que ningún handler los lea. - Las respuestas cacheadas pueden variar por llamante.
@Cache()ahora respetavaryByHeadersy el nuevokeyGenerator(c); antes la clave era solo método + ruta + params, así que una ruta cacheada por usuario podía servir el cuerpo de un usuario a otro. Cachear una ruta que lee@CurrentUser()con una clave independiente del llamante ahora emite un aviso al compilar. - Los flujos de auth ya no reportan fallos internos como 401. Una caída de base de datos dentro de
login/register/refreshse envolvía enAuthenticationException(error.message), reportando un fallo de infraestructura como credenciales inválidas y filtrando el mensaje interno. - Los errores de handlers WebSocket ya no se devuelven al cliente. Solo se responden problemas de parseo o esquema; el resto se registra en el servidor.
- Las entradas de revocación JWT se indexan por
jtien vez del token completo, así que la lista negra ya no guarda credenciales reutilizables. - CORS: se emite
Vary: Origincuando el origen permitido depende de la petición, yorigin: '*'junto acredentials: trueahora lanza al configurar, en vez de producir respuestas que el navegador rechaza en silencio. Response.file()gana la opciónroot(yResponse.fileFrom(root, path)) que confina la búsqueda a un directorio; las rutas que se salen reciben 403.
Cambios incompatibles
Sección titulada «Cambios incompatibles»SessionConfig.secretes obligatorio. El formato de la cookie cambia, así que las sesiones emitidas por 2.x se rechazan y esos usuarios vuelven a iniciar sesión.bodyLimitvale 1 MiB por defecto. Súbelo si aceptas subidas de archivos:new Veloce({ bodyLimit: 25 * 1024 * 1024 }).docsvaletruepor defecto y ahora sí monta OpenAPI en/openapi.jsony/docs. RegistrarOpenAPIPlugina mano sigue teniendo prioridad; usadocs: falsepara desactivarlo.engines.nodesube a>= 20(Node 18 llegó a fin de vida).Adapter.listen()puede devolverPromise<ServerInstance>— solo afecta a adaptadores propios.TokenBlacklistrecibe ids de token (jti) en vez de tokens crudos; los stores propios pueden implementar elclaim()opcional para rotación atómica.- El constructor de respuestas se exporta como
VeloceResponse(yRes). El exportResponseanterior ensombrecía elResponseglobal del Web API en cualquier archivo que lo importara; sigue disponible como alias obsoleto.
Corregido
Sección titulada «Corregido»@UseInterceptora nivel de clase nunca se ejecutaba — la búsqueda leíatarget.constructorsobre un valor que ya era la clase.- Los filtros de excepción nunca veían errores de validación ni de inyección de dependencias. Ahora también les llegan los errores lanzados por middleware y guards de ruta.
DIContainerreportaba dependencias circulares falsas con concurrencia. Los caminos de resolución ahora son por contexto asíncrono, y las resoluciones concurrentes de un singleton comparten una sola promesa, así que se crea exactamente una instancia.- Dos controladores con el mismo nombre de clase se pisaban las rutas. El registro usaba
target.name; ahora usa la identidad de la clase. - Las opciones
docsypluginsse aceptaban y se ignoraban. - Un cuerpo JSON malformado devolvía 422 “Expected object, received null” — ahora es un 400 que nombra el problema de JSON.
- A
AuthService.refresh()le faltaba unawait, así que sucatchnunca corría y quien lo llamaba recibía unErrorpelado en vez deInvalidTokenException. - El
listen()de Node nunca pudo funcionar. Usabarequire('@hono/node-server'), y el bundle ESM publicado no tienerequireen scope, así que siempre lanzaba y reportaba el paquete como ausente. Ahora usa import dinámico. - Los generadores de SSE/streaming que lanzan ahora marcan el stream con error y lo registran, en vez de producir un rechazo no capturado.
- Los listeners
process.on('SIGTERM'/'SIGINT')que registraban el rate limiter yMemorySessionStoreanulaban el manejo de señales por defecto de Node para toda la aplicación.
Añadido
Sección titulada «Añadido»RateLimitStoreconMemoryRateLimitStore(por defecto) yRedisRateLimitStore, para aplicar un límite compartido entre instancias en vez de por proceso.- Variables de contexto tipadas —
c.get('auth.user')viene tipado y una clave mal escrita es un error de compilación. VeloceTSConfig.bodyLimity@Cache({ keyGenerator }).
[2.0.0] - 2026-07-26
Sección titulada «[2.0.0] - 2026-07-26»Revisión profunda de seguridad, empaquetado y rendimiento. Si despliegas en Node.js, actualiza — la 1.2.0 publicada estaba rota ahí. Consulta MIGRATION.md para los dos cambios incompatibles.
Seguridad
Sección titulada «Seguridad»- Un refresh token podía reutilizarse como access token.
JWTProvider.verifyAccessToken()ignoraba el claimtypedel payload, y comorefreshSecretcae por defecto asecretcuando no se define, un refresh token — con vida por defecto de 7 días — verificaba correctamente como access token. Ahora se rechaza cualquier payload contype === 'refresh'. RS256/RS384/RS512nunca funcionaron de verdad. Se aceptaban comoJWTConfig.algorithm, pero la firma siempre usaba elsecretcompartido, así que los tokens iban firmados con HMAC y la garantía asimétrica no existía. Se añadieronprivateKey/publicKey; el constructor ahora lanza si se configura un algoritmo RS* sin ambas.- El rate limiting confiaba en cabeceras falsificables. El
keyGeneratorpor defecto leíaX-Forwarded-For/X-Real-IPsin condiciones, así que cualquier cliente podía falsificar su identidad de rate limit. Ahora se ignoran salvo que se active la nueva opcióntrustProxy: true; el modo por defecto usa la IP real del peer. - Los fallos de
@ResponseSchemase tragaban, dejando que salida malformada del handler llegara al cliente. Ahora se registran y devuelven 500.
Cambiado — incompatible
Sección titulada «Cambiado — incompatible»- Los métodos de verificación y revocación de
JWTProviderahora sonasync:verifyAccessToken,verifyRefreshToken,refreshAccessToken,blacklistToken,isBlacklisted,cleanupBlacklist. Esto habilita almacenes de revocación intercambiables. Olvidar elawaites peligroso, no solo incorrecto — la Promise devuelta siempre es truthy. - Los controladores con decoradores ahora se resuelven como
singleton, no una instancia nueva por petición. El estado por petición en campos de instancia ahora se comparte. Puedes optar por lo anterior con@Controller(prefix, { scope: 'transient' }).
Añadido
Sección titulada «Añadido»Seguridad de tipos
Sección titulada «Seguridad de tipos»- API funcional tipada —
app.get/post/put/delete/patchyapp.route()son genéricos sobre el conjunto de schemas Zod declarado, así que los argumentos del handler se infieren de extremo a extremo sin casts. El orden de inyección es body, query, params, headers; solo los schemas declarados aportan un argumento. - Se eliminó
anyde la superficie pública: configuración de plugins, configuración del store de caché, metadatos de parámetros, el registro de metadatos de auth/RBAC/sesión, y los tipos de retorno del adaptador Express.
APIs nuevas
Sección titulada «APIs nuevas»@Controller(prefix, { scope })— scope de instanciación explícito (singleton|request|transient).RedisTokenBlacklist/MemoryTokenBlacklisty la interfazTokenBlacklist— revocación de tokens que se propaga entre réplicas. La lista negra en memoria nunca lo hizo, así que el logout fallaba en silencio al revocar en otras instancias.Plugin.onStart(app)/Plugin.onStop(app)— hooks de ciclo de vida. El contrato era solo de instalación, así que un plugin con recursos abiertos no tenía forma de liberarlos.onStopcorre en orden inverso de instalación recolectando errores.app.getFetchHandler()— la forma de exportfetch(request, env, ctx)que esperan los runtimes serverless/edge. Es la vía soportada para desplegar en Cloudflare Workers, dondelisten()no puede funcionar.CacheManager.destroy()— libera los timers y conexiones de todos los stores.- Endurecimiento de WebSocket —
heartbeatIntervalMs,heartbeatTimeoutMs,idleTimeoutMs,maxMessageSizeBytes. Los mensajes demasiado grandes reciben un frame de error y código de cierre 1009. @Returns()para tipos de retorno en GraphQL, ycreateTimeoutMiddleware()exportado desdeveloce-ts/middleware.LoggerConfig.prettyahora se respeta vía un transportepino-pretty— antes se aceptaba y se ignoraba.
Tooling y CI
Sección titulada «Tooling y CI»- CI ahora corre una matriz multi-runtime real: Bun fijado + latest en ubuntu/windows/macos, un job de smoke en Node 20/22 que importa los artefactos ESM y CJS construidos, una etapa Deno tolerante a fallos, validación de paquete aislada, y un gate de cobertura que sube su reporte.
- El CLI corre bajo Node puro. La entrada
binapuntaba a un archivo TypeScript con#!/usr/bin/env bun, así que no podía ejecutarse sin Bun pese a que el paquete anuncia Node >= 18. - El fallo al generar
.d.tsahora es fatal en builds de producción; antes avisaba y continuaba, así que un release podía publicar tipos rotos con el pipeline en verde. - La validación de paquete verifica que cada condición de
exportsresuelva a un archivo real y tipa un consumidor generado contradist/types. Esto es lo que detectó las rutas rotas deadapters/*. veloce generate openapiahora usa elOpenAPIGeneratorreal en lugar de un constructor artesanal que ignoraba Zod, y el cliente generado emite interfaces reales derivadas de los schemas en lugar dePromise<any>.
Corregido — empaquetado
Sección titulada «Corregido — empaquetado»La 1.2.0 publicada no funcionaba para consumidores de Node:
veloce-ts/adapters/*no resolvía — el mapaexportsapuntaba adist/{esm,cjs}/adapters/*.jspero el build generadist/{esm,cjs}/src/adapters/*.js.- Las dependencias de runtime ya no se empaquetan dentro de
dist. Empaquetarlas enviaba una segunda copia de cada una, lo que rompía la identidad de Zod (el framework comprueba schemas creados con tu zod; una copia empaquetada es una clase distinta) y re-transpilaba código de terceros, dejando el regex desemverinválido y lanzando al hacer require. El paquete ahora pesa 1.33 MB, contra 3.82 MB antes. - El bundle ESM emitía
import.meta.require, un global exclusivo de Bun; se cambió al targetnode. import { sign } from 'jsonwebtoken'rompía el cargador ESM de Node — es un paquete solo-CJS. Ahora se importa vía su export por defecto.dist/cjs/*.jsse interpretaba como ESM porque elpackage.jsonraíz declara"type": "module". El build ahora generadist/cjs/package.jsoncon{"type":"commonjs"}.
Corregido
Sección titulada «Corregido»- GraphQL nunca ejecutaba. Los resolvers se pasaban como una forma anidada de
rootValuequegraphql-jsno lee, así que toda query resolvía anull, y el tipo de retorno de cada campo estaba fijado aString(los tipos de objeto nunca se emitían). Los resolvers ahora se adjuntan directamente a los campos del schema, y los tipos de objeto/entrada se derivan de Zod. Las suscripciones siguen sin transporte de ejecución. - El dispatch de peticiones es 1.9–2.6× más rápido. La capa de “metadata compilada” precomputaba un regex de ruta, ordenamientos de índices y flags de tipo que el handler nunca leía; los controladores se re-instanciaban por petición; la búsqueda de interceptores hacía dos lecturas de reflect por petición; y los módulos de caché/contexto/auth se cargaban con
await import()dentro del hot path. Todo se resuelve ahora una sola vez encompile(). - Doble rollback en
BaseTransactionManager— un handler que terminaba bien pero estaba marcado comorollbackOnlylanzaba, el mismocatchhacía rollback otra vez, y el “transaction not found” resultante enmascaraba el error real. app.shutdown()nunca ejecutaba los handlers deonShutdown()— solo lo hacía el camino de SIGTERM/SIGINT.CacheManager.reset()filtraba el intervalo de limpieza del store anterior, acumulando timers y manteniendo procesos vivos.- El “LRU” de la caché en memoria era en realidad FIFO — desalojaba por tiempo de inserción, no por último acceso. Ahora
get()actualiza la recencia. - Los stores Redis de caché y sesión usaban el comando bloqueante
KEYSenclear()/deletePattern()/length()/all(), que congela el event loop de Redis con keyspaces grandes. Ahora usanSCANpor cursor conDELpor lotes. EventBus: un listener que lanzaba abortaba el resto (Promise.all). Ahora todos se ejecutan; los fallos se recolectan y se relanzan juntos comoAggregateError.Response.file()lanzaba en runtimes distintos de Bun — ahora hace streaming víanode:fs.- El adaptador Express bufferizaba el cuerpo completo de la respuesta, rompiendo
@SSE()y@Stream()(los clientes no recibían nada hasta que el generador terminaba). Ahora hace streaming por chunks con manejo de contrapresión. BaseRepository.count()/updateMany()/deleteMany()hacían escaneos completos de tabla en silencio (count()cargaba en memoria cada fila coincidente; las escrituras masivas hacían fetch-y-bucle). Ahora lanzanNotImplementedError; los tres adaptadores incluyen sobrescrituras nativas.- Dos manejadores de SIGTERM/SIGINT en competencia con timeouts de apagado separados se consolidaron en uno.
- Los parámetros de query se validaban dos veces por petición con formas de error inconsistentes.
Benchmarks
Sección titulada «Benchmarks»Re-medidos y republicados; los números anteriores estaban etiquetados como v0.4.3 y se habían quedado obsoletos a lo largo de varios releases. El dispatch de peticiones mejoró entre 87% y 158% respecto a v0.4.18. Frente a otros frameworks, Veloce-TS supera a Hono crudo en parseo de body JSON (+16.7%) y validación Zod (+43.8%), y es ~33% más lento en una ruta trivial /hello. Los tests pasaron de 509 a 781.
[1.2.0] - 2026-06-27
Sección titulada «[1.2.0] - 2026-06-27»Añadido
Sección titulada «Añadido»CLI — Generación de código
Sección titulada «CLI — Generación de código»veloce generate controller <nombre>— genera clase@Controllercon stubs de rutas CRUDveloce generate service <nombre>— genera una clase de servicio planaveloce generate module <nombre>— genera controlador + servicio + DTO + barrel ensrc/modules/<nombre>/veloce generate resolver <nombre>— genera clase@Resolvercon stubs de@GQLQueryy@GQLMutationveloce generate dto <nombre>— genera esquema Zod + tipo TypeScript inferidoveloce generate middleware <nombre>— genera skeleton de middleware Honoveloce generate plugin <nombre>— genera scaffold de claseVelocePlugin
Apagado gradual (Graceful Shutdown)
Sección titulada «Apagado gradual (Graceful Shutdown)»app.onShutdown(handler)— registra un handler async; se ejecutan en orden inverso al de registro al recibir SIGTERM/SIGINTapp.setShutdownTimeout(ms)— configura el tiempo máximo de espera antes deprocess.exit(1)(por defecto: 30 000 ms)app.registerShutdownSignals()— llamado automáticamente porapp.compile(); adjuntaprocess.on('SIGTERM')yprocess.on('SIGINT')
Filtros de excepción
Sección titulada «Filtros de excepción»- Interfaz
ExceptionFilter<T>— implementacatch(error: T, c: Context): Responsepara producir respuestas de error tipadas @Catch(...errorClasses)— vincula una clase de filtro a uno o más tipos de errorapp.useFilter(filter)— registra un filtro global; se comprueba en orden de registro, gana el primero que coincideFilterManager— gestor interno; exportado para integraciones personalizadas
Interceptores
Sección titulada «Interceptores»- Interfaz
Interceptor— implementaintercept(ctx: ExecutionContext, next: () => Promise<Response>): Promise<Response> ExecutionContext— contexto tipado con camposrequest,controlleryhandler@UseInterceptor(...interceptors)— aplica interceptores a un método o clase; se apilan alrededor del handler como middlewareapp.useInterceptor(interceptor)— registra un interceptor global; se ejecuta antes de los de clase/método
Respuestas en streaming
Sección titulada «Respuestas en streaming»@SSE()— marca un handlerAsyncGeneratorcomo flujo de Server-Sent Events (text/event-stream)@Stream(contentType)— marca un handlerAsyncGeneratorcomo flujo raw con tipo de contenido personalizado (p. ej.'text/csv')
Bus de eventos
Sección titulada «Bus de eventos»EventBus— pub/sub en proceso conon,once,off,emit(async, paralelo),emitSync,removeAllListeners,listenerCountglobalEvents— singleton compartidoEventBuspara comunicación entre módulos sin inyección de dependencias
Decoradores extra
Sección titulada «Decoradores extra»@Throttle(limit, windowMs)— límite de tasa por endpoint que anula la configuración global@ApiVersion(v)— antepone/v<v>a la ruta del endpoint para APIs versionadas@ResponseHeader(name, value)— establece una cabecera de respuesta fija para la ruta@Redirect(url, status?)— devuelve una respuesta de redirección (estado por defecto: 302)@Deprecated(message?)— marca la ruta como obsoleta en OpenAPI y registra un aviso al arrancar
Aislamiento de tests
Sección titulada «Aislamiento de tests»isolate()— restablece todo el estado singleton (CacheManager, registros de plugins) entre ejecuciones de test en paralelocompileTestApp(app)— compila la app y devuelve la instancia Hono; wrapper de conveniencia para tests unitarios
OpenAPI 3.1
Sección titulada «OpenAPI 3.1»- Versión de la especificación actualizada de
3.0.xa3.1.0 - Añadido
jsonSchemaDialect: 'https://spec.openapis.org/oas/3.1/dialect/base' nullable: truenormalizado a{ type: ['x', 'null'] }o{ oneOf: [T, { type: 'null' }] }mediantenormalizeFor31()
Cambios con ruptura
Sección titulada «Cambios con ruptura»Ninguno — todos los cambios son aditivos y retrocompatibles con v1.0.1.
[1.0.1] - 2026-06-26
Sección titulada «[1.0.1] - 2026-06-26»Corregido
Sección titulada «Corregido»- Pipeline de decoradores GraphQL:
@Resolver/@GQLQuery/@GQLMutationahora funcionan de extremo a extremo.GraphQLSchemaBuilderleía de los Maps de la instancia de MetadataRegistry que nunca se llenaban (los decoradores escribían con un namespace de símbolos local separado). Ahora lee directamente de reflect-metadata víagetResolverMetadata/getFieldsMetadata. PermissionManager.revokePermission(): El filtroresourceId ? p.resourceId !== resourceId : truesiempre devolvíatruecuando no se pasabaresourceId, por lo que nunca se eliminaba nada. Corregido.
Añadido
Sección titulada «Añadido»- Opción
resolversenGraphQLPlugin: Pasa las clases resolver directamente —new GraphQLPlugin({ resolvers: [UserResolver] })— como alternativa aapp.include(). Ambas rutas se combinan y deduplicarán automáticamente. - Reconocimiento de resolvers en
app.include():include()detecta ahora las clases decoradas con@Resolvery registra sus metadatos, de modo queapp.include(UserResolver)alimenta al constructor del esquema GraphQL automáticamente.
- 89 tests nuevos (509 en total): PermissionPlugin (16), HealthCheckPlugin (25), Logger (20), OAuthPlugin (28), decoradores/pipeline GraphQL (8 nuevos, 17 en total en la suite).
[0.4.5] - 2026-03-29
Sección titulada «[0.4.5] - 2026-03-29»Corregido
Sección titulada «Corregido»- CORS en respuestas de error: las respuestas del manejador de errores (401, 422, 500,
application/problem+json, etc.) incluyenAccess-Control-Allow-Originy el resto de cabeceras CORS configuradas, alineadas con las respuestas OK (mergeVeloceCorsHeaders,VELOCE_CORS_HEADERS_KEY,VeloceCorsHeadersSnapshot).
- Mismo parche CORS que en 0.4.4; npm no permite republicar una versión tras
unpublish, así que 0.4.5 es la versión instalable.
[0.4.4] - 2026-03-28
Sección titulada «[0.4.4] - 2026-03-28»No usar: el tarball en npm llegó roto (404). Tras unpublish, el registro bloquea reutilizar ese número. Usa 0.4.5 para el arreglo de CORS.
[0.4.3] - 2026-03-27
Sección titulada «[0.4.3] - 2026-03-27»Añadido
Sección titulada «Añadido»- RFC 9457 (Problem Details): errores por defecto con
Content-Type: application/problem+json, campostype,title,status,detail,instancey extensiones (violations/detailsen validación;debugen 500 solo en desarrollo). VeloceTSConfig.errorResponseFormat:'rfc9457'(defecto) o'legacy'para el JSON{ error, statusCode, details? }anterior.- Helpers exportados:
problemTypeUri,resolveProblemType,resolveProblemTitle,buildProblemInstance,toLegacyErrorBody,sendErrorResponse,PROBLEM_JSON_MEDIA_TYPE,DEFAULT_PROBLEM_TYPE_BASE. HTTPExceptionOptions:problemTypeytitleopcionales enHTTPException.
Cambiado
Sección titulada «Cambiado»- Excepciones de auth usan el formato unificado con tipos de problema
authentication-error/authorization-error.
Documentación
Sección titulada «Documentación»- Cabeceras
@moduleen núcleo, errores, validación, DI, respuestas, logging, middleware, adaptadores, testing y barrels. - Benchmarks actualizados para v0.4.3.
- El cuerpo de error sigue incluyendo
errorystatusCodejunto a los campos RFC 9457. Paraapplication/jsonen errores, usaerrorResponseFormat: 'legacy'.
[0.4.2] - 2026-03-27
Sección titulada «[0.4.2] - 2026-03-27»Corregido
Sección titulada «Corregido»- HealthCheckPlugin: los nombres de los checkers se asignan de forma segura en Bun (
Object.defineProperty+ respaldo) para que no falle al poner.nameen funcionesasync. - Export
@Req: el decorador de parámetro ya se exporta en la entrada principal del paquete (import { Req } from 'veloce-ts').
Documentación
Sección titulada «Documentación»- Guías y referencia API actualizadas para
@Ctx(),@Req()y la API real de HealthCheckPlugin (arraychecks,HealthCheckers.database/memory/disk).
[0.4.1] - 2026-03-27
Sección titulada «[0.4.1] - 2026-03-27»Corregido
Sección titulada «Corregido»include()ya no descarta los campos definidos por decoradores (comostatusCodede@HttpCodeyresponseSchemade@ResponseSchema) al registrar rutas de controladores.
Añadido
Sección titulada «Añadido»- Suite de tests completa (53 tests) que cubre: API funcional, rutas con decoradores, validación de body/query, excepciones HTTP y contenedor DI.
- Logger de respaldo basado en
console: sipinono está instalado, el framework ya no falla al arrancar — usaconsoleautomáticamente.
Cambiado
Sección titulada «Cambiado»pino,pino-prettyeioredismovidos aoptionalDependencies— ya no se instalan automáticamente (~7 MB menos por defecto). Instálalos explícitamente conbun add pino pino-prettyobun add ioredissi los necesitas.winstoneliminado de las dependencias (estaba listado pero nunca fue usado por el framework).- Bundle principal reducido de 444 KB → 408 KB (ESM minificado).
Deprecado
Sección titulada «Deprecado»- Export
FastAPITS: usaVeloceTSo el alias cortoVeloceen su lugar.FastAPITSserá eliminado en v1.0.0.
[0.4.0] - 2026-03-27
Sección titulada «[0.4.0] - 2026-03-27»Esta versión representa la mayor actualización desde el lanzamiento inicial. Se añadieron más de 25 mejoras distribuidas en tres oleadas de trabajo, cubriendo la cadena completa desde la generación de rutas hasta la documentación OpenAPI, el testing, el ORM y la CLI.
🚀 Nuevos Decoradores
Sección titulada «🚀 Nuevos Decoradores»Documentación OpenAPI (shorthand)
Sección titulada «Documentación OpenAPI (shorthand)»Cinco decoradores de una sola línea como alternativa concisa a @ApiDoc({...}):
@Summary(text)— descripción corta visible en la lista de Swagger UI@Description(text)— texto largo en el panel de detalle de la operación@Tag(name)— asigna un tag individual; apilable con múltiples@Tag@Tags(...names)— asigna varios tags en un solo decorador@Deprecated()— marca la ruta como obsoleta (tachado en Swagger UI)
Control de respuesta
Sección titulada «Control de respuesta»@HttpCode(statusCode)— sobreescribe el código HTTP de respuesta del handler@ResponseSchema(schema, statusCode?)— valida y sanitiza la respuesta con un esquema Zod; informa el modelo de respuesta al spec de OpenAPI
Middleware declarativo por ruta
Sección titulada «Middleware declarativo por ruta»@Timeout(ms, message?)— aborta la petición con 408 Request Timeout si el handler supera el límite. Emite el headerX-Timeout-Ms@RateLimit(options)— aplica rate-limiting a nivel de ruta individual. Los headers estándarX-RateLimit-*se envían automáticamente
🛠️ Mejoras al Framework Core
Sección titulada «🛠️ Mejoras al Framework Core»Generador OpenAPI
Sección titulada «Generador OpenAPI»- Auto-tagging: deriva tags automáticamente del primer segmento del path sin necesidad de anotarlos manualmente
- Bearer security scheme: añade
components.securitySchemes.bearerAuthal spec y aplicasecurity: [{ bearerAuth: [] }]en rutas protegidas automáticamente - 401 automático: rutas protegidas reciben una respuesta
401 Unauthorizeddocumentada sin configuración adicional - Soporte de
@HttpCodey@ResponseSchemaen el spec generado
Sistema de Excepciones HTTP
Sección titulada «Sistema de Excepciones HTTP»Seis nuevas clases de excepción:
ConflictException(409),GoneException(410),PayloadTooLargeException(413)UnprocessableEntityException(422),TooManyRequestsException(429),ServiceUnavailableException(503)
Logger estructurado en ErrorHandler
Sección titulada «Logger estructurado en ErrorHandler»- Errores 5xx se registran con
getLogger().errorincluyendo path, método, status y stack - Errores 4xx se registran como
warnen entorno de desarrollo
🧪 TestClient — API fluida y helpers de autenticación
Sección titulada «🧪 TestClient — API fluida y helpers de autenticación»Reescritura completa de TestClient:
TestResponsecon métodos encadenables:expectStatus(),expectOk(),expectCreated(),expectJson(),expectField(),expectHeader(),expectArrayLength()withToken(token),withHeaders(headers)— instancias inmutables con auth pre-configuradaloginAs(credentials, endpoint?)— hace login y retorna cliente autenticadoregisterAndLogin(user, endpoints?)— registra y hace login en una sola llamadaclearAuth()— limpia el token almacenado
🗄️ Drizzle ORM — Integración DI
Sección titulada «🗄️ Drizzle ORM — Integración DI»registerDrizzle(app, db, token?)— registra como singleton en elDIContainer@InjectDB(token?)— decorador de parámetro, alias de@Depends(DB_TOKEN)DB_TOKEN— símbolo por defecto para el token de inyección
📊 Paginación mejorada
Sección titulada «📊 Paginación mejorada»PaginationMetaincluyefromyto(rango 1-based)createCursorPaginatedResultcon nuevo parámetrohadPrevCursorcreateMultiCursorydecodeMultiCursorpara cursores compuestospaginate<T>(data, total, page, limit)— helper standaloneparseCursorQuery()yPaginationHelper.parsePaginationQuery()mejorados
🔌 Express Adapter — Compatibilidad ESM
Sección titulada «🔌 Express Adapter — Compatibilidad ESM»Reescritura completa:
- Carga Express de forma lazy usando
Function('return require')() - Acepta instancia de Express pre-creada como segundo argumento
- Manejo correcto de body raw vs. parseado
- Delega errores al pipeline de Express via
next(err)
🐛 Bug Fixes
Sección titulada «🐛 Bug Fixes»ZodErrorcross-module: añadido fallbackerror.name === 'ZodError'para garantizar 422 en todos los casos- Rutas GET marcadas públicas retornaban 401: corregido con
app.on(['POST', 'PUT', 'DELETE'], ...) - Cache collision en MetadataCompiler: solucionado con IDs únicos por función vía
WeakMap @Cache/@UseMiddlewareperdían metadatos: solucionado actualizando el registro en cada decorador
📋 Mensajes de validación mejorados
Sección titulada «📋 Mensajes de validación mejorados»La respuesta 422 ahora incluye: field (formato items[0].price), received, expected, minimum/maximum
💥 Breaking Changes
Sección titulada «💥 Breaking Changes»Ninguno — todos los cambios son retrocompatibles.
[0.3.3] - 2025-10-31
Sección titulada «[0.3.3] - 2025-10-31»🐛 Bug Fixes Críticos
Sección titulada «🐛 Bug Fixes Críticos»- Serialización de Respuestas JSON: Corregido bug crítico de serialización
- Resolución de Versión CLI: CLI ahora obtiene correctamente la última versión de npm
- Compilación de la Aplicación: Corregida la falta de
await app.compile()en plantillas generadas
🔧 Mejoras CLI
Sección titulada «🔧 Mejoras CLI»- Obtención de versión corregida desde el registro npm
- Todas las plantillas incluyen
await app.compile()
[0.3.2] - 2025-10-31
Sección titulada «[0.3.2] - 2025-10-31»Lanzamiento de parches con correcciones de build internas. Sin cambios funcionales.
[0.3.1] - 2025-10-31
Sección titulada «[0.3.1] - 2025-10-31»🛠️ Mejoras CLI
Sección titulada «🛠️ Mejoras CLI»- Obtención de Última Versión: CLI obtiene automáticamente la última versión desde npm
- Mejor Manejo de Errores: Mensajes más claros y limpieza al fallar la creación de proyectos
- Indicadores de Progreso: Indicadores visuales mejorados durante la creación
- Plantillas: Todas incluyen la llamada obligatoria a
await app.compile()
[0.3.0] - 2025-10-29
Sección titulada «[0.3.0] - 2025-10-29»🚀 Nuevas Características Principales
Sección titulada «🚀 Nuevas Características Principales»Sistema de Caché de Respuestas
Sección titulada «Sistema de Caché de Respuestas»- Caché en Memoria: Caché LRU rápida con limpieza automática
- Caché Redis: Soporte de caché distribuida para despliegues multi-instancia
- Decorador
@Cache(): Caché de respuestas declarativa con configuración flexible de TTL - Decorador
@CacheInvalidate(): Invalidación de caché basada en patrones - Headers
X-Cache: Headers automáticosHIT/MISSen respuestas
Contexto de Petición Mejorado
Sección titulada «Contexto de Petición Mejorado»- IDs de Petición Automáticos: Generación de UUID para cada petición
- Decorador
@RequestId(): Inyecta el ID de petición en métodos del controlador - Decorador
@AbortSignal(): Soporte de cancelación para operaciones largas - Header
X-Request-IDen todas las respuestas
Logging con Pino
Sección titulada «Logging con Pino»- Integración automática del ID de petición en todos los logs
- Loggers hijo con herencia contextual
- Logs JSON estructurados para producción
- Pretty printing para desarrollo
🔧 Nuevo Middleware
Sección titulada «🔧 Nuevo Middleware»createRequestContextMiddleware(),createSimpleRequestIdMiddleware()createCacheMiddleware(),createCacheInvalidationMiddleware()
💥 Breaking Changes
Sección titulada «💥 Breaking Changes»Ninguno — todos los cambios son aditivos y retrocompatibles.
[0.2.6] - 2025-10-15
Sección titulada «[0.2.6] - 2025-10-15»Corregido
Sección titulada «Corregido»- Exportación de Query: Añadida la exportación faltante de
Querydel índice principal - Decoradores de Parámetro:
@Queryde HTTP ahora se exporta correctamente junto a los decoradores de GraphQL
[0.2.5] - 2025-10-15
Sección titulada «[0.2.5] - 2025-10-15»Corregido
Sección titulada «Corregido»- Conflicto de Query en GraphQL: Eliminado el alias conflictivo
Queryde los decoradores GraphQL - Los decoradores GraphQL ahora usan
GQLQuerypara evitar conflictos
Breaking Changes
Sección titulada «Breaking Changes»- Las queries de GraphQL ahora usan
@GQLQueryen lugar de@Query
[0.2.4] - 2025-10-15
Sección titulada «[0.2.4] - 2025-10-15»Corregido
Sección titulada «Corregido»- Decorador
@Query: Ahora maneja parámetros sin esquemas correctamente - Soporta
@Query(),@Query('param')y@Query(Schema)
[0.2.3] - 2025-10-14
Sección titulada «[0.2.3] - 2025-10-14»Corregido
Sección titulada «Corregido»- Exportaciones WebSocket: Corregida la exportación faltante del decorador
@WebSocket
[0.2.2] - 2025-10-14
Sección titulada «[0.2.2] - 2025-10-14»Corregido
Sección titulada «Corregido»- Decoradores GraphQL: Añadidos los alias faltantes
Query,MutationySubscription
[0.2.0] - 2025-10-14
Sección titulada «[0.2.0] - 2025-10-14»🚀 Características Principales Añadidas
Sección titulada «🚀 Características Principales Añadidas»- Sistema de Autenticación Completo: JWT con tokens de acceso/refresco
- Control de Acceso Basado en Roles (RBAC): Roles jerárquicos con sistema de permisos
- Soporte WebSocket en Tiempo Real: Decoradores para WebSocket
- Integración GraphQL: Soporte completo con resolvers y suscripciones
- Herramienta CLI: Scaffolding de proyectos y generación de código
[0.1.0] - 2025-10-12
Sección titulada «[0.1.0] - 2025-10-12»Añadido
Sección titulada «Añadido»- Lanzamiento inicial del framework veloce-ts
- Routing basado en decoradores
- Validación automática con Zod
- Sistema de inyección de dependencias
- Generación automática de documentación OpenAPI
- Soporte para WebSocket y GraphQL
- CLI para scaffolding de proyectos
- Soporte multi-runtime (Bun, Node.js, Deno, Cloudflare Workers)
:::tip Última Versión Recomendamos usar siempre la última versión de Veloce-TS para obtener la mejor experiencia y las últimas funciones. :::
:::note Breaking Changes Los cambios que rompen compatibilidad están claramente marcados en cada lanzamiento. Revisa el registro antes de actualizar a una nueva versión mayor. :::