Buscar/Mantener un SDK de protocolo bidireccional en un monorepo de TypeScript sin romper su API pública

Mantener un SDK de protocolo bidireccional en un monorepo de TypeScript sin romper su API pública

Úsalo al cambiar un SDK tipado, publicado en varios paquetes, donde cliente y servidor envían peticiones. Elige con intención lo que se exporta, añade operaciones en ambos lados con comprobación de capacidades, registra los cambios incompatibles y ejecuta los ejemplos como pruebas.

Para usar estas reglas de IA
## Ver a qué capa va un cambio y si toca la API pública Esta guía rige el trabajo dentro de un espacio de trabajo de TypeScript con múltiples paquetes que publica un SDK de protocolo: un núcleo interno compartido, un paquete cliente, un paquete servidor, un paquete de esquemas independiente y paquetes adaptadores ligeros para frameworks o entornos de ejecución. Se asume comprobación estricta de tipos, módulos ES y scripts de alcance global gestionados por un único gestor de paquetes. Dos propiedades dominan cada decisión. Primero, la superficie publicada es un contrato: todo lo que sea accesible desde el punto de entrada de un paquete es API pública y no puede cambiarse a la ligera. Segundo, el protocolo es bidireccional —ambos extremos pueden originar solicitudes—, de modo que un cambio en un lado suele conllevar una obligación especular en el otro. Antes de editar, identifica a qué capa pertenece el cambio y si afecta a la superficie publicada. Si la afecta, trátalo como un cambio de API y aplica las reglas de superficie y migración que se detallan más abajo, no solo las reglas de estilo. ## Ejecutar una sola prueba al iterar y todas las comprobaciones al terminar El espacio de trabajo expone scripts agregados que cubren todo el repositorio: instalar dependencias, compilar todos los paquetes, ejecutar comprobaciones de linting y formato, corregir automáticamente el linting y el formato, comprobar los tipos de todos los paquetes, ejecutar la batería completa de pruebas y un script combinado que une la comprobación de tipos con el linting. Reduce el ciclo mientras iteras. Usa el indicador de filtro del gestor de paquetes para ejecutar el script de pruebas de un solo paquete, pasa una ruta de archivo para ejecutar un único archivo de pruebas y pasa un filtro por nombre para ejecutar un solo caso de prueba. Existe un script en modo de observación para iteraciones rápidas. Reserva las ejecuciones sobre todo el espacio de trabajo para el momento previo a dar el trabajo por terminado, y ejecuta entonces tanto la comprobación combinada como la batería de pruebas. La comprobación de tipos por sí sola no demuestra el comportamiento en tiempo de ejecución: nunca afirmes que un cambio funciona sin ejecutar las pruebas que lo ejercitan. ## Seguir las reglas de nombres y formato del repositorio - Comprobación estricta de tipos en todo el proyecto; tipos de retorno explícitos en las funciones exportadas. - PascalCase para clases y tipos; camelCase para funciones y variables. - Nombres de archivo en minúsculas y separados por guiones; los archivos de prueba llevan un sufijo de prueba en su extensión. - Importaciones de módulos ES. Sigue la configuración de resolución de módulos del proyecto en cuanto a las extensiones: con resolución al estilo de empaquetador, las importaciones relativas no llevan extensión de archivo. Agrupa las importaciones de forma lógica. - Indentación de dos espacios, punto y coma obligatorio, preferencia por las comillas simples. - Las pruebas viven en el directorio de pruebas propio de cada paquete para que el patrón de inclusión del ejecutor las detecte; usa nombres de prueba descriptivos. - Comentarios de documentación en las API públicas; los comentarios en línea se reservan para la lógica que realmente no resulta evidente. Cuando una regla resulte ambigua, imita el archivo circundante. No reformatees ni refactorices código ajeno dentro de un cambio funcional. ## Tomar los ejemplos de la documentación de archivos que se compilan Los fragmentos escritos a mano dentro de los comentarios de documentación se pudren en silencio porque nada los compila. En su lugar, extrae el código de ejemplo de módulos de ejemplo asociados que se compilan y se someten a comprobación de tipos junto con el resto del código fuente. Un archivo asociado se sitúa junto al módulo al que acompaña, bajo un nombre coincidente. Las regiones con nombre dentro del archivo asociado se referencian desde el bloque de documentación mediante un atributo de origen que apunta al archivo y a la región. Los nombres de región siguen un patrón predecible: el nombre del símbolo exportado más una variante, o el nombre de la clase más el nombre del método más una variante. Si se omite la referencia a la región, se incluye el archivo completo, lo cual funciona con cualquier tipo de archivo. Ejecuta el script de sincronización de fragmentos después de editar los ejemplos para que el contenido actual se copie en los comentarios de documentación y en las páginas markdown, y confirma la salida sincronizada junto con el cambio. ## Exportar lo público por nombre y mantener la raíz del paquete neutral al entorno Separa un barril interno —que exporta todo, está marcado como privado y solo lo consumen los paquetes hermanos dentro del espacio de trabajo— de un módulo de exportación pública curado que expone tipos, clases de error, constantes, guardas y clases base compartidas. Los puntos de entrada de cada paquete reexportan el módulo curado más sus propias exportaciones nombradas explícitamente. Reglas que no son negociables: - Usa exportaciones nombradas explícitas en los puntos de entrada y en el módulo curado. Nunca uses reexportaciones con comodín en esos lugares. - Añadir un símbolo al punto de entrada de un paquete lo convierte en API pública. Hazlo solo de forma deliberada. - Los auxiliares internos permanecen en el barril interno y nunca se añaden al módulo curado ni a un punto de entrada. - Mantén la entrada raíz de cada paquete neutral respecto al entorno de ejecución para que los empaquetadores de navegador y de entornos edge puedan consumirla. Toda exportación cuyo grafo de módulos alcance de forma transitiva componentes integrados específicos de la plataforma, como la creación de procesos o los sockets sin procesar, debe vivir tras una exportación de subruta con nombre y estar cubierta por una prueba que verifique que el barril raíz sigue libre de ellos. - Mantén los tipos de la biblioteca de validación fuera de las superficies publicadas que prometen estar libres de ellos; en su lugar, publica los esquemas sin procesar en tiempo de ejecución desde el paquete de esquemas dedicado, y deja que los paquetes hermanos resuelvan ese paquete como una dependencia externa real en vez de arrastrar copias privadas de los esquemas. ## Construir sobre la capa de esquemas y mantener transportes intercambiables Organiza el SDK en tres capas. 1. Capa de tipos y esquemas: tipos de mensaje del protocolo, esquemas de validación y constantes alineados con la especificación. Esta es la única fuente de verdad; todas las demás capas derivan de ella. 2. Capa de protocolo: una clase base abstracta que gestiona el enrutamiento de mensajes, la correlación entre solicitud y respuesta, la negociación de capacidades y el ciclo de vida del transporte. Ambas implementaciones de extremo la extienden, razón por la cual el comportamiento bidireccional es simétrico por construcción. 3. API de alto nivel: las clases cliente y servidor que exponen métodos tipados, más un envoltorio de servidor de nivel superior que ofrece un registro simplificado de herramientas, recursos y prompts. Los transportes son una capa conectable aparte, tras una única interfaz: un transporte HTTP de streaming recomendado para extremos remotos, un transporte heredado de flujo de eventos que se conserva por compatibilidad hacia atrás y un transporte de entrada y salida estándar para integraciones con procesos lanzados localmente. La validación es conectable con el mismo espíritu, con un validador por defecto más alternativas compatibles con entornos de ejecución restringidos. Los paquetes adaptadores para frameworks y entornos de ejecución son capas de integración ligeras. Nunca deben añadir funcionalidad de protocolo; todo lo que modifique el comportamiento del protocolo pertenece a las capas del núcleo. ## Seguir el flujo de peticiones de salida y de entrada en ambos extremos Salida. Un método de alto nivel delega en el método de solicitud de la clase base, que asigna un identificador de mensaje único, comprueba que el extremo remoto haya declarado la capacidad que el método requiere, registra un manejador de respuesta pendiente, entrega la solicitud serializada al transporte y espera la resolución cuando llega la respuesta correspondiente. Entrada. El transporte expone un mensaje entrante y el enrutamiento de la clase base lo despacha como solicitud, respuesta o notificación. El tratamiento de solicitudes busca un manejador indexado por nombre de método, construye un objeto de contexto, deja que la subclase enriquezca ese contexto con detalles específicos del transporte, invoca el manejador y devuelve el resultado del manejador como respuesta a través del transporte. Forma del contexto. Un identificador de sesión; un grupo de solicitud que contiene el identificador del mensaje, el nombre del método, los metadatos de la solicitud, una señal de cancelación y auxiliares para enviar una solicitud o notificación relacionada de vuelta al extremo remoto; y un grupo de transporte opcional que transporta los datos de autorización validados cuando el transporte los proporciona. El contexto del lado del servidor añade auxiliares de registro de logs, de solicitud de datos al usuario y de muestreo del modelo. La aplicación de capacidades se ejecuta en ambas direcciones y también en el momento de registrar un manejador, de modo que un extremo no pueda registrar un manejador para algo que nunca declaró. ## Añadir una operación de protocolo con comprobación de capacidades en ambos lados 1. Define el esquema de parámetros de la solicitud y el esquema del resultado en la capa de tipos. 2. Añade el indicador de capacidad correspondiente al tipo de capacidades del cliente o del servidor. 3. Implementa el método de envío en el extremo que inicia la operación. 4. Añade la comprobación de capacidad dentro de la verificación de capacidades de ese extremo. 5. Registra el manejador receptor en el extremo opuesto. 6. Añade un envoltorio de alto nivel en la API de servidor simplificada cuando la operación lo justifique. 7. Añade pruebas en ambos lados, y un ejemplo ejecutable cuando la operación esté orientada al usuario. Los pasos dos y cuatro son los que más a menudo se omiten y los más caros de omitir: una operación sin negociación de capacidades falla en tiempo de ejecución frente a extremos que no la implementan, en lugar de fallar limpiamente durante la inicialización. ## Registrar cada cambio incompatible con qué cambió, por qué y cómo migrar Registra cada cambio incompatible en la guía de migración, dentro de la sección del subsistema al que afecta. Busca secciones relacionadas y amplíalas en lugar de añadir nuevas secciones independientes: una guía formada por entradas paralelas y aisladas se vuelve ilegible justo cuando alguien la necesita. Los cambios acotados a un único archivo de notas de versión van allí en su lugar. Cada entrada expone tres cosas: qué cambió, por qué cambió y cómo migrar. Los renombrados mecánicos pertenecen a los archivos de correspondencias de la herramienta de migración automatizada. Enlaza a esas correspondencias en vez de reproducir tablas de renombrado en prosa; las tablas duplicadas se desincronizan de la herramienta que realmente ejecuta el renombrado. Criterio: prefiere el cambio aditivo. Cuando una ruptura sea inevitable, mantén el camino antiguo en funcionamiento tras una marca de obsolescencia durante un ciclo siempre que sea viable, y plantea la ruptura de forma que se manifieste como un error claro en tiempo de compilación y no como un cambio silencioso de comportamiento. ## Ejecutar en CI los ejemplos de cliente y servidor como pruebas Trata los ejemplos como pruebas, no como adorno. Cada ejemplo es su propio paquete del espacio de trabajo y contiene un servidor y un cliente emparejados; el cliente se conecta, verifica los resultados que espera y termina con un estado de fallo si no coinciden. Un ejecutor lanza todos los ejemplos con las combinaciones de transporte y de época del protocolo configuradas, y esa tarea forma parte de la barrera de integración continua aplicada a cada cambio. La infraestructura común de los ejemplos vive en un único paquete compartido cuya exportación raíz se mantiene mínima: análisis de argumentos, auxiliares de verificación y resolución de rutas hermanas. Las dependencias opcionales más pesadas, como un proveedor de autorización de demostración o un almacén de eventos en memoria, se sitúan tras una exportación de subruta para que los ejemplos ajenos no las carguen de forma anticipada. Los ejemplos importan únicamente esta infraestructura y configuran los transportes en línea, de modo que cada ejemplo se lee como uso real y no como un envoltorio sobre una configuración oculta. Las páginas de las guías de documentación reciben acompañantes de fragmentos por página, sincronizados por el mismo paso de sincronización de fragmentos, y los que son ejecutables también se ejecutan en integración continua.