Ir al contenido

Proyecto independiente de I+D · Colonia

TypeScript y Node

Esquemas en tiempo de ejecución como fuente de los tipos, valores por defecto de validación de los frameworks, AsyncLocalStorage y reglas de dependencias.

No normativo

Versión del Companion
1.0
Corresponde a COADF Core
2.2
Estado
Vigente
Última revisión
Versión del perfil
1.0
Ejemplos de código
Los ejemplos de código llegarán en una revisión posterior.

Los ejemplos de código llegarán en una revisión posterior.

Propiedades de arquitectura tratadas

  • P-1Un esquema en tiempo de ejecución como contrato, el tipo estático derivado de él y, en el patrón de referencia, una factoría como único camino hacia un valor verificado.
  • P-4Contexto de OpenTelemetry a través de AsyncLocalStorage, y la identidad de auditoría como campo obligatorio de cada mensaje y de cada job.
  • P-5La procedencia declarada en el esquema y preservada por el serializador de respuestas.
  • P-6Reglas de dependencias y comprobaciones de esquemas en la integración continua, cada una con un fallo introducido a propósito.
  • P-7Puertos en el paquete de dominio; un paquete adaptador por sistema externo.
  • P-8Una interfaz de políticas sobre un motor, por HTTP o compilado a WebAssembly, con la revisión de la política en cada decisión.
  • P-2, P-3Solo con la profundidad que publica COADF.

Intención de arquitectura

TypeScript da a la frontera un vocabulario y nada de su aplicación. Las anotaciones de tipo nunca cambian el comportamiento en tiempo de ejecución de un programa: describen lo que un valor debería ser y desaparecen antes de que se ejecute, mientras que los datos que llegan de un modelo, de una cola o de una petición tienen la forma que tengan. Los tipados estándar declaran el resultado de JSON.parse como any, que el compilador deja tratar luego como cualquier cosa.

Por eso, en este ecosistema la frontera es el esquema en tiempo de ejecución, y el tipo estático se deriva del esquema, nunca al revés. Todo lo demás en este perfil se sigue de esa inversión. Zod, Ajv, Fastify y NestJS son ejemplos: COADF no exige ninguno de ellos, y cualquier validador en tiempo de ejecución que rechace lo que el contrato no declara mantiene la propiedad.

Correspondencia tecnológica

TypeScript y Node: Correspondencia tecnológica
Propiedad de arquitecturaTypeScript y Node
Tipo de fronteraUn esquema en tiempo de ejecución (Zod, o JSON Schema con Ajv) como fuente, con el tipo estático inferido de él. El sistema de tipos de TypeScript es estructural, así que un valor verificado que no debe confundirse con una propuesta necesita una marca (brand), y solo una factoría la aplica.
Validación en tiempo de ejecuciónparse o safeParse en cada lado receptor. parse de Zod lanza una excepción ante datos no válidos y safeParse devuelve en su lugar un resultado.
Claves desconocidasUna elección explícita por esquema. En Zod 4, z.object() elimina las claves desconocidas y z.strictObject() las rechaza.
Validación del frameworkEsquemas de ruta de Fastify, validados con Ajv y serializados con fast-json-stringify; o el ValidationPipe de NestJS con whitelist y forbidNonWhitelisted.
Regla de arquitecturaReglas de dependencias comprobadas en la CI: dependency-cruiser, reglas de fronteras de ESLint o project references de TypeScript que convierten una importación ilegal en un error de compilación.
Contexto de ejecuciónOpenTelemetry JS, cuyo gestor de contexto para Node está basado en AsyncLocalStorage.
Traza de auditoríaEl mismo esquema de PostgreSQL a través de un driver; entradas escritas en la transacción de negocio.
Aislamiento de normasInterfaces de puerto en el paquete de dominio; cada cliente de proveedor confinado a un paquete adaptador del workspace.
PolíticaOpen Policy Agent por HTTP, o Rego compilado a WebAssembly y evaluado dentro del proceso mediante el SDK de JavaScript de OPA; la revisión de la política en cada decisión.

