Middleware y Events
Amplía Transport, Endpoint, Service y los módulos colaborativos mediante Middleware y Events de propósito general.
Middleware y Events son puntos de extensión generales de Collaboration SDK. La identidad y la autorización se integran con Middleware. Úsalo para validar y restringir antes de una operación; usa Events para registrar cambios o iniciar tareas tras confirmar el estado. Ambos admiten registros, trazabilidad, métricas y actualizaciones de estado derivado.
Dos momentos de extensión
| Middleware | Event | |
|---|---|---|
| Ejecución | Durante una operación | Tras confirmar un cambio de estado |
| Participar o rechazar la operación | Sí | No |
| Ampliar el Context | Sí | Lee el Context y resultado del Event |
| Fallo del receptor | Puede fallar la operación actual | Aislado del resultado de dominio ya determinado |
| Usos habituales | Identidad, autorización, validación, registro, medición de tiempo | Índices derivados, cachés, métricas, notificaciones internas |
Usa Middleware para decidir si una operación puede continuar; Event para observar que un cambio ya ocurrió.
Modelo de ejecución de Middleware
Los Middleware de una Action forman una cadena asíncrona en orden de registro. await next() entra en el siguiente Middleware y la operación del SDK se ejecuta en el punto más interno:
service.use("submitChangeset", async (context, next) => { const startedAt = performance.now(); context.customData.startedAt = startedAt; try { await next(); } finally { metrics.observe("collaboration.submit.duration", performance.now() - startedAt); }});Middleware puede:
- leer la solicitud específica de la Action;
- compartir datos locales de la llamada con
customData; - inspeccionar entradas o consultar sistemas antes de
next(); - registrar duración o resultados tras volver de
next(); - lanzar
CollabErrorpara rechazar una operación colaborativa prevista.
El Middleware HTTP de Transport puede terminar la respuesta sin llamar a next(). Los de Service y Endpoint deben rechazar con un error explícito en vez de omitir next() silenciosamente.
Middleware de Transport
transport.use() registra Middleware HTTP ordinario, que se ejecuta en orden de registro. Llama a await next() para continuar el procesamiento o finaliza la respuesta directamente. transport.useUpgrade() gestiona los protocolos de enlace WebSocket.
Por ejemplo, registra la duración HTTP, autentica y establece un userID de confianza y, después, registra el Collaboration Endpoint:
transport.use(async (context, next) => { const start = performance.now(); context.response.once("finish", () => { recordHttpDuration({ method: context.incomingMessage.method, route: context.route?.path ?? "unmatched", status: context.response.statusCode, durationMs: performance.now() - start, }); }); await next();});transport.use(async (context, next) => { const user = await authenticate(context.incomingMessage); if (!user) { context.response.statusCode = 401; context.response.end("Authentication required"); return; } context.userID = user.userId; await next();});transport.register(collaborationEndpoint);La aplicación proporciona recordHttpDuration y authenticate. Endpoint utiliza el userID establecido por los Middleware anteriores. El Middleware de observación se registra antes de la autenticación, por lo que también se registran los rechazos.
context.route?.path es la plantilla completa de la ruta coincidente, disponible cuando la ejecución llega al Endpoint. Usa el evento finish de la respuesta para medir el tiempo hasta su finalización; los rechazos anteriores a la resolución de la ruta y las rutas desconocidas se registran como unmatched.
Middleware de Endpoint
Participa en operaciones de Session en tiempo real y Presence:
| Action | Momento | Usos habituales |
|---|---|---|
connect | Tras consumir el ticket y crear Session, antes de HELLO | Política de conexión, nombre y avatar del miembro |
joinUnit | Antes de entrar en una Room Unit | Admisión y visibilidad de la Unit |
receivePresence | Tras recibir Presence de una Session unida, antes de difundir | Validación y filtrado de Presence |
sendPresence | Antes de enviar Presence a cada miembro | Filtrado por destinatario |
endpoint.use("receivePresence", async (context, next) => { if (JSON.stringify(context.payload).length > 8_000) { throw new CollabError("INVALID_REQUEST", "Presence payload is too large"); } await next();});session.userID y session.customData de Endpoint proceden de la solicitud HTTP que emitió el Session Ticket. Endpoint crea memberID para la Session WebSocket actual; cambia al reconectar.
Middleware de Service
Participa en el ciclo de vida de los datos colaborativos autoritativos:
| Action | Límite | Usos habituales |
|---|---|---|
readUnitData | Antes de leer instantáneas, bloques o cambios | Lectura, comprobación de inquilino, auditoría |
createUnit | Antes de crear una Unit atómicamente | Tipos, política de creación, reglas de negocio |
deleteUnits | Antes de escribir el lote de eliminación | Política de eliminación y comprobaciones por Unit |
recoverUnits | Antes de escribir el lote de recuperación | Política de recuperación y auditoría |
submitChangeset | Antes de idempotencia y OT del envío lógico | Edición, límite de tamaño total, trazas |
applyChangeset | Antes de aplicar los cambios transformados al estado candidato | Validación por mutación |
commitChangeset | Antes del CAS de revisión y commit del Adapter | Reglas finales de revisión y métricas |
service.use("createUnit", async (context, next) => { await creationPolicy.assertAllowed({ userID: context.userID, unitID: context.request.snapshot.unitID, type: context.request.snapshot.type, }); context.customData.creationPolicyChecked = true; await next();});submitChangeset se ejecuta una vez por envío lógico. La contención de revisiones puede repetir applyChangeset y commitChangeset con otro attempt. Esos Middleware deben ser seguros ante reintentos y no enviar directamente mensajes externos irreversibles ni efectuar cobros de una sola vez.
Alcance de customData
customData es un objeto escribible por la aplicación y local a la llamada, apropiado para:
- ID de inquilino, trazas o registradores de solicitudes;
- consultas ACL reutilizadas en la llamada;
- puntos de inicio de medición o etiquetas de auditoría;
- objetos de negocio compartidos por una cadena Middleware.
HTTP ordinario, llamadas Service y Sessions WebSocket tienen ciclos de vida distintos. customData no se persiste, envía al navegador ni registra automáticamente. Los datos duraderos siguen en el almacenamiento de la aplicación.
Modelo de ejecución de Events
Los Services registran receptores locales al proceso con on(), que devuelve una suscripción liberable:
const subscription = service.on("changesetCommitted", async (event) => { metrics.increment("collaboration.changeset.committed", { unitType: String(event.changeset.type), }); localCache.delete(event.changeset.unitID);});subscription.dispose();Los receptores del mismo Event se ejecutan secuencialmente en orden de registro y se espera su finalización. Sus errores se registran y aíslan del resultado de dominio ya determinado. Por ejemplo, un fallo de changesetCommitted no revierte cambios ya guardados por Adapter.
Events es adecuado para tareas internas, recuperables o no críticas. No comparte un límite transaccional con el commit de la base ni garantiza la entrega a sistemas externos.
Events del Service principal
| Event | Hecho confirmado | Uso habitual |
|---|---|---|
unitCreated | Unit y datos iniciales creados | Caché de catálogo local o índice derivado |
changesetCommitted | Cambios confirmados y revisión avanzada | Difusión, métricas, índices derivados |
unitsDeleted | Adapter confirmó el lote de eliminación | Limpiar estado relacionado del proceso |
unitsRecovered | Adapter confirmó el lote de recuperación | Restaurar estado derivado del proceso |
Endpoint escucha changesetCommitted en el mismo Service y difunde los cambios confirmados a los clientes unidos a la Unit en el proceso actual, o en todos los nodos cuando hay un adaptador pub/sub configurado. También expone memberLeftUnit para salida explícita, desconexión y liberación del Endpoint.
Las extensiones tienen puntos independientes
History, Thread Comment y Worktree no heredan Middleware ni Events del Collaboration Service principal:
- History proporciona Middleware de lectura para
getHistoryList,listHistoryCreatorsygetHistoryChangesets; - Comment proporciona añadir, responder, editar, eliminar, resolver y listar, más
commentCommitted; - Worktree proporciona ciclo de vida y lectura/envío/commit de borradores, más Events de creación, estado, fusión y commit de borrador.
Instala las reglas por separado en cada módulo activo. El Middleware de registro, inquilino o autorización del Service principal no los cubre automáticamente.
Continúa con Identidad y autorización para asociar usuarios y ACL existentes con estos límites generales.
¿Cómo es esta guía?