Repository navigation
Releases
SyncPad v1.0.0
Notas colaborativas que se editan entre varias personas y siguen funcionando sin conexión, sobre un CRDT (Yjs).
Demo: https://syncpad-0xyk.onrender.com (Render + Neon, plan gratuito; se duerme a los 15 minutos sin tráfico y la primera visita puede tardar cerca de un minuto).
Qué incluye
- Edición simultánea de una nota con presencia y cursores.
- Modo sin conexión: cada cambio se guarda en IndexedDB y se sincroniza al reconectar, con el estado «Pendiente» / «Guardado».
- Convergencia por Yjs: las ramas divergentes se fusionan sin elegir un ganador; deshacer solo afecta a las ediciones propias.
- Cuentas, workspaces e invitaciones con permisos comprobados en el servidor, también en conexiones WebSocket abiertas.
- Texto con formato acotado (negrita, enlaces y listas); el servidor rechaza cualquier otra marca.
- Registro de updates con
acky snapshots en PostgreSQL; copias conscripts/backup.shyrestore.sh. - Pila completa con
docker compose up --buildy una imagen de un solo origen (deploy/Dockerfile) para el despliegue.
Caso reproducible: un conflicto resuelto
Dos réplicas parten de «el perro corre», se quedan sin conexión y reemplazan la misma palabra: Ana escribe «lobo» y Luis «loro». Al reconectar no gana ninguna: las dos ven el mismo texto y se conservan ambas versiones.
cd backend && npx tsx ../scripts/conflict-demo.mtsCon conexión, las dos réplicas tienen: "el perro corre"
Sin conexión, Ana ve: "el lobo corre"
Sin conexión, Luis ve: "el loro corre"
Tras reconectar, ambas ven: "el loboloro corre"
OK: convergen al mismo texto y se conservan las dos versiones (sin «último gana»).
El script no necesita servidor ni base de datos: usa el mismo documento y el mismo diff de reconexión que el cliente. El mismo caso con ventanas reales está en el paso 6 del guion de demo, y las reglas con sus límites en #38. Yjs garantiza convergencia, no sentido: decidir cuál de las dos palabras queda sería una decisión de producto, y hoy no hay marcas de conflicto.
Verificación
- 146 pruebas en el backend, 36 en
shared/, 161 en el frontend y 2 del entrypoint compilado. - 18 pruebas de integración con PostgreSQL y WebSocket.
- 17 pruebas Playwright con tres clientes y reinicio del servidor. No se ejecutan en CI.
- Humo público sobre la URL real: 10 de 10 el 2026-10-07 (
scripts/smoke-public.mts). - CI:
frontend.ymlybackend.yml, incluida la imagen de despliegue arrancada sin capabilities, como en Render.
Limitaciones conocidas
- Una sola instancia del servidor: las salas viven en memoria; no hay escalado horizontal.
- Con 5 y 10 editores simultáneos se supera el presupuesto de latencia de 250 ms (medido con almacén en memoria, no contra PostgreSQL desplegado).
- Copia local sin cifrar y sin compactación; sin CSP ni cabeceras de seguridad más allá de las del proxy.
- Accesibilidad revisada con el árbol de Playwright, no con lector de pantalla.
- El arranque en frío tras dormirse y las cuotas de RAM y CPU del plan gratuito no se han medido.
- Las capturas del README salen de datos sembrados en local, no de la instancia pública.
- Es un despliegue de exposición: los datos de la demo no son privados ni se garantizan.