Patrón de referencia

Los ejemplos de código llegarán en una revisión posterior. Se aplazan hasta que los haya leído una persona revisora que trabaje en este ecosistema, en lugar de escribirlos para que los cuatro perfiles se parezcan. La estructura que sigue es el patrón de referencia que implementarán esos ejemplos.

Paquetes

  • schemas contiene los esquemas en tiempo de ejecución de cada contrato: la propuesta con su procedencia, la entrada de auditoría con los campos publicados, la decisión de política con su revisión. Todos los demás paquetes importan de aquí sus tipos, inferidos de los esquemas.
  • inference llama al modelo y devuelve el resultado de analizar la respuesta con el esquema de propuesta. No puede importar records ni audit, y una regla de dependencias en la CI lo afirma.
  • records exporta una factoría que recibe una propuesta y la verificación de una persona con nombre y devuelve un valor verificado con marca. El tipo de la marca no puede construirse en otro lugar sin un cast, y una regla de lint prohíbe ese cast fuera de este paquete.
  • audit, classification con su adaptador del proveedor, y policy siguen la misma forma que en los demás perfiles.

Fronteras en tiempo de ejecución

Cada lado receptor analiza los datos: la ruta HTTP, el consumidor de mensajes, el ejecutor de jobs y la factoría del dominio. Un esquema de ruta en Fastify o un pipe en NestJS cubre solo el punto de entrada web.

El SDK de OpenTelemetry se carga antes que los módulos propios de la aplicación. Una aplicación con módulos ES necesita el loader hook de OpenTelemetry para la instrumentación automática, además de precargar el SDK.

Modos de fallo

  1. Una anotación de tipo tomada por validación

    Hacer un cast del resultado de JSON.parse al tipo de propuesta compila, y no comprueba nada. El cast es la frontera más habitual en el código TypeScript, y no es una frontera.

  2. Eliminar tomado por rechazar

    Un objeto de Zod por defecto elimina las claves desconocidas. Una respuesta que afirma estar verificada no se rechaza; la afirmación se quita en silencio, y con ella se pierde el hecho de que el modelo afirmó de más. Donde la propia afirmación es evidencia, hay que usar un objeto estricto y registrar el rechazo.

  3. Conversión por defecto en Fastify

    Las opciones de Ajv por defecto de Fastify incluyen coerceTypes, useDefaults y removeAdditional. Un número enviado como texto llega como número, y un valor ausente puede llegar como valor por defecto. Hay que configurar el validador explícitamente para las rutas de frontera.

  4. Procedencia descartada por el serializador de respuestas

    Con un esquema de respuesta, las propiedades que el esquema no enumera se omiten, salvo que admita propiedades adicionales. Un esquema de respuesta escrito para la pantalla, sin la procedencia, la elimina de todas las respuestas.

  5. Un pipe de NestJS que lo deja pasar todo

  6. Contexto perdido a través del trabajo asíncrono

    AsyncLocalStorage conserva datos durante la vida de una petición u otra duración asíncrona, y sigue las continuaciones que Node puede ver. El trabajo que vuelve a entrar desde un callback de un pool o desde la cola propia de una biblioteca puede llegar sin él; la identidad de auditoría tiene que ir en el mensaje, no recuperarse del contexto.

  7. Instrumentación automática ausente sin aviso

    Un servicio con módulos ES arrancado sin el loader hook funciona con normalidad, y las bibliotecas que el hook habría instrumentado no producen spans. Nada falla; la traza simplemente es más corta que la ejecución.

  8. Una marca falsificada con un cast

    Un cast al tipo verificado compila en cualquier sitio. La marca más la factoría es una convención que el compilador no puede imponer por sí solo; lo que la hace cumplirse son la regla de lint y la revisión de código.

  9. Dos versiones de los esquemas compartidos

    Productor y consumidor dependen de versiones distintas del paquete schemas, y cada uno valida correctamente contra un contrato distinto. Lo detectan las pruebas de contrato entre las versiones realmente desplegadas; las pruebas unitarias no.

Verificación

  • Prueba unitaria

    Pasa cuando: El esquema de propuesta rechaza las respuestas mal formadas: clave desconocida, tipo erróneo, procedencia ausente, atributo inesperado.

    Prueba de dientes: Sustituir el objeto estricto por uno por defecto en una rama desechable: la prueba de la clave desconocida falla.

  • Prueba unitaria

    Pasa cuando: Una prueba a nivel de tipos afirma que una propuesta no es asignable a un valor verificado.

    Prueba de dientes: Quitar la marca: la prueba a nivel de tipos deja de compilar.

  • Prueba de arquitectura

    Pasa cuando: Las reglas de dependencias pasan: ninguna importación de inference hacia records o audit, ningún cliente de proveedor fuera de su adaptador.

    Prueba de dientes: Introducir a propósito la importación prohibida: la CI falla.

  • Prueba de contrato

    Pasa cuando: La respuesta de cada ruta que sirve una propuesta contiene los campos de procedencia.

    Prueba de dientes: Quitar una propiedad de procedencia del esquema de respuesta: la prueba falla, porque el serializador la descartó.

  • Prueba de integración

    Pasa cuando: Con un exportador de spans en memoria, el trabajo asíncrono aparece en la traza de la petición, y las entradas de auditoría persistidas llevan una sola identidad.

  • Prueba de integración

    Pasa cuando: Las decisiones de política llevan la revisión del material de políticas que las produjo; las respuestas sin ella se rechazan.

Realizaciones alternativas

  • Otras bibliotecas de esquemas (Valibot, TypeBox con Ajv, io-ts). La propiedad es un esquema en tiempo de ejecución que es la fuente del tipo; la biblioteca es una elección.
  • Transportes con el esquema primero, como GraphQL o Protocol Buffers, donde el contrato es un artefacto separado a partir del cual se generan ambos lados.
  • Un motor de políticas dentro del proceso mediante WebAssembly, en lugar de por HTTP, para eliminar el salto de red; la exigencia de registrar la revisión de la política no cambia.

Compromisos

  • Poner el esquema primero desplaza la fuente de verdad fuera de las declaraciones de tipos, algo que a algunos equipos les resulta poco natural; poner el tipo primero deja el tiempo de ejecución sin protección.
  • Los validadores compilados son rápidos y traen valores por defecto que convierten y rellenan; la configuración más rápida no es la más estricta.
  • Los objetos estrictos rechazan más de lo que produce un modelo, y cada rechazo exige una decisión sobre qué registrar.
  • Un workspace de paquetes pequeños hace expresables las reglas de dependencias, y cuesta maquinaria de build y de publicación de versiones.

Limitaciones

  • En Companion 1.0 no se publica código para este perfil, y no se ejecutó ningún ejemplo. El comportamiento descrito procede de la documentación oficial vigente, comprobada el 11 de septiembre de 2026, para Zod 4, Fastify 5, NestJS, Node.js 24 y OpenTelemetry JS 2.
  • Nada de lo que aquí figura muestra cómo se representa la confianza, cuándo se requiere revisión ni cómo se organiza la revisión; COADF no publica esas partes de P-2 y P-3.

Lo que este perfil no establece

Seguir este perfil no establece cumplimiento normativo, certificación ni evaluación de la conformidad, y COADF no exige ni TypeScript ni Node.

Entorno de referencia probado

No se ejecutó ningún ejemplo para este perfil.

Fuentes

COADF Engineering Companion 1.0 · no normativo · corresponde a COADF Core 2.2

Derechos de publicación reservados. Por ahora no se concede ninguna licencia pública para el COADF Engineering Companion 1.0 ni para sus ejemplos de referencia.

Estado de propiedad intelectual y de publicación