diff --git a/docusaurus.config.js b/docusaurus.config.js
index 66d6a955..9ad4c3d0 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -304,6 +304,6 @@ module.exports = {
],
i18n: {
defaultLocale: "en",
- locales: ["en", "fr", "pt-br", "zh-CN"],
+ locales: ["en", "es", "fr", "pt-br", "zh-CN"],
}
};
diff --git a/i18n/es/code.json b/i18n/es/code.json
new file mode 100644
index 00000000..000303ee
--- /dev/null
+++ b/i18n/es/code.json
@@ -0,0 +1,446 @@
+{
+ "theme.NotFound.title": {
+ "message": "Página no encontrada",
+ "description": "El título de la página 404"
+ },
+ "theme.NotFound.p1": {
+ "message": "No pudimos encontrar lo que estabas buscando.",
+ "description": "El primer párrafo de la página 404"
+ },
+ "theme.NotFound.p2": {
+ "message": "Por favor, contacta al propietario del sitio que te enlazó a la URL original y hazles saber que su enlace está roto.",
+ "description": "El segundo párrafo de la página 404"
+ },
+ "theme.AnnouncementBar.closeButtonAriaLabel": {
+ "message": "Cerrar",
+ "description": "La etiqueta ARIA para el botón de cerrar de la barra de anuncios"
+ },
+ "theme.blog.archive.title": {
+ "message": "Archivo",
+ "description": "El título de la página y hero del archivo del blog"
+ },
+ "theme.blog.archive.description": {
+ "message": "Archivo",
+ "description": "La descripción de la página y hero del archivo del blog"
+ },
+ "theme.blog.paginator.navAriaLabel": {
+ "message": "Navegación de la lista de artículos del blog",
+ "description": "La etiqueta ARIA para la paginación del blog"
+ },
+ "theme.blog.paginator.newerEntries": {
+ "message": "Entradas más recientes",
+ "description": "La etiqueta usada para navegar a la página de publicaciones más recientes del blog (página anterior)"
+ },
+ "theme.blog.paginator.olderEntries": {
+ "message": "Entradas anteriores",
+ "description": "La etiqueta usada para navegar a la página de publicaciones anteriores del blog (página siguiente)"
+ },
+ "theme.blog.post.readingTime.plurals": {
+ "message": "Un minuto de lectura|{readingTime} minutos de lectura",
+ "description": "Etiqueta pluralizada para \"{readingTime} min read\". Usa tantas formas plurales (separadas por \"|\") como tu idioma soporte (ver https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+ },
+ "theme.blog.post.readMore": {
+ "message": "Leer más",
+ "description": "La etiqueta usada en extractos de publicaciones del blog para enlazar a publicaciones completas"
+ },
+ "theme.blog.post.readMoreLabel": {
+ "message": "Leer más sobre {title}",
+ "description": "La etiqueta ARIA para el enlace a publicaciones completas del blog desde extractos"
+ },
+ "theme.blog.post.paginator.navAriaLabel": {
+ "message": "Navegación de artículos del blog",
+ "description": "La etiqueta ARIA para la paginación de publicaciones del blog"
+ },
+ "theme.blog.post.paginator.newerPost": {
+ "message": "Artículo más reciente",
+ "description": "La etiqueta del botón de publicación del blog para navegar a la publicación más reciente/anterior"
+ },
+ "theme.blog.post.paginator.olderPost": {
+ "message": "Artículo anterior",
+ "description": "La etiqueta del botón de publicación del blog para navegar a la publicación más antigua/siguiente"
+ },
+ "theme.blog.sidebar.navAriaLabel": {
+ "message": "Navegación de publicaciones recientes del blog",
+ "description": "La etiqueta ARIA para publicaciones recientes en la barra lateral del blog"
+ },
+ "theme.blog.post.plurals": {
+ "message": "Un artículo|{count} artículos",
+ "description": "Etiqueta pluralizada para \"{count} posts\". Usa tantas formas plurales (separadas por \"|\") como tu idioma soporte (ver https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+ },
+ "theme.blog.tagTitle": {
+ "message": "{nPosts} etiquetados con «{tagName}»",
+ "description": "El título de la página para una etiqueta del blog"
+ },
+ "theme.tags.tagsPageLink": {
+ "message": "Ver todas las etiquetas",
+ "description": "La etiqueta del enlace que apunta a la página de lista de etiquetas"
+ },
+ "theme.CodeBlock.copyButtonAriaLabel": {
+ "message": "Copiar código al portapapeles",
+ "description": "La etiqueta ARIA para el botón de copiar bloques de código"
+ },
+ "theme.CodeBlock.copied": {
+ "message": "Copiado",
+ "description": "La etiqueta del botón copiado en bloques de código"
+ },
+ "theme.CodeBlock.copy": {
+ "message": "Copiar",
+ "description": "La etiqueta del botón copiar en bloques de código"
+ },
+ "theme.CodeBlock.wordWrapToggle": {
+ "message": "Alternar ajuste de línea",
+ "description": "El atributo title para el botón de alternar ajuste de línea en bloques de código"
+ },
+ "theme.docs.sidebar.expandButtonTitle": {
+ "message": "Expandir barra lateral",
+ "description": "La etiqueta ARIA y atributo title para el botón de expandir de la barra lateral de documentos"
+ },
+ "theme.docs.sidebar.expandButtonAriaLabel": {
+ "message": "Expandir barra lateral",
+ "description": "La etiqueta ARIA y atributo title para el botón de expandir de la barra lateral de documentos"
+ },
+ "theme.docs.paginator.navAriaLabel": {
+ "message": "Navegación de documentos",
+ "description": "La etiqueta ARIA para la paginación de documentos"
+ },
+ "theme.docs.paginator.previous": {
+ "message": "Anterior",
+ "description": "La etiqueta usada para navegar al documento anterior"
+ },
+ "theme.docs.paginator.next": {
+ "message": "Siguiente",
+ "description": "La etiqueta usada para navegar al siguiente documento"
+ },
+ "theme.docs.sidebar.collapseButtonTitle": {
+ "message": "Contraer barra lateral",
+ "description": "El atributo title para el botón de contraer de la barra lateral de documentos"
+ },
+ "theme.docs.sidebar.collapseButtonAriaLabel": {
+ "message": "Contraer barra lateral",
+ "description": "La etiqueta ARIA para el botón de contraer de la barra lateral de documentos"
+ },
+ "theme.docs.sidebar.navAriaLabel": {
+ "message": "Barra lateral de documentación",
+ "description": "La etiqueta ARIA para la navegación de la barra lateral"
+ },
+ "theme.docs.sidebar.closeSidebarButtonAriaLabel": {
+ "message": "Cerrar barra de navegación",
+ "description": "La etiqueta ARIA para el botón de cerrar de la barra lateral móvil"
+ },
+ "theme.docs.sidebar.toggleSidebarButtonAriaLabel": {
+ "message": "Alternar barra de navegación",
+ "description": "La etiqueta ARIA para el botón de menú hamburguesa de la navegación móvil"
+ },
+ "theme.docs.tagDocListPageTitle.nDocsTagged": {
+ "message": "Un documento etiquetado|{count} documentos etiquetados",
+ "description": "Etiqueta pluralizada para \"{count} docs tagged\". Usa tantas formas plurales (separadas por \"|\") como tu idioma soporte (ver https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+ },
+ "theme.docs.tagDocListPageTitle": {
+ "message": "{nDocsTagged} con «{tagName}»",
+ "description": "El título de la página para una etiqueta de documentos"
+ },
+ "theme.docs.breadcrumbs.navAriaLabel": {
+ "message": "Migas de pan",
+ "description": "La etiqueta ARIA para las migas de pan"
+ },
+ "theme.docs.breadcrumbs.home": {
+ "message": "Página de inicio",
+ "description": "La etiqueta ARIA para la página de inicio en las migas de pan"
+ },
+ "theme.docs.DocCard.categoryDescription": {
+ "message": "{count} elementos",
+ "description": "La descripción predeterminada para una tarjeta de categoría en el índice generado sobre cuántos elementos incluye esta categoría"
+ },
+ "theme.docs.versionBadge.label": {
+ "message": "Versión: {versionLabel}"
+ },
+ "theme.common.editThisPage": {
+ "message": "Editar esta página",
+ "description": "La etiqueta del enlace para editar la página actual"
+ },
+ "theme.docs.versions.unreleasedVersionLabel": {
+ "message": "Esta es la documentación sin publicar de {siteTitle} versión {versionLabel}.",
+ "description": "La etiqueta usada para indicar al usuario que está navegando una versión de documentación sin publicar"
+ },
+ "theme.docs.versions.unmaintainedVersionLabel": {
+ "message": "Esta es la documentación de {siteTitle} {versionLabel}, que ya no se mantiene activamente.",
+ "description": "La etiqueta usada para indicar al usuario que está navegando una versión de documentación sin mantenimiento"
+ },
+ "theme.docs.versions.latestVersionSuggestionLabel": {
+ "message": "Para documentación actualizada, consulta la {latestVersionLink} ({versionLabel}).",
+ "description": "La etiqueta usada para indicar al usuario que consulte la última versión"
+ },
+ "theme.docs.versions.latestVersionLinkLabel": {
+ "message": "última versión",
+ "description": "La etiqueta usada para el enlace de sugerencia de última versión"
+ },
+ "theme.common.headingLinkTitle": {
+ "message": "Enlace directo a {heading}",
+ "description": "Título para el enlace al encabezado"
+ },
+ "theme.lastUpdated.atDate": {
+ "message": " el {date}",
+ "description": "Las palabras usadas para describir en qué fecha una página fue actualizada por última vez"
+ },
+ "theme.lastUpdated.byUser": {
+ "message": " por {user}",
+ "description": "Las palabras usadas para describir por quién la página fue actualizada por última vez"
+ },
+ "theme.lastUpdated.lastUpdatedAtBy": {
+ "message": "Última actualización{atDate}{byUser}",
+ "description": "La oración usada para mostrar cuándo una página fue actualizada por última vez, y por quién"
+ },
+ "theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel": {
+ "message": "← Volver al menú principal",
+ "description": "La etiqueta del botón atrás para volver al menú principal, dentro del menú secundario de la barra lateral móvil de la barra de navegación (usado notablemente para mostrar la barra lateral de documentos)"
+ },
+ "theme.navbar.mobileVersionsDropdown.label": {
+ "message": "Versiones",
+ "description": "La etiqueta para el desplegable de versiones de la barra de navegación en vista móvil"
+ },
+ "theme.navbar.mobileLanguageDropdown.label": {
+ "message": "Idiomas",
+ "description": "La etiqueta para el selector de idioma móvil desplegable"
+ },
+ "theme.NavBar.navAriaLabel": {
+ "message": "Principal",
+ "description": "La etiqueta ARIA para la navegación principal"
+ },
+ "theme.common.skipToMainContent": {
+ "message": "Ir al contenido principal",
+ "description": "La etiqueta de saltar al contenido usada para accesibilidad, permitiendo navegar rápidamente al contenido principal con la navegación de tabulador/enter del teclado"
+ },
+ "theme.TOCCollapsible.toggleButtonLabel": {
+ "message": "En esta página",
+ "description": "La etiqueta usada por el botón en el componente de TOC contraíble"
+ },
+ "theme.tags.tagsListLabel": {
+ "message": "Etiquetas:",
+ "description": "La etiqueta junto a una lista de etiquetas"
+ },
+ "theme.tags.tagsPageTitle": {
+ "message": "Etiquetas",
+ "description": "El título de la página de lista de etiquetas"
+ },
+ "theme.admonition.note": {
+ "message": "nota",
+ "description": "La etiqueta predeterminada usada para la admonición Note (:::note)"
+ },
+ "theme.admonition.tip": {
+ "message": "consejo",
+ "description": "La etiqueta predeterminada usada para la admonición Tip (:::tip)"
+ },
+ "theme.admonition.danger": {
+ "message": "peligro",
+ "description": "La etiqueta predeterminada usada para la admonición Danger (:::danger)"
+ },
+ "theme.admonition.info": {
+ "message": "información",
+ "description": "La etiqueta predeterminada usada para la admonición Info (:::info)"
+ },
+ "theme.admonition.caution": {
+ "message": "precaución",
+ "description": "La etiqueta predeterminada usada para la admonición Caution (:::caution)"
+ },
+ "theme.BackToTopButton.buttonAriaLabel": {
+ "message": "Volver arriba",
+ "description": "La etiqueta ARIA para el botón de volver arriba"
+ },
+ "theme.colorToggle.ariaLabel": {
+ "message": "Cambiar entre modo oscuro y claro (actualmente {mode})",
+ "description": "La etiqueta ARIA para el alternador de modo de color de la barra de navegación"
+ },
+ "theme.colorToggle.ariaLabel.mode.dark": {
+ "message": "modo oscuro",
+ "description": "El nombre para el modo de color oscuro"
+ },
+ "theme.colorToggle.ariaLabel.mode.light": {
+ "message": "modo claro",
+ "description": "El nombre para el modo de color claro"
+ },
+ "theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel": {
+ "message": "Alternar la categoría contraíble de la barra lateral '{label}'",
+ "description": "La etiqueta ARIA para alternar la categoría contraíble de la barra lateral"
+ },
+ "theme.SearchBar.seeAll": {
+ "message": "Ver los {count} resultados"
+ },
+ "theme.SearchPage.documentsFound.plurals": {
+ "message": "Un documento encontrado|{count} documentos encontrados",
+ "description": "Etiqueta pluralizada para \"{count} documents found\". Usa tantas formas plurales (separadas por \"|\") como tu idioma soporte (ver https://www.unicode.org/cldr/cldr-aux/charts/34/supplemental/language_plural_rules.html)"
+ },
+ "theme.SearchPage.existingResultsTitle": {
+ "message": "Resultados de búsqueda para «{query}»",
+ "description": "El título de la página de búsqueda para consulta no vacía"
+ },
+ "theme.SearchPage.emptyResultsTitle": {
+ "message": "Buscar en la documentación",
+ "description": "El título de la página de búsqueda para consulta vacía"
+ },
+ "theme.SearchPage.inputPlaceholder": {
+ "message": "Escribe tu búsqueda aquí",
+ "description": "El marcador de posición para la entrada de la página de búsqueda"
+ },
+ "theme.SearchPage.inputLabel": {
+ "message": "Buscar",
+ "description": "La etiqueta ARIA para la entrada de la página de búsqueda"
+ },
+ "theme.SearchPage.algoliaLabel": {
+ "message": "Búsqueda con Algolia",
+ "description": "La etiqueta ARIA para la mención de Algolia"
+ },
+ "theme.SearchPage.noResultsText": {
+ "message": "No se encontraron resultados",
+ "description": "El párrafo para resultado de búsqueda vacío"
+ },
+ "theme.SearchPage.fetchingNewResults": {
+ "message": "Obteniendo nuevos resultados...",
+ "description": "El párrafo para obtener nuevos resultados de búsqueda"
+ },
+ "theme.SearchBar.label": {
+ "message": "Buscar",
+ "description": "La etiqueta ARIA y marcador de posición para el botón de búsqueda"
+ },
+ "theme.SearchModal.searchBox.resetButtonTitle": {
+ "message": "Limpiar la búsqueda",
+ "description": "La etiqueta y etiqueta ARIA para el botón de resetear el cuadro de búsqueda"
+ },
+ "theme.SearchModal.searchBox.cancelButtonText": {
+ "message": "Cancelar",
+ "description": "La etiqueta y etiqueta ARIA para el botón de cancelar el cuadro de búsqueda"
+ },
+ "theme.SearchModal.startScreen.recentSearchesTitle": {
+ "message": "Reciente",
+ "description": "El título para búsquedas recientes"
+ },
+ "theme.SearchModal.startScreen.noRecentSearchesText": {
+ "message": "No hay búsquedas recientes",
+ "description": "El texto cuando no hay búsquedas recientes"
+ },
+ "theme.SearchModal.startScreen.saveRecentSearchButtonTitle": {
+ "message": "Guardar esta búsqueda",
+ "description": "La etiqueta para el botón de guardar búsqueda reciente"
+ },
+ "theme.SearchModal.startScreen.removeRecentSearchButtonTitle": {
+ "message": "Eliminar esta búsqueda del historial",
+ "description": "La etiqueta para el botón de eliminar búsqueda reciente"
+ },
+ "theme.SearchModal.startScreen.favoriteSearchesTitle": {
+ "message": "Favoritos",
+ "description": "El título para búsquedas favoritas"
+ },
+ "theme.SearchModal.startScreen.removeFavoriteSearchButtonTitle": {
+ "message": "Eliminar esta búsqueda de favoritos",
+ "description": "La etiqueta para el botón de eliminar búsqueda favorita"
+ },
+ "theme.SearchModal.errorScreen.titleText": {
+ "message": "No se pudieron obtener resultados",
+ "description": "El título para la pantalla de error del modal de búsqueda"
+ },
+ "theme.SearchModal.errorScreen.helpText": {
+ "message": "Es posible que desees verificar tu conexión de red.",
+ "description": "El texto de ayuda para la pantalla de error del modal de búsqueda"
+ },
+ "theme.SearchModal.footer.selectText": {
+ "message": "para seleccionar",
+ "description": "El texto explicativo de la acción para la tecla enter"
+ },
+ "theme.SearchModal.footer.selectKeyAriaLabel": {
+ "message": "Tecla Enter",
+ "description": "La etiqueta ARIA para el botón de la tecla Enter que realiza la selección"
+ },
+ "theme.SearchModal.footer.navigateText": {
+ "message": "para navegar",
+ "description": "El texto explicativo de la acción para las teclas de flecha arriba y flecha abajo"
+ },
+ "theme.SearchModal.footer.navigateUpKeyAriaLabel": {
+ "message": "Flecha arriba",
+ "description": "La etiqueta ARIA para el botón de la tecla flecha arriba que realiza la navegación"
+ },
+ "theme.SearchModal.footer.navigateDownKeyAriaLabel": {
+ "message": "Flecha abajo",
+ "description": "La etiqueta ARIA para el botón de la tecla flecha abajo que realiza la navegación"
+ },
+ "theme.SearchModal.footer.closeText": {
+ "message": "para cerrar",
+ "description": "El texto explicativo de la acción para la tecla Escape"
+ },
+ "theme.SearchModal.footer.closeKeyAriaLabel": {
+ "message": "Tecla Escape",
+ "description": "La etiqueta ARIA para el botón de la tecla Escape que cierra el modal"
+ },
+ "theme.SearchModal.footer.searchByText": {
+ "message": "Búsqueda con",
+ "description": "El texto que explica que la búsqueda se realiza con Algolia"
+ },
+ "theme.SearchModal.noResultsScreen.noResultsText": {
+ "message": "Sin resultados para",
+ "description": "El texto que explica que no hay resultados para la siguiente búsqueda"
+ },
+ "theme.SearchModal.noResultsScreen.suggestedQueryText": {
+ "message": "Intenta buscar",
+ "description": "El texto para la consulta sugerida cuando no se encuentran resultados para la siguiente búsqueda"
+ },
+ "theme.SearchModal.noResultsScreen.reportMissingResultsText": {
+ "message": "¿Crees que esta búsqueda debería devolver resultados?",
+ "description": "El texto para la pregunta donde el usuario piensa que faltan resultados"
+ },
+ "theme.SearchModal.noResultsScreen.reportMissingResultsLinkText": {
+ "message": "Háznoslo saber.",
+ "description": "El texto para el enlace para reportar resultados faltantes"
+ },
+ "theme.SearchModal.placeholder": {
+ "message": "Buscar en la documentación",
+ "description": "El marcador de posición de la entrada del modal emergente de DocSearch"
+ },
+ "theme.ErrorPageContent.title": {
+ "message": "Esta página falló.",
+ "description": "El título de la página de respaldo cuando la página falló"
+ },
+ "theme.ErrorPageContent.tryAgain": {
+ "message": "Intentar de nuevo",
+ "description": "La etiqueta del botón para intentar de nuevo el renderizado cuando el límite de error de React captura un error"
+ },
+ "Latest blog posts": {
+ "message": "Últimas publicaciones del blog"
+ },
+ "Basic example": {
+ "message": "Ejemplo básico"
+ },
+ "Run this example on": {
+ "message": "Ejecuta este ejemplo en"
+ },
+ "Performant": {
+ "message": "Alto rendimiento"
+ },
+ "In most cases, the connection will be established with WebSocket, providing a low-overhead communication channel between the server and the client.": {
+ "message": "En la mayoría de los casos, la conexión se establecerá con WebSocket, proporcionando un canal de comunicación de baja sobrecarga entre el servidor y el cliente."
+ },
+ "Reliable": {
+ "message": "Fiable"
+ },
+ "Rest assured! In case the WebSocket connection is not possible, it will fall back to HTTP long-polling. And if the connection is lost, the client will automatically try to reconnect.": {
+ "message": "¡Quédate tranquilo! En caso de que la conexión WebSocket no sea posible, recurrirá a HTTP long-polling. Y si se pierde la conexión, el cliente intentará reconectarse automáticamente."
+ },
+ "Scalable": {
+ "message": "Escalable"
+ },
+ "Scale to multiple servers and send events to all connected clients with ease.": {
+ "message": "Escala a múltiples servidores y envía eventos a todos los clientes conectados con facilidad."
+ },
+ "Bidirectional and low-latency communication for every platform": {
+ "message": "Comunicación bidireccional y de baja latencia para todas las plataformas"
+ },
+ "Get started": {
+ "message": "Comenzar"
+ },
+ "Documentation": {
+ "message": "Documentación"
+ },
+ "Our sponsors": {
+ "message": "Nuestros patrocinadores"
+ },
+ "Become a sponsor": {
+ "message": "Conviértete en patrocinador"
+ }
+}
diff --git a/i18n/es/docusaurus-plugin-content-blog/options.json b/i18n/es/docusaurus-plugin-content-blog/options.json
new file mode 100644
index 00000000..fdab0409
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-blog/options.json
@@ -0,0 +1,14 @@
+{
+ "title": {
+ "message": "Blog",
+ "description": "El título para el blog usado en SEO"
+ },
+ "description": {
+ "message": "Blog",
+ "description": "La descripción para el blog usada en SEO"
+ },
+ "sidebar.title": {
+ "message": "Publicaciones recientes",
+ "description": "La etiqueta para la barra lateral izquierda"
+ }
+}
diff --git a/i18n/es/docusaurus-plugin-content-docs/current.json b/i18n/es/docusaurus-plugin-content-docs/current.json
new file mode 100644
index 00000000..d8d70737
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current.json
@@ -0,0 +1,38 @@
+{
+ "version.label": {
+ "message": "4.x",
+ "description": "La etiqueta para la versión actual"
+ },
+ "sidebar.sidebar.category.Documentation": {
+ "message": "Documentación",
+ "description": "La etiqueta para la categoría Documentation en la barra lateral"
+ },
+ "sidebar.sidebar.category.Server": {
+ "message": "Servidor",
+ "description": "La etiqueta para la categoría Server en la barra lateral"
+ },
+ "sidebar.sidebar.category.Client": {
+ "message": "Cliente",
+ "description": "La etiqueta para la categoría Client en la barra lateral"
+ },
+ "sidebar.sidebar.category.Events": {
+ "message": "Eventos",
+ "description": "La etiqueta para la categoría Events en la barra lateral"
+ },
+ "sidebar.sidebar.category.Adapters": {
+ "message": "Adaptadores",
+ "description": "La etiqueta para la categoría Adapters en la barra lateral"
+ },
+ "sidebar.sidebar.category.Advanced": {
+ "message": "Avanzado",
+ "description": "La etiqueta para la categoría Advanced en la barra lateral"
+ },
+ "sidebar.sidebar.category.Migrations": {
+ "message": "Migraciones",
+ "description": "La etiqueta para la categoría Migrations en la barra lateral"
+ },
+ "sidebar.sidebar.category.Miscellaneous": {
+ "message": "Miscelánea",
+ "description": "La etiqueta para la categoría Miscellaneous en la barra lateral"
+ }
+}
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/connection-state-recovery.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/connection-state-recovery.md
new file mode 100644
index 00000000..ed84fc95
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/connection-state-recovery.md
@@ -0,0 +1,169 @@
+---
+title: Recuperación del estado de conexión
+sidebar_position: 4
+slug: /connection-state-recovery
+---
+
+La recuperación del estado de conexión es una característica que permite restaurar el estado de un cliente después de una desconexión temporal, incluyendo cualquier paquete perdido.
+
+:::info
+
+Esta característica fue añadida en la versión `4.6.0`, lanzada en febrero de 2023.
+
+Las notas de lanzamiento se pueden encontrar [aquí](../../changelog/4.6.0.md).
+
+:::
+
+## Descargo de responsabilidad
+
+Bajo condiciones reales, un cliente Socket.IO inevitablemente experimentará desconexiones temporales, independientemente de la calidad de la conexión.
+
+Esta característica te ayudará a lidiar con tales desconexiones, pero por favor ten en cuenta que la recuperación **no siempre será exitosa**. Es por eso que todavía necesitarás manejar el caso donde los estados del cliente y el servidor deben sincronizarse.
+
+## Uso
+
+La recuperación del estado de conexión debe ser habilitada por el servidor:
+
+```js
+const io = new Server(httpServer, {
+ connectionStateRecovery: {
+ // la duración del respaldo de las sesiones y los paquetes
+ maxDisconnectionDuration: 2 * 60 * 1000,
+ // si se deben omitir los middlewares tras una recuperación exitosa
+ skipMiddlewares: true,
+ }
+});
+```
+
+:::caution
+
+La característica de recuperación del estado de conexión está diseñada para lidiar con desconexiones intermitentes, así que por favor usa un valor razonable para `maxDisconnectionDuration`.
+
+:::
+
+Ante una desconexión inesperada (es decir, no una desconexión manual con `socket.disconnect()`), el servidor almacenará el `id`, las salas y el atributo `data` del socket.
+
+Luego, tras la reconexión, el servidor intentará restaurar el estado del cliente. El atributo `recovered` indica si esta recuperación fue exitosa:
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ if (socket.recovered) {
+ // la recuperación fue exitosa: socket.id, socket.rooms y socket.data fueron restaurados
+ } else {
+ // sesión nueva o no recuperable
+ }
+});
+```
+
+*Cliente*
+
+```js
+socket.on("connect", () => {
+ if (socket.recovered) {
+ // cualquier evento perdido durante el período de desconexión se recibirá ahora
+ } else {
+ // sesión nueva o no recuperable
+ }
+});
+```
+
+Puedes verificar que la recuperación está funcionando forzando el cierre del motor subyacente:
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io({
+ reconnectionDelay: 10000, // por defecto es 1000
+ reconnectionDelayMax: 10000 // por defecto es 5000
+});
+
+socket.on("connect", () => {
+ console.log("¿recuperado?", socket.recovered);
+
+ setTimeout(() => {
+ if (socket.io.engine) {
+ // cerrar la conexión de bajo nivel y activar una reconexión
+ socket.io.engine.close();
+ }
+ }, 10000);
+});
+```
+
+:::tip
+
+También puedes ejecutar este ejemplo directamente en tu navegador en:
+
+- [CodeSandbox](https://codesandbox.io/p/sandbox/github/socketio/socket.io/tree/main/examples/connection-state-recovery-example/esm?file=index.js)
+- [StackBlitz](https://stackblitz.com/github/socketio/socket.io/tree/main/examples/connection-state-recovery-example/esm?file=index.js)
+
+:::
+
+## Compatibilidad con adaptadores existentes
+
+| Adaptador | ¿Soporte? |
+|------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------:|
+| Adaptador integrado (en memoria) | SÍ :white_check_mark: |
+| [Adaptador Redis](../05-Adapters/adapter-redis.md) | NO1 |
+| [Adaptador Redis Streams](../05-Adapters/adapter-redis-streams.md) | SÍ :white_check_mark: |
+| [Adaptador MongoDB](../05-Adapters/adapter-mongo.md) | SÍ :white_check_mark: (desde la versión [`0.3.0`](https://github.com/socketio/socket.io-mongo-adapter/releases/tag/0.3.0)) |
+| [Adaptador Postgres](../05-Adapters/adapter-postgres.md) | WIP |
+| [Adaptador Cluster](../05-Adapters/adapter-cluster.md) | WIP |
+
+[1] Persistir los paquetes no es compatible con el mecanismo Redis PUB/SUB.
+
+## Cómo funciona internamente
+
+- el servidor envía un ID de sesión [durante el handshake](../08-Miscellaneous/sio-protocol.md#connection-to-a-namespace-1) (que es diferente del atributo `id` existente, que es público y puede compartirse libremente)
+
+Ejemplo:
+
+```
+40{"sid":"GNpWD7LbGCBNCr8GAAAB","pid":"YHcX2sdAF1z452-HAAAW"}
+
+donde
+
+4 => el tipo de mensaje Engine.IO
+0 => el tipo CONNECT de Socket.IO
+GN...AB => el id público de la sesión
+YH...AW => el id privado de la sesión
+```
+
+- el servidor también incluye un offset en [cada paquete](../08-Miscellaneous/sio-protocol.md#sending-and-receiving-data-1) (añadido al final del array de datos, para compatibilidad hacia atrás)
+
+Ejemplo:
+
+```
+42["foo","MzUPkW0"]
+
+donde
+
+4 => el tipo de mensaje Engine.IO
+2 => el tipo EVENT de Socket.IO
+foo => el nombre del evento (socket.emit("foo"))
+MzUPkW0 => el offset
+```
+
+:::note
+
+Para que la recuperación tenga éxito, el servidor debe enviar al menos un evento, para inicializar el offset en el lado del cliente.
+
+:::
+
+- tras una desconexión temporal, el servidor almacena el estado del cliente por un tiempo dado (implementado a nivel del adaptador)
+
+- tras la reconexión, el cliente envía tanto el ID de sesión como el último offset que procesó, y el servidor intenta restaurar el estado
+
+Ejemplo:
+
+```
+40{"pid":"YHcX2sdAF1z452-HAAAW","offset":"MzUPkW0"}
+
+donde
+
+4 => el tipo de mensaje Engine.IO
+0 => el tipo CONNECT de Socket.IO
+YH...AW => el id privado de la sesión
+MzUPkW0 => el último offset procesado
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/delivery-guarantees.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/delivery-guarantees.md
new file mode 100644
index 00000000..8ba85512
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/delivery-guarantees.md
@@ -0,0 +1,120 @@
+---
+title: Garantías de entrega
+sidebar_position: 3
+slug: /delivery-guarantees
+toc_max_heading_level: 4
+---
+
+## Orden de los mensajes
+
+Socket.IO garantiza el orden de los mensajes, sin importar qué transporte de bajo nivel se utilice (incluso durante una actualización de HTTP long-polling a WebSocket).
+
+Esto se logra gracias a:
+
+- las garantías proporcionadas por la conexión TCP subyacente
+- el diseño cuidadoso del [mecanismo de actualización](how-it-works.md#mecanismo-de-actualización)
+
+Ejemplo:
+
+```js
+socket.emit("event1");
+socket.emit("event2");
+socket.emit("event3");
+```
+
+En el ejemplo anterior, los eventos siempre serán recibidos en el mismo orden por el otro lado (siempre que realmente lleguen, ver [abajo](#llegada-de-mensajes)).
+
+## Llegada de mensajes
+
+### Como máximo una vez
+
+Por defecto, Socket.IO proporciona una garantía de entrega de **como máximo una vez**:
+
+- si la conexión se interrumpe mientras se envía un evento, entonces no hay garantía de que el otro lado lo haya recibido y no habrá reintento tras la reconexión
+- un cliente desconectado [almacenará eventos en búfer hasta la reconexión](../03-Client/client-offline-behavior.md) (aunque el punto anterior sigue aplicando)
+- no hay tal búfer en el servidor, lo que significa que cualquier evento que haya perdido un cliente desconectado no será transmitido a ese cliente tras la reconexión
+
+:::info
+
+Actualmente, garantías de entrega adicionales deben implementarse en tu aplicación.
+
+:::
+
+### Al menos una vez
+
+#### Del cliente al servidor
+
+Desde el lado del cliente, puedes lograr una garantía de **al menos una vez** con la opción [`retries`](../../client-options.md#retries):
+
+```js
+const socket = io({
+ retries: 3,
+ ackTimeout: 10000
+});
+```
+
+El cliente intentará enviar el evento (hasta `retries + 1` veces), hasta que obtenga una confirmación del servidor.
+
+:::caution
+
+Incluso en ese caso, cualquier evento pendiente se perderá si el usuario actualiza su pestaña.
+
+:::
+
+#### Del servidor al cliente
+
+Para eventos enviados por el servidor, garantías de entrega adicionales pueden implementarse:
+
+- asignando un ID único a cada evento
+- persistiendo los eventos en una base de datos
+- almacenando el offset del último evento recibido en el lado del cliente, y enviándolo tras la reconexión
+
+Ejemplo:
+
+*Cliente*
+
+```js
+const socket = io({
+ auth: {
+ offset: undefined
+ }
+});
+
+socket.on("my-event", ({ id, data }) => {
+ // hacer algo con los datos, y luego actualizar el offset
+ socket.auth.offset = id;
+});
+```
+
+*Servidor*
+
+```js
+io.on("connection", async (socket) => {
+ const offset = socket.handshake.auth.offset;
+ if (offset) {
+ // esto es una reconexión
+ for (const event of await fetchMissedEventsFromDatabase(offset)) {
+ socket.emit("my-event", event);
+ }
+ } else {
+ // esta es una primera conexión
+ }
+});
+
+setInterval(async () => {
+ const event = {
+ id: generateUniqueId(),
+ data: new Date().toISOString()
+ }
+
+ await persistEventToDatabase(event);
+ io.emit("my-event", event);
+}, 1000);
+```
+
+Implementar los métodos faltantes (`fetchMissedEventsFromDatabase()`, `generateUniqueId()` y `persistEventToDatabase()`) es específico de la base de datos y se deja como ejercicio para el lector.
+
+Referencias:
+
+- [`socket.auth`](../../client-options.md#socket-options) (cliente)
+- [`socket.handshake`](../../server-api.md#sockethandshake) (servidor)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/how-it-works.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/how-it-works.md
new file mode 100644
index 00000000..906997d0
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/how-it-works.md
@@ -0,0 +1,130 @@
+---
+title: Cómo funciona
+sidebar_position: 2
+slug: /how-it-works/
+---
+
+El canal bidireccional entre el servidor Socket.IO (Node.js) y el cliente Socket.IO (navegador, Node.js, u [otro lenguaje de programación](index.md#qué-es-socketio)) se establece con una [conexión WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSocket) siempre que sea posible, y usará HTTP long-polling como respaldo.
+
+El código base de Socket.IO está dividido en dos capas distintas:
+
+- la plomería de bajo nivel: lo que llamamos Engine.IO, el motor dentro de Socket.IO
+- la API de alto nivel: Socket.IO en sí
+
+## Engine.IO
+
+Engine.IO es responsable de establecer la conexión de bajo nivel entre el servidor y el cliente. Se encarga de:
+
+- los diversos [transportes](#transportes) y el [mecanismo de actualización](#mecanismo-de-actualización)
+- la [detección de desconexión](#detección-de-desconexión)
+
+Una versión detallada del protocolo Engine.IO se puede encontrar [aquí](../08-Miscellaneous/eio-protocol.md).
+
+El código fuente de la implementación de referencia (escrita en TypeScript) se puede encontrar aquí:
+
+- servidor: https://github.com/socketio/engine.io
+- cliente: https://github.com/socketio/engine.io-client
+- parser: https://github.com/socketio/engine.io-parser
+
+### Transportes
+
+Actualmente hay dos transportes implementados:
+
+- [HTTP long-polling](#http-long-polling)
+- [WebSocket](#websocket)
+
+#### HTTP long-polling
+
+El transporte HTTP long-polling (también llamado simplemente "polling") consiste en solicitudes HTTP sucesivas:
+
+- solicitudes `GET` de larga duración, para recibir datos del servidor
+- solicitudes `POST` de corta duración, para enviar datos al servidor
+
+Debido a la naturaleza del transporte, emisiones sucesivas pueden concatenarse y enviarse dentro de la misma solicitud HTTP.
+
+#### WebSocket
+
+El transporte WebSocket consiste, bueno, en una [conexión WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSockets_API), que proporciona un canal de comunicación bidireccional y de baja latencia entre el servidor y el cliente.
+
+Debido a la naturaleza del transporte, cada emisión se envía en su propio frame WebSocket (algunas emisiones pueden incluso resultar en dos frames WebSocket distintos, más información [aquí](../06-Advanced/custom-parser.md#the-default-parser)).
+
+### Handshake
+
+Al comienzo de la conexión Engine.IO, el servidor envía alguna información:
+
+```json
+{
+ "sid": "FSDjX-WRwSA4zTZMALqx",
+ "upgrades": ["websocket"],
+ "pingInterval": 25000,
+ "pingTimeout": 20000,
+ "maxPayload": 1000000
+}
+```
+
+- el `sid` es el ID de la sesión, debe incluirse en el parámetro de consulta `sid` en todas las solicitudes HTTP posteriores
+- el array `upgrades` contiene la lista de todos los transportes "mejores" que son soportados por el servidor
+- los valores `pingInterval` y `pingTimeout` se usan en el mecanismo de heartbeat
+- el valor `maxPayload` indica el número máximo de bytes por paquete aceptado por el servidor
+
+### Mecanismo de actualización
+
+Por defecto, el cliente establece la conexión con el transporte HTTP long-polling.
+
+**Pero, ¿por qué?**
+
+Aunque WebSocket es claramente la mejor manera de establecer una comunicación bidireccional, la experiencia ha demostrado que no siempre es posible establecer una conexión WebSocket, debido a proxies corporativos, firewalls personales, software antivirus...
+
+Desde la perspectiva del usuario, una conexión WebSocket fallida puede traducirse en hasta 10 segundos de espera para que la aplicación en tiempo real comience a intercambiar datos. Esto **perceptiblemente** afecta la experiencia del usuario.
+
+En resumen, Engine.IO se enfoca primero en la fiabilidad y la experiencia del usuario, y segundo en mejoras marginales potenciales de UX y mayor rendimiento del servidor.
+
+Para actualizar, el cliente:
+
+- se asegura de que su búfer de salida esté vacío
+- pone el transporte actual en modo de solo lectura
+- intenta establecer una conexión con el otro transporte
+- si tiene éxito, cierra el primer transporte
+
+Puedes verificar en el Monitor de Red de tu navegador:
+
+
+
+1. handshake (contiene el ID de sesión — aquí, `zBjrh...AAAK` — que se usa en las solicitudes posteriores)
+2. enviar datos (HTTP long-polling)
+3. recibir datos (HTTP long-polling)
+4. actualización (WebSocket)
+5. recibir datos (HTTP long-polling, cerrado una vez que la conexión WebSocket en 4. se establece exitosamente)
+
+### Detección de desconexión
+
+La conexión Engine.IO se considera cerrada cuando:
+
+- una solicitud HTTP (ya sea GET o POST) falla (por ejemplo, cuando el servidor se apaga)
+- la conexión WebSocket se cierra (por ejemplo, cuando el usuario cierra la pestaña en su navegador)
+- `socket.disconnect()` se llama en el lado del servidor o del cliente
+
+También hay un mecanismo de heartbeat que verifica que la conexión entre el servidor y el cliente sigue activa y funcionando:
+
+En un intervalo dado (el valor `pingInterval` enviado en el handshake) el servidor envía un paquete PING y el cliente tiene unos segundos (el valor `pingTimeout`) para enviar un paquete PONG de vuelta. Si el servidor no recibe un paquete PONG de vuelta, considerará que la conexión está cerrada. De manera inversa, si el cliente no recibe un paquete PING dentro de `pingInterval + pingTimeout`, considerará que la conexión está cerrada.
+
+Las razones de desconexión se enumeran [aquí](../02-Server/server-socket-instance.md#disconnect) (lado del servidor) y [aquí](../03-Client/client-socket-instance.md#disconnect) (lado del cliente).
+
+
+## Socket.IO
+
+Socket.IO proporciona algunas características adicionales sobre la conexión Engine.IO:
+
+- reconexión automática
+- [almacenamiento en búfer de paquetes](../03-Client/client-offline-behavior.md#buffered-events)
+- [confirmaciones](../04-Events/emitting-events.md#acknowledgements)
+- broadcasting [a todos los clientes](../04-Events/broadcasting-events.md) o [a un subconjunto de clientes](../04-Events/rooms.md) (lo que llamamos "Room")
+- [multiplexación](../06-Advanced/namespaces.md) (lo que llamamos "Namespace")
+
+Una versión detallada del protocolo Socket.IO se puede encontrar [aquí](../08-Miscellaneous/sio-protocol.md).
+
+El código fuente de la implementación de referencia (escrita en TypeScript) se puede encontrar aquí:
+
+- servidor: https://github.com/socketio/socket.io
+- cliente: https://github.com/socketio/socket.io-client
+- parser: https://github.com/socketio/socket.io-parser
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/index.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/index.md
new file mode 100644
index 00000000..0151955e
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/index.md
@@ -0,0 +1,222 @@
+---
+title: Introducción
+sidebar_position: 1
+slug: /
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+:::tip
+
+Si eres nuevo en Socket.IO, te recomendamos revisar nuestro [tutorial](../../tutorial/01-introduction.md).
+
+:::
+
+## Qué es Socket.IO
+
+Socket.IO es una biblioteca que permite la comunicación **de baja latencia**, **bidireccional** y **basada en eventos** entre un cliente y un servidor.
+
+
+
+La conexión de Socket.IO puede establecerse con diferentes transportes de bajo nivel:
+
+- HTTP long-polling
+- [WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSockets_API)
+- [WebTransport](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API)
+
+Socket.IO seleccionará automáticamente la mejor opción disponible, dependiendo de:
+
+- las capacidades del navegador (ver [aquí](https://caniuse.com/websockets) y [aquí](https://caniuse.com/webtransport))
+- la red (algunas redes bloquean conexiones WebSocket y/o WebTransport)
+
+Puedes encontrar más detalles sobre esto en la sección ["Cómo funciona"](./how-it-works.md).
+
+### Implementaciones del servidor
+
+| Lenguaje | Sitio web |
+|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| JavaScript (Node.js) | - [Pasos de instalación](../02-Server/server-installation.md) - [API](../../server-api.md) - [Código fuente](https://github.com/socketio/socket.io) |
+| JavaScript (Deno) | https://github.com/socketio/socket.io-deno |
+| Java | https://github.com/mrniko/netty-socketio |
+| Java | https://github.com/trinopoty/socket.io-server-java |
+| Python | https://github.com/miguelgrinberg/python-socketio |
+| Golang | https://github.com/googollee/go-socket.io |
+| Rust | https://github.com/Totodore/socketioxide |
+
+### Implementaciones del cliente
+
+| Lenguaje | Sitio web |
+|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| JavaScript (navegador, Node.js o React Native) | - [Pasos de instalación](../03-Client/client-installation.md) - [API](../../client-api.md) - [Código fuente](https://github.com/socketio/socket.io-client) |
+| JavaScript (para Mini-Programas de WeChat) | https://github.com/weapp-socketio/weapp.socket.io |
+| Java | https://github.com/socketio/socket.io-client-java |
+| C++ | https://github.com/socketio/socket.io-client-cpp |
+| Swift | https://github.com/socketio/socket.io-client-swift |
+| Dart | https://github.com/rikulo/socket.io-client-dart |
+| Python | https://github.com/miguelgrinberg/python-socketio |
+| .Net | https://github.com/doghappy/socket.io-client-csharp |
+| Rust | https://github.com/1c3t3a/rust-socketio |
+| Kotlin | https://github.com/icerockdev/moko-socket-io |
+| PHP | https://github.com/ElephantIO/elephant.io |
+| Golang | https://github.com/maldikhan/go.socket.io |
+
+## Qué NO es Socket.IO
+
+:::caution
+
+Socket.IO **NO** es una implementación de WebSocket.
+
+:::
+
+Aunque Socket.IO efectivamente usa WebSocket para el transporte cuando es posible, agrega metadatos adicionales a cada paquete. Es por eso que un cliente WebSocket no podrá conectarse exitosamente a un servidor Socket.IO, y un cliente Socket.IO tampoco podrá conectarse a un servidor WebSocket simple.
+
+```js
+// ADVERTENCIA: ¡el cliente NO podrá conectarse!
+const socket = io("ws://echo.websocket.org");
+```
+
+Si estás buscando un servidor WebSocket simple, por favor echa un vistazo a [ws](https://github.com/websockets/ws) o [µWebSockets.js](https://github.com/uNetworking/uWebSockets.js).
+
+También hay [discusiones](https://github.com/nodejs/node/issues/19308) para incluir un servidor WebSocket en el núcleo de Node.js.
+
+En el lado del cliente, podrías estar interesado en el paquete [robust-websocket](https://github.com/nathanboktae/robust-websocket).
+
+:::caution
+
+Socket.IO no está pensado para ser usado en un servicio en segundo plano para aplicaciones móviles.
+
+:::
+
+La biblioteca Socket.IO mantiene una conexión TCP abierta al servidor, lo que puede resultar en un alto consumo de batería para tus usuarios. Por favor usa una plataforma de mensajería dedicada como [FCM](https://firebase.google.com/docs/cloud-messaging) para este caso de uso.
+
+## Características
+
+Aquí están las características que proporciona Socket.IO sobre WebSockets simples:
+
+### Fallback a HTTP long-polling
+
+La conexión recurrirá a HTTP long-polling en caso de que no se pueda establecer la conexión WebSocket.
+
+Esta característica fue la razón #1 por la que la gente usaba Socket.IO cuando el proyecto fue creado hace más de diez años (!), ya que el soporte del navegador para WebSockets todavía estaba en sus inicios.
+
+Aunque la mayoría de los navegadores ahora soportan WebSockets (más del [97%](https://caniuse.com/mdn-api_websocket)), sigue siendo una gran característica ya que todavía recibimos informes de usuarios que no pueden establecer una conexión WebSocket porque están detrás de algún proxy mal configurado.
+
+### Reconexión automática
+
+Bajo algunas condiciones particulares, la conexión WebSocket entre el servidor y el cliente puede interrumpirse sin que ambos lados estén conscientes del estado roto del enlace.
+
+Es por eso que Socket.IO incluye un mecanismo de heartbeat, que verifica periódicamente el estado de la conexión.
+
+Y cuando el cliente finalmente se desconecta, se reconecta automáticamente con un retraso de retroceso exponencial, para no sobrecargar el servidor.
+
+### Almacenamiento en búfer de paquetes
+
+Los paquetes se almacenan automáticamente en búfer cuando el cliente está desconectado, y se enviarán tras la reconexión.
+
+Más información [aquí](../03-Client/client-offline-behavior.md#buffered-events).
+
+### Confirmaciones (Acknowledgements)
+
+Socket.IO proporciona una forma conveniente de enviar un evento y recibir una respuesta:
+
+*Emisor*
+
+```js
+socket.emit("hello", "world", (response) => {
+ console.log(response); // "recibido"
+});
+```
+
+*Receptor*
+
+```js
+socket.on("hello", (arg, callback) => {
+ console.log(arg); // "world"
+ callback("recibido");
+});
+```
+
+También puedes agregar un tiempo de espera:
+
+```js
+socket.timeout(5000).emit("hello", "world", (err, response) => {
+ if (err) {
+ // el otro lado no confirmó el evento en el tiempo dado
+ } else {
+ console.log(response); // "recibido"
+ }
+});
+```
+
+### Broadcasting
+
+En el lado del servidor, puedes enviar un evento a [todos los clientes conectados](../04-Events/broadcasting-events.md) o [a un subconjunto de clientes](../04-Events/rooms.md):
+
+```js
+// a todos los clientes conectados
+io.emit("hello");
+
+// a todos los clientes conectados en la sala "news"
+io.to("news").emit("hello");
+```
+
+Esto también funciona cuando se [escala a múltiples nodos](../02-Server/using-multiple-nodes.md).
+
+### Multiplexación
+
+Los namespaces te permiten dividir la lógica de tu aplicación sobre una única conexión compartida. Esto puede ser útil, por ejemplo, si quieres crear un canal de "admin" al que solo puedan unirse usuarios autorizados.
+
+```js
+io.on("connection", (socket) => {
+ // usuarios clásicos
+});
+
+io.of("/admin").on("connection", (socket) => {
+ // usuarios administradores
+});
+```
+
+Más información sobre esto [aquí](../06-Advanced/namespaces.md).
+
+## Preguntas comunes
+
+### ¿Socket.IO sigue siendo necesario hoy en día?
+
+Es una pregunta justa, ya que WebSockets es soportado [casi en todas partes](https://caniuse.com/mdn-api_websocket) ahora.
+
+Dicho esto, creemos que si usas WebSockets simples para tu aplicación, eventualmente necesitarás implementar la mayoría de las características que ya están incluidas (y probadas en batalla) en Socket.IO, como [reconexión](#reconexión-automática), [confirmaciones](#confirmaciones-acknowledgements) o [broadcasting](#broadcasting).
+
+### ¿Cuál es la sobrecarga del protocolo Socket.IO?
+
+`socket.emit("hello", "world")` se enviará como un único frame WebSocket conteniendo `42["hello","world"]` con:
+
+- `4` siendo el tipo de paquete "message" de Engine.IO
+- `2` siendo el tipo de paquete "message" de Socket.IO
+- `["hello","world"]` siendo la versión `JSON.stringify()` del array de argumentos
+
+Así que, unos pocos bytes adicionales por cada mensaje, que pueden reducirse aún más mediante el uso de un [parser personalizado](../06-Advanced/custom-parser.md).
+
+:::info
+
+El tamaño del bundle del navegador en sí es de [`10.4 kB`](https://bundlephobia.com/package/socket.io-client) (minificado y comprimido con gzip).
+
+:::
+
+Puedes encontrar los detalles del protocolo Socket.IO [aquí](../08-Miscellaneous/sio-protocol.md).
+
+### ¿Algo no funciona correctamente, pueden ayudarme?
+
+Por favor revisa nuestra [Guía de solución de problemas](../01-Documentation/troubleshooting.md).
+
+## Próximos pasos
+
+- [Ejemplo para comenzar](/get-started/chat)
+- [Instalación del servidor](../02-Server/server-installation.md)
+- [Instalación del cliente](../03-Client/client-installation.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/logging-and-debugging.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/logging-and-debugging.md
new file mode 100644
index 00000000..61fec382
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/logging-and-debugging.md
@@ -0,0 +1,73 @@
+---
+title: Registro y depuración
+sidebar_position: 5
+slug: /logging-and-debugging/
+---
+
+Socket.IO ahora está completamente instrumentado por una utilidad minimalista pero tremendamente poderosa llamada [debug](https://github.com/visionmedia/debug) de TJ Holowaychuk.
+
+Antes de la versión 1.0, el servidor Socket.IO registraba todo en la consola por defecto. Esto resultó ser molestamente verboso para muchos usuarios (aunque extremadamente útil para otros), así que ahora por defecto somos completamente silenciosos.
+
+La idea básica es que cada módulo usado por Socket.IO proporciona diferentes ámbitos de depuración que te dan información sobre los internos. Por defecto, toda la salida está suprimida, y puedes optar por ver mensajes proporcionando la variable de entorno `DEBUG` (Node.js) o la propiedad `localStorage.debug` (navegadores).
+
+Puedes verlo en acción, por ejemplo, en nuestra página de inicio:
+
+
+
+## Ámbitos de depuración disponibles
+
+La mejor manera de ver qué información está disponible es usar el `*`:
+
+```
+DEBUG=* node yourfile.js
+```
+
+o en el navegador:
+
+```
+localStorage.debug = '*';
+```
+
+Y luego filtrar por los ámbitos que te interesen. Puedes prefijar el `*` con ámbitos, separados por coma si hay más de uno. Por ejemplo, para ver solo las declaraciones de depuración del cliente socket.io en Node.js prueba esto:
+
+```
+DEBUG=socket.io:client* node yourfile.js
+```
+
+Para ver todos los mensajes de depuración del motor *y* socket.io:
+
+```
+DEBUG=engine,socket.io* node yourfile.js
+```
+
+
+### Eliminar debug de tu bundle del navegador
+
+Aunque es útil durante el desarrollo, el paquete debug añade un peso extra al bundle final (aproximadamente 4KB minificado y comprimido con gzip), por eso está excluido del bundle slim (más detalles sobre los diversos bundles del navegador se pueden encontrar [aquí](../03-Client/client-installation.md#from-a-cdn)).
+
+Si estás usando webpack, puedes eliminarlo con [webpack-remove-debug](https://github.com/johngodley/webpack-remove-debug):
+
+```js
+{
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ loader: 'webpack-remove-debug'
+ }
+ ]
+ }
+}
+```
+
+## Registros de error en la consola del navegador
+
+Por favor nota que los registros de error como:
+
+- `net::ERR_INTERNET_DISCONNECTED`
+- `net::ERR_CONNECTION_REFUSED`
+- `WebSocket is already in CLOSING or CLOSED state`
+- `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at xxx. (Reason: CORS header 'Access-Control-Allow-Origin' missing).`
+- `The connection to xxx was interrupted while the page was loading`
+
+no son emitidos por la biblioteca Socket.IO sino por el navegador en sí, y por lo tanto están fuera de nuestro control.
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/memory-usage.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/memory-usage.md
new file mode 100644
index 00000000..d2d749cf
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/memory-usage.md
@@ -0,0 +1,81 @@
+---
+title: Uso de memoria
+sidebar_position: 9
+slug: /memory-usage/
+---
+
+Los recursos consumidos por tu servidor Socket.IO dependerán principalmente de:
+
+- el número de clientes conectados
+- el número de mensajes ([emit básico](../04-Events/emitting-events.md#basic-emit), [emit con confirmación](../04-Events/emitting-events.md#acknowledgements) y [broadcast](../04-Events/broadcasting-events.md)) recibidos y enviados por segundo
+
+:::info
+
+El uso de memoria del servidor Socket.IO debería escalar **linealmente** con el número de clientes conectados.
+
+:::
+
+:::tip
+
+Por defecto, se mantiene en memoria una referencia a la primera solicitud HTTP de cada sesión. Esta referencia es necesaria cuando se trabaja con `express-session` por ejemplo (ver [aquí](/how-to/use-with-express-session)), pero puede descartarse para ahorrar memoria:
+
+```js
+io.engine.on("connection", (rawSocket) => {
+ rawSocket.request = null;
+});
+```
+
+:::
+
+El código fuente para reproducir los resultados presentados en esta página se puede encontrar [aquí](https://github.com/socketio/socket.io-benchmarks).
+
+Ver también:
+
+- [Pruebas de carga](../06-Advanced/load-testing.md)
+- [Ajuste de rendimiento](../06-Advanced/performance-tuning.md)
+
+## Uso de memoria por implementación de servidor WebSocket
+
+El uso de memoria del servidor Socket.IO depende en gran medida del uso de memoria de la implementación subyacente del servidor WebSocket.
+
+El gráfico a continuación muestra el uso de memoria del servidor Socket.IO, desde 0 hasta 10,000 clientes conectados, con:
+
+- un servidor Socket.IO basado en el paquete [`ws`](https://github.com/websockets/ws) (usado por defecto)
+- un servidor Socket.IO basado en el paquete [`eiows`](https://github.com/mmdevries/eiows), una implementación de servidor WebSocket en C++ (ver [pasos de instalación](../02-Server/server-installation.md#other-websocket-server-implementations))
+- un servidor Socket.IO basado en el paquete [`µWebSockets.js`](https://github.com/uNetworking/uWebSockets.js), una alternativa en C++ al servidor HTTP nativo de Node.js (ver [pasos de instalación](../02-Server/server-installation.md#usage-with-uwebsockets))
+- un servidor WebSocket simple basado en el paquete [`ws`](https://github.com/websockets/ws)
+
+
+
+
+Probado en `Ubuntu 22.04 LTS` con Node.js `v20.3.0`, con las siguientes versiones de paquetes:
+
+- `socket.io@4.7.2`
+- `eiows@6.7.2`
+- `uWebSockets.js@20.33.0`
+- `ws@8.11.0`
+
+## Uso de memoria a lo largo del tiempo
+
+El gráfico a continuación muestra el uso de memoria del servidor Socket.IO a lo largo del tiempo, desde 0 hasta 10,000 clientes conectados.
+
+
+
+:::note
+
+Para propósitos de demostración, llamamos manualmente al recolector de basura al final de cada ola de clientes:
+
+```js
+io.on("connection", (socket) => {
+ socket.on("disconnect", () => {
+ const lastToDisconnect = io.of("/").sockets.size === 0;
+ if (lastToDisconnect) {
+ gc();
+ }
+ });
+});
+```
+
+Lo cual explica la caída limpia en el uso de memoria cuando el último cliente se desconecta. Esto no es necesario en tu aplicación, la recolección de basura se activará automáticamente cuando sea necesario.
+
+:::
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/testing.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/testing.md
new file mode 100644
index 00000000..7fedfa74
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/testing.md
@@ -0,0 +1,1017 @@
+---
+title: Pruebas
+sidebar_position: 6
+slug: /testing/
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+A continuación encontrarás algunos ejemplos de código con bibliotecas de pruebas comunes:
+
+
+
+
+
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev mocha chai
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+const { createServer } = require("node:http");
+const { Server } = require("socket.io");
+const ioc = require("socket.io-client");
+const { assert } = require("chai");
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ before((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ after(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ assert.equal(arg, "world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ it("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ assert.equal(arg, "hola");
+ done();
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ assert.equal(result, "bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev mocha chai
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+import { createServer } from "node:http";
+import { io as ioc } from "socket.io-client";
+import { Server } from "socket.io";
+import { assert } from "chai";
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ before((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ after(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ assert.equal(arg, "world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ it("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ assert.equal(arg, "hola");
+ done();
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ assert.equal(result, "bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev mocha chai @types/mocha @types/chai
+```
+
+Suite de pruebas:
+
+```ts title="test/basic.ts"
+import { createServer } from "node:http";
+import { type AddressInfo } from "node:net";
+import { io as ioc, type Socket as ClientSocket } from "socket.io-client";
+import { Server, type Socket as ServerSocket } from "socket.io";
+import { assert } from "chai";
+
+function waitFor(socket: ServerSocket | ClientSocket, event: string) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io: Server, serverSocket: ServerSocket, clientSocket: ClientSocket;
+
+ before((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = (httpServer.address() as AddressInfo).port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ after(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ assert.equal(arg, "world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ it("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ assert.equal(arg, "hola");
+ done();
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ assert.equal(result, "bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Referencia: https://mochajs.org/
+
+
+
+
+
+
+
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev jest
+```
+
+Suite de pruebas:
+
+```js title="__tests__/basic.test.js"
+const { createServer } = require("node:http");
+const { Server } = require("socket.io");
+const ioc = require("socket.io-client");
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ beforeAll((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ test("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toBe("world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ test("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toBe("hola");
+ done();
+ });
+ });
+
+ test("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toBe("bar");
+ });
+
+ test("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev jest
+```
+
+Suite de pruebas:
+
+```js title="__tests__/basic.test.js"
+import { createServer } from "node:http";
+import { io as ioc } from "socket.io-client";
+import { Server } from "socket.io";
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ beforeAll((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ test("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toBe("world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ test("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toBe("hola");
+ done();
+ });
+ });
+
+ test("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toBe("bar");
+ });
+
+ test("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev jest @types/jest
+```
+
+Suite de pruebas:
+
+```ts title="__tests__/basic.test.ts"
+import { createServer } from "node:http";
+import { type AddressInfo } from "node:net";
+import { io as ioc, type Socket as ClientSocket } from "socket.io-client";
+import { Server, type Socket as ServerSocket } from "socket.io";
+
+function waitFor(socket: ServerSocket | ClientSocket, event: string) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ beforeAll((done) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = (httpServer.address() as AddressInfo).port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", done);
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ test("debería funcionar", (done) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toBe("world");
+ done();
+ });
+ serverSocket.emit("hello", "world");
+ });
+
+ test("debería funcionar con una confirmación", (done) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toBe("hola");
+ done();
+ });
+ });
+
+ test("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toBe("bar");
+ });
+
+ test("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Referencia: https://jestjs.io/
+
+
+
+
+
+
+
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev tape
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+const test = require("tape");
+const { createServer } = require("node:http");
+const { Server } = require("socket.io");
+const ioc = require("socket.io-client");
+
+let io, serverSocket, clientSocket;
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+test("configuración", (t) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", t.end);
+ });
+});
+
+test("funciona", (t) => {
+ t.plan(1);
+ clientSocket.on("hello", (arg) => {
+ t.equal(arg, "world");
+ });
+ serverSocket.emit("hello", "world");
+});
+
+test("funciona con una confirmación", (t) => {
+ t.plan(1);
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ t.equal(arg, "hola");
+ });
+});
+
+test("funciona con emitWithAck()", async (t) => {
+ t.plan(1);
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ t.equal(result, "bar");
+});
+
+test("funciona con waitFor()", async (t) => {
+ t.plan(1);
+ clientSocket.emit("baz");
+
+ await waitFor(serverSocket, "baz");
+ t.pass();
+});
+
+test.onFinish(() => {
+ io.close();
+ clientSocket.disconnect();
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev tape
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+import { test } from "tape";
+import { createServer } from "node:http";
+import { io as ioc } from "socket.io-client";
+import { Server } from "socket.io";
+
+let io, serverSocket, clientSocket;
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+test("configuración", (t) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", t.end);
+ });
+});
+
+test("funciona", (t) => {
+ t.plan(1);
+ clientSocket.on("hello", (arg) => {
+ t.equal(arg, "world");
+ });
+ serverSocket.emit("hello", "world");
+});
+
+test("funciona con una confirmación", (t) => {
+ t.plan(1);
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ t.equal(arg, "hola");
+ });
+});
+
+test("funciona con emitWithAck()", async (t) => {
+ t.plan(1);
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ t.equal(result, "bar");
+});
+
+test("funciona con waitFor()", async (t) => {
+ t.plan(1);
+ clientSocket.emit("baz");
+
+ await waitFor(serverSocket, "baz");
+ t.pass();
+});
+
+test.onFinish(() => {
+ io.close();
+ clientSocket.disconnect();
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev tape
+```
+
+Suite de pruebas:
+
+```ts title="test/basic.ts"
+import { test } from "tape";
+import { createServer } from "node:http";
+import { type AddressInfo } from "node:net";
+import { io as ioc, type Socket as ClientSocket } from "socket.io-client";
+import { Server, type Socket as ServerSocket } from "socket.io";
+
+let io: Server, serverSocket: ServerSocket, clientSocket: ClientSocket;
+
+function waitFor(socket: ServerSocket | ClientSocket, event: string) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+test("configuración", (t) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = (httpServer.address() as AddressInfo).port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", t.end);
+ });
+});
+
+test("funciona", (t) => {
+ t.plan(1);
+ clientSocket.on("hello", (arg) => {
+ t.equal(arg, "world");
+ });
+ serverSocket.emit("hello", "world");
+});
+
+test("funciona con una confirmación", (t) => {
+ t.plan(1);
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ t.equal(arg, "hola");
+ });
+});
+
+test("funciona con emitWithAck()", async (t) => {
+ t.plan(1);
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ t.equal(result, "bar");
+});
+
+test("funciona con waitFor()", async (t) => {
+ t.plan(1);
+ clientSocket.emit("baz");
+
+ await waitFor(serverSocket, "baz");
+ t.pass();
+});
+
+test.onFinish(() => {
+ io.close();
+ clientSocket.disconnect();
+});
+```
+
+
+
+
+Referencia: https://github.com/ljharb/tape
+
+
+
+
+
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev vitest
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+const { beforeAll, afterAll, describe, it, expect } = require("vitest");
+const { createServer } = require("node:http");
+const { Server } = require("socket.io");
+const ioc = require("socket.io-client");
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ beforeAll(() => {
+ return new Promise((resolve) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", resolve);
+ });
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", () => {
+ return new Promise((resolve) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toEqual("world");
+ resolve();
+ });
+ serverSocket.emit("hello", "world");
+ });
+ });
+
+ it("debería funcionar con una confirmación", () => {
+ return new Promise((resolve) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toEqual("hola");
+ resolve();
+ });
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toEqual("bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev vitest
+```
+
+Suite de pruebas:
+
+```js title="test/basic.js"
+import { beforeAll, afterAll, describe, it, expect } from "vitest";
+import { createServer } from "node:http";
+import { io as ioc } from "socket.io-client";
+import { Server } from "socket.io";
+
+function waitFor(socket, event) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io, serverSocket, clientSocket;
+
+ beforeAll(() => {
+ return new Promise((resolve) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = httpServer.address().port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", resolve);
+ });
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", () => {
+ return new Promise((resolve) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toEqual("world");
+ resolve();
+ });
+ serverSocket.emit("hello", "world");
+ });
+ });
+
+ it("debería funcionar con una confirmación", () => {
+ return new Promise((resolve) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toEqual("hola");
+ resolve();
+ });
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toEqual("bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Instalación:
+
+```
+npm install --save-dev vitest
+```
+
+Suite de pruebas:
+
+```ts title="test/basic.ts"
+import { beforeAll, afterAll, describe, it, expect } from "vitest";
+import { createServer } from "node:http";
+import { type AddressInfo } from "node:net";
+import { io as ioc, type Socket as ClientSocket } from "socket.io-client";
+import { Server, type Socket as ServerSocket } from "socket.io";
+
+function waitFor(socket: ServerSocket | ClientSocket, event: string) {
+ return new Promise((resolve) => {
+ socket.once(event, resolve);
+ });
+}
+
+describe("mi proyecto increíble", () => {
+ let io: Server, serverSocket: ServerSocket, clientSocket: ClientSocket;
+
+ beforeAll(() => {
+ return new Promise((resolve) => {
+ const httpServer = createServer();
+ io = new Server(httpServer);
+ httpServer.listen(() => {
+ const port = (httpServer.address() as AddressInfo).port;
+ clientSocket = ioc(`http://localhost:${port}`);
+ io.on("connection", (socket) => {
+ serverSocket = socket;
+ });
+ clientSocket.on("connect", resolve);
+ });
+ });
+ });
+
+ afterAll(() => {
+ io.close();
+ clientSocket.disconnect();
+ });
+
+ it("debería funcionar", () => {
+ return new Promise((resolve) => {
+ clientSocket.on("hello", (arg) => {
+ expect(arg).toEqual("world");
+ resolve();
+ });
+ serverSocket.emit("hello", "world");
+ });
+ });
+
+ it("debería funcionar con una confirmación", () => {
+ return new Promise((resolve) => {
+ serverSocket.on("hi", (cb) => {
+ cb("hola");
+ });
+ clientSocket.emit("hi", (arg) => {
+ expect(arg).toEqual("hola");
+ resolve();
+ });
+ });
+ });
+
+ it("debería funcionar con emitWithAck()", async () => {
+ serverSocket.on("foo", (cb) => {
+ cb("bar");
+ });
+ const result = await clientSocket.emitWithAck("foo");
+ expect(result).toEqual("bar");
+ });
+
+ it("debería funcionar con waitFor()", () => {
+ clientSocket.emit("baz");
+
+ return waitFor(serverSocket, "baz");
+ });
+});
+```
+
+
+
+
+Referencia: https://vitest.dev/
+
+
+
+
+
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/troubleshooting.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/troubleshooting.md
new file mode 100644
index 00000000..42265cca
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/troubleshooting.md
@@ -0,0 +1,538 @@
+---
+title: Solución de problemas de conexión
+sidebar_label: Solución de problemas
+sidebar_position: 7
+slug: /troubleshooting-connection-issues/
+toc_max_heading_level: 4
+---
+
+:::tip
+
+La [Interfaz de Administración](../06-Advanced/admin-ui.md) puede darte información adicional sobre el estado de tu despliegue de Socket.IO.
+
+:::
+
+Problemas comunes/conocidos:
+
+- [el socket no puede conectarse](#problema-el-socket-no-puede-conectarse)
+- [el socket se desconecta](#problema-el-socket-se-desconecta)
+- [el socket está atascado en HTTP long-polling](#problema-el-socket-está-atascado-en-http-long-polling)
+
+Otros errores comunes:
+
+- [Registro duplicado de eventos](#registro-duplicado-de-eventos)
+- [Registro retrasado del manejador de eventos](#registro-retrasado-del-manejador-de-eventos)
+- [Uso del atributo `socket.id`](#uso-del-atributo-socketid)
+- [Despliegue en una plataforma serverless](#despliegue-en-una-plataforma-serverless)
+
+
+## Problema: el socket no puede conectarse
+
+### Pasos de solución de problemas
+
+En el lado del cliente, el evento `connect_error` proporciona información adicional:
+
+```js
+socket.on("connect_error", (err) => {
+ // la razón del error, por ejemplo "xhr poll error"
+ console.log(err.message);
+
+ // alguna descripción adicional, por ejemplo el código de estado de la respuesta HTTP inicial
+ console.log(err.description);
+
+ // algún contexto adicional, por ejemplo el objeto XMLHttpRequest
+ console.log(err.context);
+});
+```
+
+En el lado del servidor, el evento `connection_error` también puede proporcionar información adicional:
+
+```js
+io.engine.on("connection_error", (err) => {
+ console.log(err.req); // el objeto de solicitud
+ console.log(err.code); // el código de error, por ejemplo 1
+ console.log(err.message); // el mensaje de error, por ejemplo "Session ID unknown"
+ console.log(err.context); // algún contexto adicional del error
+});
+```
+
+Aquí está la lista de posibles códigos de error:
+
+| Código | Mensaje | Posibles explicaciones |
+|:------:|:------------------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 0 | "Transport unknown" | Esto no debería suceder bajo circunstancias normales. |
+| 1 | "Session ID unknown" | Usualmente, esto significa que las sesiones sticky no están habilitadas (ver [abajo](#no-habilitaste-sesiones-sticky-en-una-configuración-de-múltiples-servidores)). |
+| 2 | "Bad handshake method" | Esto no debería suceder bajo circunstancias normales. |
+| 3 | "Bad request" | Usualmente, esto significa que un proxy frente a tu servidor no está reenviando correctamente los encabezados WebSocket (ver [aquí](../02-Server/behind-a-reverse-proxy.md)). |
+| 4 | "Forbidden" | La conexión fue denegada por el método [`allowRequest()`](../../server-options.md#allowrequest). |
+| 5 | "Unsupported protocol version" | La versión del cliente no es compatible con el servidor (ver [aquí](#el-cliente-no-es-compatible-con-la-versión-del-servidor)). |
+
+### Posibles explicaciones
+
+#### Estás intentando alcanzar un servidor WebSocket simple
+
+Como se explicó en la sección ["Qué NO es Socket.IO"](index.md#qué-no-es-socketio), el cliente Socket.IO no es una implementación de WebSocket y por lo tanto no podrá establecer una conexión con un servidor WebSocket, incluso con `transports: ["websocket"]`:
+
+```js
+const socket = io("ws://echo.websocket.org", {
+ transports: ["websocket"]
+});
+```
+
+#### El servidor no es alcanzable
+
+Por favor asegúrate de que el servidor Socket.IO sea realmente alcanzable en la URL dada. Puedes probarlo con:
+
+```
+curl "/socket.io/?EIO=4&transport=polling"
+```
+
+lo cual debería devolver algo como esto:
+
+```
+0{"sid":"Lbo5JLzTotvW3g2LAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}
+```
+
+Si ese no es el caso, por favor verifica que el servidor Socket.IO esté ejecutándose, y que no haya nada entre medio que prevenga la conexión.
+
+:::note
+
+Los servidores v1/v2 (que implementan la v3 del protocolo, de ahí el `EIO=3`) devolverán algo como esto:
+
+```
+96:0{"sid":"ptzi_578ycUci8WLB9G1","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000}2:40
+```
+
+:::
+
+#### El cliente no es compatible con la versión del servidor
+
+Mantener la compatibilidad hacia atrás es una prioridad principal para nosotros, pero en algunos casos particulares tuvimos que implementar algunos cambios importantes a nivel de protocolo:
+
+- de v1.x a v2.0.0 (lanzado en mayo de 2017), para mejorar la compatibilidad con clientes que no son JavaScript (ver [aquí](https://github.com/socketio/engine.io/issues/315))
+- de v2.x a v3.0.0 (lanzado en noviembre de 2020), para arreglar algunos problemas de larga data en el protocolo de una vez por todas (ver [aquí](../07-Migrations/migrating-from-2-to-3.md))
+
+:::info
+
+`v4.0.0` contiene algunos cambios importantes en la API del servidor JavaScript. El protocolo Socket.IO en sí no fue actualizado, por lo que un cliente v3 podrá alcanzar un servidor v4 y viceversa (ver [aquí](../07-Migrations/migrating-from-3-to-4.md)).
+
+:::
+
+Por ejemplo, alcanzar un servidor v3/v4 con un cliente v1/v2 resultará en la siguiente respuesta:
+
+```
+< HTTP/1.1 400 Bad Request
+< Content-Type: application/json
+
+{"code":5,"message":"Unsupported protocol version"}
+```
+
+Aquí está la tabla de compatibilidad para el [cliente JS](https://github.com/socketio/socket.io-client/):
+
+
+
+
Versión del cliente JS
+
Versión del servidor Socket.IO
+
+
+
1.x
+
2.x
+
3.x
+
4.x
+
+
+
1.x
+
SÍ
+
NO
+
NO
+
NO
+
+
+
2.x
+
NO
+
SÍ
+
SÍ1
+
SÍ1
+
+
+
3.x
+
NO
+
NO
+
SÍ
+
SÍ
+
+
+
4.x
+
NO
+
NO
+
SÍ
+
SÍ
+
+
+
+[1] Sí, con [allowEIO3: true](../../server-options.md#alloweio3)
+
+Aquí está la tabla de compatibilidad para el [cliente Java](https://github.com/socketio/socket.io-client-java/):
+
+
+
+
Versión del cliente Java
+
Versión del servidor Socket.IO
+
+
+
2.x
+
3.x
+
4.x
+
+
+
1.x
+
SÍ
+
SÍ1
+
SÍ1
+
+
+
2.x
+
NO
+
SÍ
+
SÍ
+
+
+
+[1] Sí, con [allowEIO3: true](../../server-options.md#alloweio3)
+
+Aquí está la tabla de compatibilidad para el [cliente Swift](https://github.com/socketio/socket.io-client-swift/):
+
+
+
+
Versión del cliente Swift
+
Versión del servidor Socket.IO
+
+
+
2.x
+
3.x
+
4.x
+
+
+
v15.x
+
SÍ
+
SÍ1
+
SÍ2
+
+
+
v16.x
+
SÍ3
+
SÍ
+
SÍ
+
+
+
+[1] Sí, con [allowEIO3: true](../../server-options.md#alloweio3) (servidor) y `.connectParams(["EIO": "3"])` (cliente):
+
+```swift
+SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.connectParams(["EIO": "3"])])
+```
+
+[2] Sí, [allowEIO3: true](../../server-options.md#alloweio3) (servidor)
+
+[3] Sí, con `.version(.two)` (cliente):
+
+```swift
+SocketManager(socketURL: URL(string:"http://localhost:8087/")!, config: [.version(.two)])
+```
+
+#### El servidor no envía los encabezados CORS necesarios
+
+Si ves el siguiente error en tu consola:
+
+```
+Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ...
+```
+
+Probablemente significa que:
+
+- o no estás realmente alcanzando el servidor Socket.IO (ver [arriba](#el-servidor-no-es-alcanzable))
+- o no habilitaste [Cross-Origin Resource Sharing](https://developer.mozilla.org/es/docs/Web/HTTP/CORS) (CORS) en el lado del servidor.
+
+Por favor consulta la documentación [aquí](../02-Server/handling-cors.md).
+
+#### No habilitaste sesiones sticky (en una configuración de múltiples servidores)
+
+Al escalar a múltiples servidores Socket.IO, necesitas asegurarte de que todas las solicitudes de una sesión Socket.IO dada lleguen al mismo servidor Socket.IO. La explicación se puede encontrar [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+No hacerlo resultará en respuestas HTTP 400 con el código: `{"code":1,"message":"Session ID unknown"}`
+
+Por favor consulta la documentación [aquí](../02-Server/using-multiple-nodes.md).
+
+#### La ruta de solicitud no coincide en ambos lados
+
+Por defecto, el cliente envía — y el servidor espera — solicitudes HTTP con la ruta de solicitud "/socket.io/".
+
+Esto puede controlarse con la opción `path`:
+
+*Servidor*
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({
+ path: "/my-custom-path/"
+});
+
+io.listen(3000);
+```
+
+*Cliente*
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io(SERVER_URL, {
+ path: "/my-custom-path/"
+});
+```
+
+En ese caso, las solicitudes HTTP se verán como `/my-custom-path/?EIO=4&transport=polling[&...]`.
+
+:::caution
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io("/my-custom-path/");
+```
+
+significa que el cliente intentará alcanzar el [namespace](../06-Advanced/namespaces.md) llamado "/my-custom-path/", pero la ruta de solicitud seguirá siendo "/socket.io/".
+
+:::
+
+## Problema: el socket se desconecta
+
+### Pasos de solución de problemas
+
+Primero y ante todo, por favor nota que las desconexiones son comunes y esperadas, incluso en una conexión a Internet estable:
+
+- cualquier cosa entre el usuario y el servidor Socket.IO puede encontrar una falla temporal o ser reiniciada
+- el servidor en sí puede ser terminado como parte de una política de autoescalado
+- el usuario puede perder la conexión o cambiar de WiFi a 4G, en caso de un navegador móvil
+- el navegador en sí puede congelar una pestaña inactiva
+
+Dicho esto, el cliente Socket.IO siempre intentará reconectarse, a menos que se le indique específicamente [lo contrario](../../client-options.md#reconnection).
+
+El evento `disconnect` proporciona información adicional:
+
+```js
+socket.on("disconnect", (reason, details) => {
+ // la razón de la desconexión, por ejemplo "transport error"
+ console.log(reason);
+
+ // la razón de bajo nivel de la desconexión, por ejemplo "xhr post error"
+ console.log(details.message);
+
+ // alguna descripción adicional, por ejemplo el código de estado de la respuesta HTTP
+ console.log(details.description);
+
+ // algún contexto adicional, por ejemplo el objeto XMLHttpRequest
+ console.log(details.context);
+});
+```
+
+Las posibles razones se enumeran [aquí](../03-Client/client-socket-instance.md#disconnect).
+
+### Posibles explicaciones
+
+#### Algo entre el servidor y el cliente cierra la conexión
+
+Si la desconexión ocurre a intervalos regulares, esto podría indicar que algo entre el servidor y el cliente no está configurado correctamente y cierra la conexión:
+
+- nginx
+
+El valor de [`proxy_read_timeout`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout) de nginx (60 segundos por defecto) debe ser mayor que [`pingInterval + pingTimeout`](../../server-options.md#pinginterval) de Socket.IO (45 segundos por defecto), de lo contrario cerrará forzosamente la conexión si no se envían datos después del retraso dado y el cliente obtendrá un error "transport close".
+
+- Apache HTTP Server
+
+El valor de [`ProxyTimeout`](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#proxytimeout) de httpd (60 segundos por defecto) debe ser mayor que [`pingInterval + pingTimeout`](../../server-options.md#pinginterval) de Socket.IO (45 segundos por defecto), de lo contrario cerrará forzosamente la conexión si no se envían datos después del retraso dado y el cliente obtendrá un error "transport close".
+
+#### La pestaña del navegador fue minimizada y el heartbeat falló
+
+Cuando una pestaña del navegador no está en foco, algunos navegadores (como [Chrome](https://developer.chrome.com/blog/timer-throttling-in-chrome-88/#intensive-throttling)) limitan los temporizadores de JavaScript, lo que podría llevar a una desconexión por timeout de ping **en Socket.IO v2**, ya que el mecanismo de heartbeat dependía de la función `setTimeout` en el lado del cliente.
+
+Como solución alternativa, puedes aumentar el valor de `pingTimeout` en el lado del servidor:
+
+```js
+const io = new Server({
+ pingTimeout: 60000
+});
+```
+
+Por favor nota que actualizar a Socket.IO v4 (al menos `socket.io-client@4.1.3`, debido a [esto](https://github.com/socketio/engine.io-client/commit/f30a10b7f45517fcb3abd02511c58a89e0ef498f)) debería prevenir este tipo de problemas, ya que el mecanismo de heartbeat ha sido invertido (el servidor ahora envía paquetes PING).
+
+#### El cliente no es compatible con la versión del servidor
+
+Dado que el formato de los paquetes enviados sobre el transporte WebSocket es similar en v2 y v3/v4, podrías ser capaz de conectar con un cliente incompatible (ver [arriba](#el-cliente-no-es-compatible-con-la-versión-del-servidor)), pero la conexión eventualmente se cerrará después de un retraso dado.
+
+Así que si estás experimentando una desconexión regular después de 30 segundos (que era la suma de los valores de [pingTimeout](../../server-options.md#pingtimeout) y [pingInterval](../../server-options.md#pinginterval) en Socket.IO v2), esto ciertamente se debe a una incompatibilidad de versiones.
+
+#### Estás intentando enviar una carga útil enorme
+
+Si te desconectas mientras envías una carga útil enorme, esto puede significar que has alcanzado el valor de [`maxHttpBufferSize`](../../server-options.md#maxhttpbuffersize), que por defecto es 1 MB. Por favor ajústalo según tus necesidades:
+
+```js
+const io = require("socket.io")(httpServer, {
+ maxHttpBufferSize: 1e8
+});
+```
+
+Una carga útil enorme que toma más tiempo en subir que el valor de la opción [`pingTimeout`](../../server-options.md#pingtimeout) también puede desencadenar una desconexión (ya que el [mecanismo de heartbeat](../01-Documentation/how-it-works.md#detección-de-desconexión) falla durante la subida). Por favor ajústalo según tus necesidades:
+
+```js
+const io = require("socket.io")(httpServer, {
+ pingTimeout: 60000
+});
+```
+
+## Problema: el socket está atascado en HTTP long-polling
+
+### Pasos de solución de problemas
+
+En la mayoría de los casos, deberías ver algo como esto:
+
+
+
+1. el handshake de Engine.IO (contiene el ID de sesión — aquí, `zBjrh...AAAK` — que se usa en las solicitudes posteriores)
+2. la solicitud de handshake de Socket.IO (contiene el valor de la opción `auth`)
+3. la respuesta de handshake de Socket.IO (contiene el [Socket#id](../02-Server/server-socket-instance.md#socketid))
+4. la conexión WebSocket
+5. la primera solicitud HTTP long-polling, que se cierra una vez que la conexión WebSocket se establece
+
+Si no ves una respuesta [HTTP 101 Switching Protocols](https://developer.mozilla.org/es/docs/Web/HTTP/Status/101) para la 4ta solicitud, eso significa que algo entre el servidor y tu navegador está previniendo la conexión WebSocket.
+
+Por favor nota que esto no es necesariamente bloqueante ya que la conexión todavía se establece con HTTP long-polling, pero es menos eficiente.
+
+Puedes obtener el nombre del transporte actual con:
+
+**Lado del cliente**
+
+```js
+socket.on("connect", () => {
+ const transport = socket.io.engine.transport.name; // en la mayoría de los casos, "polling"
+
+ socket.io.engine.on("upgrade", () => {
+ const upgradedTransport = socket.io.engine.transport.name; // en la mayoría de los casos, "websocket"
+ });
+});
+```
+
+**Lado del servidor**
+
+```js
+io.on("connection", (socket) => {
+ const transport = socket.conn.transport.name; // en la mayoría de los casos, "polling"
+
+ socket.conn.on("upgrade", () => {
+ const upgradedTransport = socket.conn.transport.name; // en la mayoría de los casos, "websocket"
+ });
+});
+```
+
+### Posibles explicaciones
+
+#### Un proxy frente a tus servidores no acepta la conexión WebSocket
+
+Si un proxy como nginx o Apache HTTPD no está configurado correctamente para aceptar conexiones WebSocket, entonces podrías obtener un error `TRANSPORT_MISMATCH`:
+
+```js
+io.engine.on("connection_error", (err) => {
+ console.log(err.code); // 3
+ console.log(err.message); // "Bad request"
+ console.log(err.context); // { name: 'TRANSPORT_MISMATCH', transport: 'websocket', previousTransport: 'polling' }
+});
+```
+
+Lo que significa que el servidor Socket.IO no recibe el encabezado `Connection: upgrade` necesario (puedes verificar el objeto `err.req.headers`).
+
+Por favor consulta la documentación [aquí](../02-Server/behind-a-reverse-proxy.md).
+
+#### [`express-status-monitor`](https://www.npmjs.com/package/express-status-monitor) ejecuta su propia instancia de socket.io
+
+Por favor consulta la solución [aquí](https://github.com/RafalWilinski/express-status-monitor).
+
+## Otros errores comunes
+
+### Registro duplicado de eventos
+
+En el lado del cliente, el evento `connect` se emitirá cada vez que el socket se reconecte, por lo que los listeners de eventos deben registrarse fuera del listener del evento `connect`:
+
+MAL :warning:
+
+```js
+socket.on("connect", () => {
+ socket.on("foo", () => {
+ // ...
+ });
+});
+```
+
+BIEN :+1:
+
+```js
+socket.on("connect", () => {
+ // ...
+});
+
+socket.on("foo", () => {
+ // ...
+});
+```
+
+Si ese no es el caso, tu listener de eventos podría ser llamado múltiples veces.
+
+### Registro retrasado del manejador de eventos
+
+MAL :warning:
+
+```js
+io.on("connection", async (socket) => {
+ await longRunningOperation();
+
+ // ¡ADVERTENCIA! Algunos paquetes podrían ser recibidos por el servidor pero sin manejador
+ socket.on("hello", () => {
+ // ...
+ });
+});
+```
+
+BIEN :+1:
+
+```js
+io.on("connection", async (socket) => {
+ socket.on("hello", () => {
+ // ...
+ });
+
+ await longRunningOperation();
+});
+```
+
+### Uso del atributo `socket.id`
+
+Por favor nota que, a menos que la [recuperación del estado de conexión](../01-Documentation/connection-state-recovery.md) esté habilitada, el atributo `id` es un ID **efímero** que no está destinado a ser usado en tu aplicación (o solo para propósitos de depuración) porque:
+
+- este ID se regenera después de cada reconexión (por ejemplo cuando la conexión WebSocket se corta, o cuando el usuario actualiza la página)
+- dos pestañas diferentes del navegador tendrán dos IDs diferentes
+- no hay cola de mensajes almacenada para un ID dado en el servidor (es decir, si el cliente está desconectado, los mensajes enviados desde el servidor a este ID se pierden)
+
+Por favor usa un ID de sesión regular en su lugar (ya sea enviado en una cookie, o almacenado en localStorage y enviado en el payload de [`auth`](../../client-options.md#auth)).
+
+Ver también:
+
+- [Parte II de nuestra guía de mensajes privados](/get-started/private-messaging-part-2/)
+- [Cómo lidiar con cookies](/how-to/deal-with-cookies)
+
+### Despliegue en una plataforma serverless
+
+Dado que la mayoría de las plataformas serverless (como Vercel) cobran por la duración del manejador de solicitudes, mantener una conexión de larga duración con Socket.IO (o incluso WebSocket simple) no es recomendado.
+
+Referencias:
+
+- https://vercel.com/guides/do-vercel-serverless-functions-support-websocket-connections
+- https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api.html
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/typescript.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/typescript.md
new file mode 100644
index 00000000..fbb34279
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/01-Documentation/typescript.md
@@ -0,0 +1,189 @@
+---
+title: TypeScript
+sidebar_position: 8
+slug: /typescript/
+---
+
+A partir de la versión 3, Socket.IO ahora tiene soporte de primera clase para [TypeScript](https://www.typescriptlang.org/).
+
+## Tipos para el servidor
+
+Primero, declara algunos tipos:
+
+```ts
+interface ServerToClientEvents {
+ noArg: () => void;
+ basicEmit: (a: number, b: string, c: Buffer) => void;
+ withAck: (d: string, callback: (e: number) => void) => void;
+}
+
+interface ClientToServerEvents {
+ hello: () => void;
+}
+
+interface InterServerEvents {
+ ping: () => void;
+}
+
+interface SocketData {
+ name: string;
+ age: number;
+}
+```
+
+Y úsalos al crear tu servidor:
+
+```ts
+const io = new Server<
+ ClientToServerEvents,
+ ServerToClientEvents,
+ InterServerEvents,
+ SocketData
+>();
+```
+
+¡Entonces, aprovecha la ayuda de tu IDE!
+
+Los eventos declarados en la interfaz `ServerToClientEvents` se usan al enviar y transmitir eventos:
+
+```ts
+io.on("connection", (socket) => {
+ socket.emit("noArg");
+ socket.emit("basicEmit", 1, "2", Buffer.from([3]));
+ socket.emit("withAck", "4", (e) => {
+ // e se infiere como number
+ });
+
+ // funciona al transmitir a todos
+ io.emit("noArg");
+
+ // funciona al transmitir a una sala
+ io.to("room1").emit("basicEmit", 1, "2", Buffer.from([3]));
+});
+```
+
+Los declarados en la interfaz `ClientToServerEvents` se usan al recibir eventos:
+
+```ts
+io.on("connection", (socket) => {
+ socket.on("hello", () => {
+ // ...
+ });
+});
+```
+
+Los declarados en la interfaz `InterServerEvents` se usan para la comunicación entre servidores (añadido en `socket.io@4.1.0`):
+
+```ts
+io.serverSideEmit("ping");
+
+io.on("ping", () => {
+ // ...
+});
+```
+
+Y finalmente, el tipo `SocketData` se usa para tipar el atributo `socket.data` (añadido en `socket.io@4.4.0`):
+
+```ts
+io.on("connection", (socket) => {
+ socket.data.name = "john";
+ socket.data.age = 42;
+});
+```
+
+:::caution
+
+Estas sugerencias de tipo no reemplazan la validación/sanitización adecuada de la entrada. Como siempre, nunca confíes en la entrada del usuario.
+
+:::
+
+## Tipos para el cliente
+
+En el lado del cliente, puedes reutilizar las mismas interfaces `ServerToClientEvents` y `ClientToServerEvents`:
+
+```ts
+import { io, Socket } from "socket.io-client";
+
+// por favor nota que los tipos están invertidos
+const socket: Socket = io();
+```
+
+De manera similar, los eventos declarados en la interfaz `ClientToServerEvents` se usan al enviar eventos:
+
+```ts
+socket.emit("hello");
+```
+
+Y los declarados en `ServerToClientEvents` se usan al recibir eventos:
+
+```ts
+socket.on("noArg", () => {
+ // ...
+});
+
+socket.on("basicEmit", (a, b, c) => {
+ // a se infiere como number, b como string y c como buffer
+});
+
+socket.on("withAck", (d, callback) => {
+ // d se infiere como string y callback como una función que toma un number como argumento
+});
+```
+
+## Tipos personalizados para cada namespace
+
+Ya que cada [Namespace](../06-Advanced/namespaces.md) puede tener su propio conjunto de eventos, también puedes proporcionar algunos tipos para cada uno de ellos:
+
+```ts
+import { Server } from "socket.io";
+
+// tipos para el namespace principal
+const io = new Server();
+
+// tipos para el namespace llamado "/my-namespace"
+interface NamespaceSpecificClientToServerEvents {
+ foo: (arg: string) => void
+}
+
+interface NamespaceSpecificServerToClientEvents {
+ bar: (arg: string) => void;
+}
+
+interface NamespaceSpecificInterServerEvents {
+ // ...
+}
+
+interface NamespaceSpecificSocketData {
+ // ...
+}
+
+const myNamespace: Namespace<
+ NamespaceSpecificClientToServerEvents,
+ NamespaceSpecificServerToClientEvents,
+ NamespaceSpecificInterServerEvents,
+ NamespaceSpecificSocketData
+ > = io.of("/my-namespace");
+
+myNamespace.on("connection", (socket) => {
+ socket.on("foo", () => {
+ // ...
+ });
+
+ socket.emit("bar", "123");
+});
+```
+
+Y en el lado del cliente:
+
+```ts
+import { io, Socket } from "socket.io-client";
+
+const socket: Socket<
+ NamespaceSpecificServerToClientEvents,
+ NamespaceSpecificClientToServerEvents
+ > = io("/my-namespace");
+
+socket.on("bar", (arg) => {
+ console.log(arg); // "123"
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/behind-a-reverse-proxy.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/behind-a-reverse-proxy.md
new file mode 100644
index 00000000..1230cd5b
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/behind-a-reverse-proxy.md
@@ -0,0 +1,205 @@
+---
+title: Detrás de un proxy inverso
+sidebar_position: 6
+slug: /reverse-proxy/
+---
+
+A continuación encontrarás la configuración necesaria para desplegar un servidor Socket.IO detrás de una solución de proxy inverso, como:
+
+- [nginx](#nginx)
+- [Apache HTTPD](#apache-httpd)
+- [Node.js `http-proxy`](#nodejs-http-proxy)
+- [Caddy 2](#caddy-2)
+
+En una configuración de múltiples servidores, por favor revisa la documentación [aquí](using-multiple-nodes.md).
+
+## nginx
+
+Contenido de `/etc/nginx/nginx.conf`:
+
+```nginx
+http {
+ server {
+ listen 80;
+
+ location / {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+
+ proxy_pass http://localhost:3000;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ }
+}
+```
+
+Relacionado:
+
+- [documentación de proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass)
+- [configuración en una configuración de múltiples servidores](using-multiple-nodes.md#nginx-configuration)
+
+:::caution
+
+El valor de [`proxy_read_timeout`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout) de nginx (60 segundos por defecto) debe ser mayor que [`pingInterval + pingTimeout`](../../server-options.md#pinginterval) de Socket.IO (45 segundos por defecto), de lo contrario nginx cerrará forzosamente la conexión si no se envían datos después del retraso dado y el cliente obtendrá un error "transport close".
+
+:::
+
+Si solo quieres reenviar las solicitudes de Socket.IO (por ejemplo cuando nginx maneja el contenido estático):
+
+```
+http {
+ server {
+ listen 80;
+ root /var/www/html;
+
+ location /socket.io/ {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+
+ proxy_pass http://localhost:3000;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ }
+}
+```
+
+O con una [ruta](../../server-options.md#path) personalizada:
+
+```
+http {
+ server {
+ listen 80;
+ root /var/www/html;
+
+ location /my-custom-path/ {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+
+ proxy_pass http://localhost:3000;
+
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ }
+}
+```
+
+En ese caso, el servidor y el cliente deben configurarse en consecuencia:
+
+*Servidor*
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({
+ path: "/my-custom-path/"
+});
+```
+
+*Cliente*
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io({
+ path: "/my-custom-path/"
+});
+```
+
+## Apache HTTPD
+
+Contenido de `/usr/local/apache2/conf/httpd.conf`:
+
+```apache
+Listen 80
+
+ServerName example.com
+
+LoadModule mpm_event_module modules/mod_mpm_event.so
+
+LoadModule authn_file_module modules/mod_authn_file.so
+LoadModule authn_core_module modules/mod_authn_core.so
+LoadModule authz_host_module modules/mod_authz_host.so
+LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
+LoadModule authz_user_module modules/mod_authz_user.so
+LoadModule authz_core_module modules/mod_authz_core.so
+
+LoadModule headers_module modules/mod_headers.so
+LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
+LoadModule proxy_module modules/mod_proxy.so
+LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
+LoadModule proxy_http_module modules/mod_proxy_http.so
+LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
+LoadModule rewrite_module modules/mod_rewrite.so
+LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
+LoadModule unixd_module modules/mod_unixd.so
+
+User daemon
+Group daemon
+
+ProxyPass / http://localhost:3000/
+RewriteEngine on
+RewriteCond %{HTTP:Upgrade} websocket [NC]
+RewriteCond %{HTTP:Connection} upgrade [NC]
+RewriteRule ^/?(.*) "ws://localhost:3000/$1" [P,L]
+
+# debe ser mayor que pingInterval (25s por defecto) + pingTimeout (20s por defecto)
+ProxyTimeout 60
+```
+
+Relacionado:
+
+- [documentación de mod_proxy_wstunnel](https://httpd.apache.org/docs/2.4/en/mod/mod_proxy_wstunnel.html)
+- [configuración en una configuración de múltiples servidores](using-multiple-nodes.md#apache-httpd-configuration)
+
+## Node.js `http-proxy`
+
+Instalación: `npm i http-proxy`
+
+```js
+const httpProxy = require("http-proxy");
+
+httpProxy
+ .createProxyServer({
+ target: "http://localhost:3000",
+ ws: true,
+ })
+ .listen(80);
+```
+
+[Documentación](https://github.com/http-party/node-http-proxy#readme)
+
+## Caddy 2
+
+Contenido de `Caddyfile` para [Caddy 2](https://caddyserver.com/v2), si solo quieres reenviar las solicitudes de Socket.IO
+
+```
+example.com {
+ reverse_proxy /socket.io/* localhost:3000
+}
+```
+
+O, si quieres una ruta personalizada:
+
+```
+example.com {
+ rewrite /path /path/
+ handle_path /path/* {
+ rewrite * /socket.io{path}
+ reverse_proxy localhost:3000
+ }
+}
+```
+
+Relacionado
+
+- [Publicación en el foro de la solución](https://caddy.community/t/i-cant-get-socket-io-proxy-to-work-on-v2/8703/2)
+- [Caddyfile reverse proxy](https://caddyserver.com/docs/caddyfile/patterns#reverse-proxy)
+- [Directivas de Caddyfile](https://caddyserver.com/docs/caddyfile/directives)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/handling-cors.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/handling-cors.md
new file mode 100644
index 00000000..ef25ceb9
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/handling-cors.md
@@ -0,0 +1,177 @@
+---
+title: Manejo de CORS
+sidebar_position: 8
+slug: /handling-cors/
+---
+
+## ¿Qué es CORS?
+
+Referencia: https://developer.mozilla.org/es/docs/Web/HTTP/CORS
+
+Cross-Origin Resource Sharing (CORS) es una característica de seguridad aplicada por los navegadores web que controla cómo los recursos pueden ser obtenidos desde un origen diferente al que está ejecutando la aplicación web. Un "origen" se define por la combinación de un esquema (como `https://`), un dominio y un puerto:
+
+- El origen `https://example.com` es diferente de `http://example.com` porque los protocolos difieren.
+- El origen `https://api.example.com` es diferente de `https://example.com` porque los dominios difieren.
+- El origen `https://example.com:8080` es diferente de `https://example.com` porque los puertos difieren.
+
+Por defecto, los navegadores bloquean las solicitudes hechas por JavaScript ejecutándose en un origen hacia recursos alojados en otro origen, a menos que el servidor lo permita explícitamente. Esta restricción está diseñada para prevenir que scripts maliciosos accedan a datos sensibles de otros sitios.
+
+Por ejemplo, digamos que un usuario de tu sitio web `https://buen-dominio.com` es dirigido al sitio web `https://mal-dominio.com` al hacer clic en un enlace de correo electrónico fraudulento. Gracias a CORS, el navegador de tu usuario bloqueará cualquier solicitud que intente llegar a tu sitio web `https://buen-dominio.com`, asegurando que cualquier script malicioso en `https://mal-dominio.com` no pueda extraer datos o realizar acciones en tu sitio web en nombre de tu usuario.
+
+Sin embargo, hay casos comunes donde realmente quieres permitir solicitudes de origen cruzado, por ejemplo:
+
+- si tu backend está alojado en un subdominio diferente (ej. frontend en `https://example.com` y backend en `https://api.example.com`)
+- para desarrollo local (ej. frontend en `http://localhost:3000` y backend en `http://localhost:8080`)
+
+La opción [`cors`](../../server-options.md#cors) cubre estos casos de uso, ver [abajo](#configuración).
+
+:::caution
+
+Dos advertencias importantes:
+
+- CORS solo aplica a navegadores
+
+Incluso con la configuración CORS adecuada, un atacante todavía puede ejecutar un script en su máquina o en una VM y alcanzar tu sitio web. Las aplicaciones nativas tampoco están cubiertas.
+
+- CORS solo aplica a HTTP long-polling
+
+Las conexiones WebSocket no están sujetas a restricciones CORS.
+
+Si quieres restringir quién puede realmente alcanzar tu sitio web, puedes usar la opción [`allowRequest`](../../server-options.md#allowrequest).
+
+:::
+
+## Configuración
+
+Desde Socket.IO v3, necesitas habilitar explícitamente [Cross-Origin Resource Sharing](https://developer.mozilla.org/es/docs/Web/HTTP/CORS) (CORS).
+
+```js
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ cors: {
+ origin: ["https://example.com"]
+ }
+});
+```
+
+Opciones disponibles:
+
+| Opción | Descripción |
+|------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `origin` | Configura el encabezado CORS **Access-Control-Allow-Origin**. |
+| `methods` | Configura el encabezado CORS **Access-Control-Allow-Methods**. Espera una cadena delimitada por comas (ej: 'GET,PUT,POST') o un array (ej: `['GET', 'PUT', 'POST']`). |
+| `allowedHeaders` | Configura el encabezado CORS **Access-Control-Allow-Headers**. Espera una cadena delimitada por comas (ej: 'Content-Type,Authorization') o un array (ej: `['Content-Type', 'Authorization']`). Si no se especifica, por defecto refleja los encabezados especificados en el encabezado **Access-Control-Request-Headers** de la solicitud. |
+| `exposedHeaders` | Configura el encabezado CORS **Access-Control-Expose-Headers**. Espera una cadena delimitada por comas (ej: 'Content-Range,X-Content-Range') o un array (ej: `['Content-Range', 'X-Content-Range']`). Si no se especifica, no se exponen encabezados personalizados. |
+| `credentials` | Configura el encabezado CORS **Access-Control-Allow-Credentials**. Establece a `true` para pasar el encabezado, de lo contrario se omite. |
+| `maxAge` | Configura el encabezado CORS **Access-Control-Max-Age**. Establece a un entero para pasar el encabezado, de lo contrario se omite. |
+| `preflightContinue` | Pasa la respuesta preflight de CORS al siguiente manejador. |
+| `optionsSuccessStatus` | Proporciona un código de estado para usar en solicitudes `OPTIONS` exitosas, ya que algunos navegadores antiguos (IE11, varios SmartTVs) fallan con `204`. |
+
+Valores posibles para la opción `origin`:
+
+- `Boolean` - establece `origin` a `true` para reflejar el [origen de la solicitud](http://tools.ietf.org/html/draft-abarth-origin-09), como se define por `req.header('Origin')`, o establécelo a `false` para deshabilitar CORS.
+- `String` - establece `origin` a un origen específico. Por ejemplo si lo estableces a `"http://example.com"` solo se permitirán solicitudes de "http://example.com".
+- `RegExp` - establece `origin` a un patrón de expresión regular que se usará para probar el origen de la solicitud. Si coincide, el origen de la solicitud se reflejará. Por ejemplo el patrón `/example\.com$/` reflejará cualquier solicitud que venga de un origen que termine con "example.com".
+- `Array` - establece `origin` a un array de orígenes válidos. Cada origen puede ser un `String` o un `RegExp`. Por ejemplo `["http://example1.com", /\.example2\.com$/]` aceptará cualquier solicitud de "http://example1.com" o de un subdominio de "example2.com".
+- `Function` - establece `origin` a una función que implementa alguna lógica personalizada. La función toma el origen de la solicitud como el primer parámetro y un callback (que espera la firma `err [object], allow [bool]`) como el segundo.
+
+Ejemplo con cookies ([withCredentials](https://developer.mozilla.org/es/docs/Web/API/XMLHttpRequest/withCredentials)) y encabezados adicionales:
+
+```js
+// lado del servidor
+const io = new Server(httpServer, {
+ cors: {
+ origin: ["https://example.com"],
+ allowedHeaders: ["my-custom-header"],
+ credentials: true
+ }
+});
+
+// lado del cliente
+import { io } from "socket.io-client";
+const socket = io("https://api.example.com", {
+ withCredentials: true,
+ extraHeaders: {
+ "my-custom-header": "abcd"
+ }
+});
+```
+
+Nota: esto también aplica a localhost si tu aplicación web y tu servidor no se sirven desde el mismo puerto
+
+```js
+const io = new Server(httpServer, {
+ cors: {
+ origin: ["http://localhost:8080"]
+ }
+});
+
+httpServer.listen(3000);
+```
+
+Puedes denegar todas las solicitudes de origen cruzado con la opción [`allowRequest`](../../server-options.md#allowrequest):
+
+```js
+const io = new Server(httpServer, {
+ allowRequest: (req, callback) => {
+ const noOriginHeader = req.headers.origin === undefined;
+ callback(null, noOriginHeader); // solo permite solicitudes sin encabezado 'origin'
+ }
+});
+```
+
+## Solución de problemas
+
+### Falta el encabezado CORS 'Access-Control-Allow-Origin'
+
+Mensaje de error completo:
+
+> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at .../socket.io/?EIO=4&transport=polling&t=NMnp2WI. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
+
+Si has configurado correctamente tu servidor (ver [arriba](#configuración)), esto podría significar que tu navegador no pudo alcanzar el servidor Socket.IO.
+
+El siguiente comando:
+
+```
+curl "https://api.example.com/socket.io/?EIO=4&transport=polling"
+```
+
+debería devolver algo como:
+
+```
+0{"sid":"Lbo5JLzTotvW3g2LAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":20000,"maxPayload":1000000}
+```
+
+Si ese no es el caso, por favor verifica que tu servidor esté escuchando y sea realmente alcanzable en el puerto dado.
+
+### La credencial no es soportada si el encabezado CORS 'Access-Control-Allow-Origin' es '*'
+
+Mensaje de error completo:
+
+> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at '.../socket.io/?EIO=4&transport=polling&t=NvQfU77'. (Reason: Credential is not supported if the CORS header 'Access-Control-Allow-Origin' is '*')
+
+No puedes establecer [`withCredentials`](../../client-options.md#withcredentials) a `true` con `origin: *`, necesitas usar un origen específico:
+
+```js
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ cors: {
+ origin: ["https://my-frontend.com"],
+ credentials: true
+ }
+});
+```
+
+### Se esperaba 'true' en el encabezado CORS 'Access-Control-Allow-Credentials'
+
+Mensaje de error completo:
+
+> Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at .../socket.io/?EIO=4&transport=polling&t=NvQny19. (Reason: expected 'true' in CORS header 'Access-Control-Allow-Credentials')
+
+En ese caso, [`withCredentials`](../../client-options.md#withcredentials) está establecido a `true` en el cliente, pero al servidor le falta el atributo `credentials` en la opción [`cors`](../../server-options.md#cors). Ver el ejemplo de arriba.
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/middlewares.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/middlewares.md
new file mode 100644
index 00000000..bdb4b2b2
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/middlewares.md
@@ -0,0 +1,194 @@
+---
+title: Middlewares
+sidebar_position: 5
+slug: /middlewares/
+---
+
+Una función middleware es una función que se ejecuta para cada conexión entrante.
+
+Las funciones middleware pueden ser útiles para:
+
+- registro (logging)
+- autenticación / autorización
+- limitación de tasa (rate limiting)
+
+Nota: esta función se ejecutará solo una vez por conexión (incluso si la conexión consiste en múltiples solicitudes HTTP).
+
+:::info
+
+Si estás buscando middlewares de Express, por favor revisa [esta sección](#compatibilidad-con-middleware-de-express).
+
+:::
+
+## Registrar un middleware
+
+Una función middleware tiene acceso a la [instancia de Socket](server-socket-instance.md) y a la siguiente función middleware registrada.
+
+```js
+io.use((socket, next) => {
+ if (isValid(socket.request)) {
+ next();
+ } else {
+ next(new Error("inválido"));
+ }
+});
+```
+
+Puedes registrar varias funciones middleware, y se ejecutarán secuencialmente:
+
+```js
+io.use((socket, next) => {
+ next();
+});
+
+io.use((socket, next) => {
+ next(new Error("no pasarás"));
+});
+
+io.use((socket, next) => {
+ // no se ejecuta, ya que el middleware anterior retornó un error
+ next();
+});
+```
+
+Por favor asegúrate de llamar a `next()` en cualquier caso. De lo contrario, la conexión quedará colgada hasta que se cierre después de un tiempo de espera dado.
+
+**Nota importante**: la instancia de Socket no está realmente conectada cuando se ejecuta el middleware, lo que significa que no se emitirá ningún evento `disconnect` si la conexión finalmente falla.
+
+Por ejemplo, si el cliente cierra manualmente la conexión:
+
+```js
+// lado del servidor
+io.use((socket, next) => {
+ setTimeout(() => {
+ // next se llama después de la desconexión del cliente
+ next();
+ }, 1000);
+
+ socket.on("disconnect", () => {
+ // no se activa
+ });
+});
+
+io.on("connection", (socket) => {
+ // no se activa
+});
+
+// lado del cliente
+const socket = io();
+setTimeout(() => {
+ socket.disconnect();
+}, 500);
+```
+
+## Enviar credenciales
+
+El cliente puede enviar credenciales con la opción `auth`:
+
+```js
+// objeto simple
+const socket = io({
+ auth: {
+ token: "abc"
+ }
+});
+
+// o con una función
+const socket = io({
+ auth: (cb) => {
+ cb({
+ token: "abc"
+ });
+ }
+});
+```
+
+Esas credenciales se pueden acceder en el objeto [handshake](server-socket-instance.md#sockethandshake) en el lado del servidor:
+
+```js
+io.use((socket, next) => {
+ const token = socket.handshake.auth.token;
+ // ...
+});
+```
+
+## Manejo de errores de middleware
+
+Si el método `next` se llama con un objeto Error, la conexión será rechazada y el cliente recibirá un evento `connect_error`.
+
+```js
+// lado del cliente
+socket.on("connect_error", (err) => {
+ console.log(err.message); // imprime el mensaje asociado con el error
+});
+```
+
+Puedes adjuntar detalles adicionales al objeto Error:
+
+```js
+// lado del servidor
+io.use((socket, next) => {
+ const err = new Error("no autorizado");
+ err.data = { content: "Por favor intenta más tarde" }; // detalles adicionales
+ next(err);
+});
+
+// lado del cliente
+socket.on("connect_error", (err) => {
+ console.log(err instanceof Error); // true
+ console.log(err.message); // no autorizado
+ console.log(err.data); // { content: "Por favor intenta más tarde" }
+});
+```
+
+## Compatibilidad con middleware de Express
+
+Dado que no están vinculados a un ciclo usual de solicitud/respuesta HTTP, los middlewares de Socket.IO no son realmente compatibles con los [middlewares de Express](https://expressjs.com/en/guide/using-middleware.html).
+
+Dicho esto, a partir de la versión `4.6.0`, los middlewares de Express ahora son soportados por el motor subyacente:
+
+```js
+io.engine.use((req, res, next) => {
+ // hacer algo
+
+ next();
+});
+```
+
+Los middlewares serán llamados para cada solicitud HTTP entrante, incluyendo solicitudes de actualización.
+
+Ejemplo con [`express-session`](https://www.npmjs.com/package/express-session):
+
+```js
+import session from "express-session";
+
+io.engine.use(session({
+ secret: "keyboard cat",
+ resave: false,
+ saveUninitialized: true,
+ cookie: { secure: true }
+}));
+```
+
+Ejemplo con [`helmet`](https://www.npmjs.com/package/helmet):
+
+```js
+import helmet from "helmet";
+
+io.engine.use(helmet());
+```
+
+Si el middleware debe aplicarse solo a la solicitud de handshake (y no a cada solicitud HTTP), puedes verificar la existencia del parámetro de consulta `sid`.
+
+Ejemplo con [`passport-jwt`](https://www.npmjs.com/package/passport-jwt):
+
+```js
+io.engine.use((req, res, next) => {
+ const isHandshake = req._query.sid === undefined;
+ if (isHandshake) {
+ passport.authenticate("jwt", { session: false })(req, res, next);
+ } else {
+ next();
+ }
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-initialization.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-initialization.md
new file mode 100644
index 00000000..00aca9a3
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-initialization.md
@@ -0,0 +1,741 @@
+---
+title: Inicialización del servidor
+sidebar_label: Inicialización
+sidebar_position: 2
+slug: /server-initialization/
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Una vez que hayas [instalado](server-installation.md) la biblioteca del servidor Socket.IO, ahora puedes inicializar el servidor. La lista completa de opciones se puede encontrar [aquí](../../server-options.md).
+
+:::tip
+
+Para usuarios de TypeScript, es posible proporcionar sugerencias de tipo para los eventos. Por favor revisa [esto](../01-Documentation/typescript.md).
+
+:::
+
+## Inicialización
+
+### Independiente
+
+
+
+
+```js
+const { Server } = require("socket.io");
+
+const io = new Server({ /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+io.listen(3000);
+```
+
+
+
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({ /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+io.listen(3000);
+```
+
+
+
+
+```ts
+import { Server } from "socket.io";
+
+const io = new Server({ /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+io.listen(3000);
+```
+
+
+
+
+También puedes pasar el puerto como primer argumento:
+
+
+
+
+```js
+const { Server } = require("socket.io");
+
+const io = new Server(3000, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+
+
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server(3000, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+
+
+
+```ts
+import { Server } from "socket.io";
+
+const io = new Server(3000, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+
+
+
+Esto inicia implícitamente un [servidor HTTP](https://nodejs.org/docs/latest/api/http.html#http_class_http_server) de Node.js, al cual se puede acceder a través de `io.httpServer`.
+
+### Con un servidor HTTP
+
+
+
+
+```js
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+
+const httpServer = createServer();
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```js
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```ts
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+### Con un servidor HTTPS
+
+
+
+
+```js
+const { readFileSync } = require("fs");
+const { createServer } = require("https");
+const { Server } = require("socket.io");
+
+const httpsServer = createServer({
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpsServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpsServer.listen(3000);
+```
+
+
+
+
+```js
+import { readFileSync } from "fs";
+import { createServer } from "https";
+import { Server } from "socket.io";
+
+const httpsServer = createServer({
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpsServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpsServer.listen(3000);
+```
+
+
+
+
+```ts
+import { readFileSync } from "fs";
+import { createServer } from "https";
+import { Server } from "socket.io";
+
+const httpsServer = createServer({
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpsServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpsServer.listen(3000);
+```
+
+
+
+
+Ver también: [documentación de Node.js](https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener)
+
+Con autenticación de certificado de cliente:
+
+*Servidor*
+
+```js
+import { readFileSync } from "fs";
+import { createServer } from "https";
+import { Server } from "socket.io";
+
+const httpsServer = createServer({
+ key: readFileSync("/path/to/server-key.pem"),
+ cert: readFileSync("/path/to/server-cert.pem"),
+ requestCert: true,
+ ca: [
+ readFileSync("/path/to/client-cert.pem")
+ ]
+});
+
+const io = new Server(httpsServer, { /* opciones */ });
+
+io.engine.on("connection", (rawSocket) => {
+ // si necesitas los detalles del certificado (ya no está disponible una vez completado el handshake)
+ rawSocket.peerCertificate = rawSocket.request.client.getPeerCertificate();
+});
+
+io.on("connection", (socket) => {
+ console.log(socket.conn.peerCertificate);
+ // ...
+});
+
+httpsServer.listen(3000);
+```
+
+*Cliente*
+
+```js
+import { readFileSync } from "fs";
+import { io } from "socket.io-client";
+
+const socket = io("https://example.com", {
+ key: readFileSync("/path/to/client-key.pem"),
+ cert: readFileSync("/path/to/client-cert.pem"),
+ ca: [
+ readFileSync("/path/to/server-cert.pem")
+ ]
+});
+```
+
+### Con un servidor HTTP/2
+
+
+
+
+```js
+const { readFileSync } = require("fs");
+const { createSecureServer } = require("http2");
+const { Server } = require("socket.io");
+
+const httpServer = createSecureServer({
+ allowHTTP1: true,
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```js
+import { readFileSync } from "fs";
+import { createSecureServer } from "http2";
+import { Server } from "socket.io";
+
+const httpServer = createSecureServer({
+ allowHTTP1: true,
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```ts
+import { readFileSync } from "fs";
+import { createSecureServer } from "http2";
+import { Server } from "socket.io";
+
+const httpServer = createSecureServer({
+ allowHTTP1: true,
+ key: readFileSync("/path/to/my/key.pem"),
+ cert: readFileSync("/path/to/my/cert.pem")
+});
+
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+Ver también: [documentación de Node.js](https://nodejs.org/api/http2.html#http2_http2_createsecureserver_options_onrequesthandler)
+
+### Con Express
+
+
+
+
+```js
+const express = require("express");
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+
+const app = express();
+const httpServer = createServer(app);
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```js
+import express from "express";
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const app = express();
+const httpServer = createServer(app);
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```ts
+import * as express from "express";
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const app = express();
+const httpServer = createServer(app);
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+:::caution
+
+Usar `app.listen(3000)` no funcionará aquí, ya que crea un nuevo servidor HTTP.
+
+:::
+
+Más información [aquí](http://expressjs.com/).
+
+### Con Koa
+
+
+
+
+```js
+const Koa = require("koa");
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+
+const app = new Koa();
+const httpServer = createServer(app.callback());
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```js
+import Koa from "koa";
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const app = new Koa();
+const httpServer = createServer(app.callback());
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+```ts
+import * as Koa from "koa";
+import { createServer } from "http";
+import { Server } from "socket.io";
+
+const app = new Koa();
+const httpServer = createServer(app.callback());
+const io = new Server(httpServer, { /* opciones */ });
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+httpServer.listen(3000);
+```
+
+
+
+
+Más información [aquí](https://koajs.com/).
+
+### Con Nest
+
+Ver la documentación [aquí](https://docs.nestjs.com/websockets/gateways).
+
+:::caution
+
+NestJS v7 y anteriores dependen de Socket.IO v2, mientras que NestJS v8 depende de Socket.IO v4. Por favor usa un [cliente compatible](../03-Client/client-installation.md#version-compatibility).
+
+:::
+
+### Con Fastify
+
+Necesitas registrar el plugin [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io):
+
+
+
+
+```js
+const fastify = require("fastify");
+const fastifyIO = require("fastify-socket.io");
+
+const server = fastify();
+server.register(fastifyIO);
+
+server.get("/", (req, reply) => {
+ server.io.emit("hello");
+});
+
+server.ready().then(() => {
+ // necesitamos esperar a que el servidor esté listo, de lo contrario `server.io` es undefined
+ server.io.on("connection", (socket) => {
+ // ...
+ });
+});
+
+server.listen({ port: 3000 });
+```
+
+
+
+
+```js
+import fastify from "fastify";
+import fastifyIO from "fastify-socket.io";
+
+const server = fastify();
+server.register(fastifyIO);
+
+server.get("/", (req, reply) => {
+ server.io.emit("hello");
+});
+
+server.ready().then(() => {
+ // necesitamos esperar a que el servidor esté listo, de lo contrario `server.io` es undefined
+ server.io.on("connection", (socket) => {
+ // ...
+ });
+});
+
+server.listen({ port: 3000 });
+```
+
+
+
+
+```ts
+import fastify from "fastify";
+import fastifyIO from "fastify-socket.io";
+
+const server = fastify();
+server.register(fastifyIO);
+
+server.get("/", (req, reply) => {
+ server.io.emit("hello");
+});
+
+server.ready().then(() => {
+ // necesitamos esperar a que el servidor esté listo, de lo contrario `server.io` es undefined
+ server.io.on("connection", (socket) => {
+ // ...
+ });
+});
+
+server.listen({ port: 3000 });
+```
+
+
+
+
+### Con µWebSockets.js {#with-uwebsocketsjs}
+
+```js
+import { App } from "uWebSockets.js";
+import { Server } from "socket.io";
+
+const app = App();
+const io = new Server();
+
+io.attachApp(app);
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+app.listen(3000, (token) => {
+ if (!token) {
+ console.warn("puerto ya en uso");
+ }
+});
+```
+
+Referencia: https://github.com/uNetworking/uWebSockets.js
+
+### Con Hono (Node.js)
+
+
+
+
+```js
+const { Hono } = require("hono");
+const { serve } = require("@hono/node-server");
+const { Server } = require("socket.io");
+
+const app = new Hono();
+
+const httpServer = serve({
+ fetch: app.fetch,
+ port: 3000,
+});
+
+const io = new Server(httpServer, {
+ /* opciones */
+});
+
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+
+
+
+```js
+import { Hono } from "hono";
+import { serve } from "@hono/node-server";
+import { Server } from "socket.io";
+
+const app = new Hono();
+
+const httpServer = serve({
+ fetch: app.fetch,
+ port: 3000,
+});
+
+const io = new Server(httpServer, {
+ /* opciones */
+});
+
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+
+
+
+```ts
+import { Hono } from "hono";
+import { serve } from "@hono/node-server";
+import { Server } from "socket.io";
+import type { Server as HTTPServer } from "node:http";
+
+const app = new Hono();
+
+const httpServer = serve({
+ fetch: app.fetch,
+ port: 3000,
+});
+
+const io = new Server(httpServer as HTTPServer, {
+ /* opciones */
+});
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+```
+
+
+
+
+Referencia: https://hono.dev/docs/
+
+### Con Hono & Bun
+
+```js
+import { Server as Engine } from "@socket.io/bun-engine";
+import { Server } from "socket.io";
+import { Hono } from "hono";
+
+const io = new Server();
+
+const engine = new Engine();
+
+io.bind(engine);
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+const app = new Hono();
+
+const { websocket } = engine.handler();
+
+export default {
+ port: 3000,
+ idleTimeout: 30, // debe ser mayor que la opción "pingInterval" del motor, que por defecto es 25 segundos
+
+ fetch(req, server) {
+ const url = new URL(req.url);
+
+ if (url.pathname === "/socket.io/") {
+ return engine.handleRequest(req, server);
+ } else {
+ return app.fetch(req, server);
+ }
+ },
+
+ websocket
+}
+```
+
+Referencia: https://hono.dev/docs/
+
+## Opciones
+
+La lista completa de opciones disponibles se puede encontrar [aquí](../../server-options.md).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-installation.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-installation.md
new file mode 100644
index 00000000..7420f230
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-installation.md
@@ -0,0 +1,359 @@
+---
+title: Instalación del servidor
+sidebar_label: Instalación
+sidebar_position: 1
+slug: /server-installation/
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info
+
+La última versión actualmente es `4.8.1`, lanzada en octubre de 2024.
+
+Puedes encontrar las notas de la versión [aquí](../../changelog/4.8.1.md).
+
+:::
+
+## Prerrequisitos
+
+Por favor asegúrate de que [Node.js](https://nodejs.org/es/) esté instalado en tu sistema. La versión actual de Soporte a Largo Plazo (LTS) es un punto de partida ideal, ver [aquí](https://github.com/nodejs/Release#release-schedule).
+
+:::info
+
+Se necesita al menos Node.js 10, las versiones anteriores ya no son soportadas.
+
+:::
+
+## Instalación
+
+Para instalar la última versión:
+
+
+
+
+```sh
+npm install socket.io
+```
+
+
+
+
+```sh
+yarn add socket.io
+```
+
+
+
+
+```sh
+pnpm add socket.io
+```
+
+
+
+
+```sh
+bun add socket.io
+```
+
+
+
+
+Para instalar una versión específica:
+
+
+
+
+```sh
+npm install socket.io@version
+```
+
+
+
+
+```sh
+yarn add socket.io@version
+```
+
+
+
+
+```sh
+pnpm add socket.io@version
+```
+
+
+
+
+```sh
+bun add socket.io@version
+```
+
+
+
+
+## Paquetes adicionales
+
+Por defecto, Socket.IO usa el servidor WebSocket proporcionado por el paquete [ws](https://www.npmjs.com/package/ws).
+
+Hay 2 paquetes opcionales que se pueden instalar junto con este paquete. Estos paquetes son complementos binarios que mejoran ciertas operaciones. Los binarios precompilados están disponibles para las plataformas más populares, así que no necesariamente necesitas tener un compilador C++ instalado en tu máquina.
+
+- [bufferutil](https://www.npmjs.com/package/bufferutil): Permite realizar eficientemente operaciones como enmascarar y desenmascarar la carga de datos de los frames WebSocket.
+- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate): Permite verificar eficientemente si un mensaje contiene UTF-8 válido como lo requiere la especificación.
+
+Para instalar esos paquetes:
+
+
+
+
+```sh
+npm install --save-optional bufferutil utf-8-validate
+```
+
+
+
+
+```sh
+yarn add --optional bufferutil utf-8-validate
+```
+
+
+
+
+```sh
+pnpm add -O bufferutil utf-8-validate
+```
+
+
+
+
+```sh
+bun add --optional bufferutil utf-8-validate
+```
+
+
+
+
+Por favor nota que estos paquetes son opcionales, el servidor WebSocket recurrirá a la implementación en JavaScript si no están disponibles. Más información se puede encontrar [aquí](https://github.com/websockets/ws/#opt-in-for-performance-and-spec-compliance).
+
+## Otras implementaciones de servidor WebSocket
+
+Cualquier implementación de servidor WebSocket que exponga la misma API que ws (notablemente el método [handleUpgrade](https://github.com/websockets/ws/blob/master/doc/ws.md#serverhandleupgraderequest-socket-head-callback)) puede ser usada.
+
+Por ejemplo, puedes usar el paquete [eiows](https://www.npmjs.com/package/eiows), que es un fork del paquete (ahora obsoleto) [uws](https://www.npmjs.com/package/uws):
+
+
+
+
+```sh
+npm install eiows
+```
+
+
+
+
+```sh
+yarn add eiows
+```
+
+
+
+
+```sh
+pnpm add eiows
+```
+
+
+
+
+```sh
+bun add eiows
+```
+
+
+
+
+Y luego usa la opción `wsEngine`:
+
+```js
+const { Server } = require("socket.io");
+const eiows = require("eiows");
+
+const io = new Server(3000, {
+ wsEngine: eiows.Server
+});
+```
+
+Esta implementación "permite, pero no garantiza" mejoras significativas de rendimiento y uso de memoria sobre la implementación predeterminada. Como siempre, por favor haz benchmarks contra tu propio uso.
+
+## Uso con `µWebSockets.js` {#usage-with-uwebsockets}
+
+A partir de la versión [4.4.0](/blog/socket-io-4-4-0/), un servidor Socket.IO ahora puede vincularse a un servidor [`µWebSockets.js`](https://github.com/uNetworking/uWebSockets.js).
+
+Instalación:
+
+
+
+
+```sh
+npm install uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0
+```
+
+
+
+
+```sh
+yarn add uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0
+```
+
+
+
+
+```sh
+pnpm add uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0
+```
+
+
+
+
+```sh
+bun add uWebSockets.js@uNetworking/uWebSockets.js#v20.52.0
+```
+
+
+
+
+Uso:
+
+```js
+const { App } = require("uWebSockets.js");
+const { Server } = require("socket.io");
+
+const app = App();
+const io = new Server();
+
+io.attachApp(app);
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+app.listen(3000, (token) => {
+ if (!token) {
+ console.warn("puerto ya en uso");
+ }
+});
+```
+
+## Uso con Bun
+
+El paquete `@socket.io/bun-engine` proporciona un motor de bajo nivel específico para Bun, destinado a aprovechar la velocidad y escalabilidad de Bun.
+
+### Instalación
+
+```
+bun add socket.io @socket.io/bun-engine
+```
+
+### Uso
+
+```js
+import { Server as Engine } from "@socket.io/bun-engine";
+import { Server } from "socket.io";
+
+const io = new Server();
+
+const engine = new Engine({
+ path: "/socket.io/",
+});
+
+io.bind(engine);
+
+io.on("connection", (socket) => {
+ // ...
+});
+
+export default {
+ port: 3000,
+ idleTimeout: 30, // debe ser mayor que la opción "pingInterval" del motor, que por defecto es 25 segundos
+
+ ...engine.handler(),
+};
+```
+
+:::tip
+
+Cualquier adaptador existente puede ser usado sin ninguna modificación.
+
+:::
+
+## Miscelánea
+
+### Árbol de dependencias
+
+Una instalación básica del servidor incluye **21** paquetes, de los cuales **6** son mantenidos por nuestro equipo:
+
+```
+└─┬ socket.io@4.8.1
+ ├─┬ accepts@1.3.8
+ │ ├─┬ mime-types@2.1.35
+ │ │ └── mime-db@1.52.0
+ │ └── negotiator@0.6.3
+ ├── base64id@2.0.0
+ ├─┬ cors@2.8.5
+ │ ├── object-assign@4.1.1
+ │ └── vary@1.1.2
+ ├─┬ debug@4.3.7
+ │ └── ms@2.1.3
+ ├─┬ engine.io@6.6.4
+ │ ├─┬ @types/cors@2.8.17
+ │ │ └── @types/node@22.13.9 deduped
+ │ ├─┬ @types/node@22.13.9
+ │ │ └── undici-types@6.20.0
+ │ ├── accepts@1.3.8 deduped
+ │ ├── base64id@2.0.0 deduped
+ │ ├── cookie@0.7.2
+ │ ├── cors@2.8.5 deduped
+ │ ├── debug@4.3.7 deduped
+ │ ├── engine.io-parser@5.2.3
+ │ └─┬ ws@8.17.1
+ │ ├── UNMET OPTIONAL DEPENDENCY bufferutil@^4.0.1
+ │ └── UNMET OPTIONAL DEPENDENCY utf-8-validate@>=5.0.2
+ ├─┬ socket.io-adapter@2.5.5
+ │ ├── debug@4.3.7 deduped
+ │ └── ws@8.17.1 deduped
+ └─┬ socket.io-parser@4.2.4
+ ├── @socket.io/component-emitter@3.1.2
+ └── debug@4.3.7 deduped
+```
+
+:::info
+
+Las declaraciones de tipos para paquetes de terceros están incluidas, para facilitar el uso de la biblioteca para usuarios de TypeScript (pero a costa de un paquete ligeramente más grande).
+
+Ver también: https://github.com/microsoft/types-publisher/issues/81#issuecomment-234051345
+
+:::
+
+
+### Versiones transitivas
+
+El paquete `engine.io` trae el motor que es responsable de gestionar las conexiones de bajo nivel (HTTP long-polling o WebSocket). Ver también: [Cómo funciona](../01-Documentation/how-it-works.md)
+
+| versión de `socket.io` | versión de `engine.io` | versión de `ws` |
+|------------------------|------------------------|-----------------|
+| `4.8.x` | `6.6.x` | `8.17.x` |
+| `4.7.x` | `6.5.x` | `8.17.x` |
+| `4.6.x` | `6.4.x` | `8.11.x` |
+| `4.5.x` | `6.2.x` | `8.2.x` |
+| `4.4.x` | `6.1.x` | `8.2.x` |
+| `4.3.x` | `6.0.x` | `8.2.x` |
+| `4.2.x` | `5.2.x` | `7.4.x` |
+| `4.1.x` | `5.1.x` | `7.4.x` |
+| `4.0.x` | `5.0.x` | `7.4.x` |
+| `3.1.x` | `4.1.x` | `7.4.x` |
+| `3.0.x` | `4.0.x` | `7.4.x` |
+| `2.5.x` | `3.6.x` | `7.5.x` |
+| `2.4.x` | `3.5.x` | `7.4.x` |
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-instance.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-instance.md
new file mode 100644
index 00000000..8071e6a4
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-instance.md
@@ -0,0 +1,277 @@
+---
+title: La instancia del Servidor
+sidebar_position: 3
+slug: /server-instance/
+---
+
+La instancia del Servidor (a menudo llamada `io` en los ejemplos de código) tiene algunos atributos que pueden ser útiles en tu aplicación.
+
+También hereda todos los métodos del [namespace principal](../06-Advanced/namespaces.md#main-namespace), como [`namespace.use()`](../../server-api.md#namespaceusefn) (ver [aquí](middlewares.md)) o [`namespace.allSockets()`](../../server-api.md#namespaceallsockets).
+
+## Server#engine
+
+Una referencia al servidor Engine.IO subyacente.
+
+Puede usarse para obtener el número de clientes conectados actualmente:
+
+```js
+const count = io.engine.clientsCount;
+// puede o no ser similar al conteo de instancias Socket en el namespace principal, dependiendo de tu uso
+const count2 = io.of("/").sockets.size;
+```
+
+O para generar un ID de sesión personalizado (el parámetro de consulta `sid`):
+
+```js
+const uuid = require("uuid");
+
+io.engine.generateId = (req) => {
+ return uuid.v4(); // debe ser único en todos los servidores Socket.IO
+}
+```
+
+A partir de `socket.io@4.1.0`, el servidor Engine.IO emite tres eventos especiales:
+
+- `initial_headers`: se emitirá justo antes de escribir los encabezados de respuesta de la primera solicitud HTTP de la sesión (el handshake), permitiéndote personalizarlos.
+
+```js
+io.engine.on("initial_headers", (headers, req) => {
+ headers["test"] = "123";
+ headers["set-cookie"] = "mycookie=456";
+});
+```
+
+- `headers`: se emitirá justo antes de escribir los encabezados de respuesta de cada solicitud HTTP de la sesión (incluyendo la actualización a WebSocket), permitiéndote personalizarlos.
+
+```js
+io.engine.on("headers", (headers, req) => {
+ headers["test"] = "789";
+});
+```
+
+- `connection_error`: se emitirá cuando una conexión se cierra anormalmente
+
+```js
+io.engine.on("connection_error", (err) => {
+ console.log(err.req); // el objeto de solicitud
+ console.log(err.code); // el código de error, por ejemplo 1
+ console.log(err.message); // el mensaje de error, por ejemplo "Session ID unknown"
+ console.log(err.context); // algún contexto adicional del error
+});
+```
+
+Aquí está la lista de posibles códigos de error:
+
+| Código | Mensaje |
+|:------:|:------------------------------:|
+| 0 | "Transport unknown" |
+| 1 | "Session ID unknown" |
+| 2 | "Bad handshake method" |
+| 3 | "Bad request" |
+| 4 | "Forbidden" |
+| 5 | "Unsupported protocol version" |
+
+## Métodos de utilidad
+
+Se añadieron algunos métodos de utilidad en Socket.IO v4.0.0 para gestionar las instancias de Socket y sus salas:
+
+- [`socketsJoin`](#socketsjoin): hace que las instancias de socket coincidentes se unan a las salas especificadas
+- [`socketsLeave`](#socketsleave): hace que las instancias de socket coincidentes abandonen las salas especificadas
+- [`disconnectSockets`](#disconnectsockets): hace que las instancias de socket coincidentes se desconecten
+- [`fetchSockets`](#fetchsockets): devuelve las instancias de socket coincidentes
+
+El método [`serverSideEmit`](#serversideemit) fue añadido en Socket.IO v4.1.0.
+
+Esos métodos comparten la misma semántica que broadcasting, y se aplican los mismos filtros:
+
+```js
+io.of("/admin").in("room1").except("room2").local.disconnectSockets();
+```
+
+Lo cual hace que todas las instancias de Socket del namespace "admin"
+
+- en la sala "room1" (`in("room1")` o `to("room1")`)
+- excepto las que están en "room2" (`except("room2")`)
+- y solo en el servidor Socket.IO actual (`local`)
+
+se desconecten.
+
+Por favor nota que también son compatibles con el adaptador Redis (a partir de `socket.io-redis@6.1.0`), lo que significa que funcionarán a través de servidores Socket.IO.
+
+### `socketsJoin`
+
+Este método hace que las instancias de Socket coincidentes se unan a las salas especificadas:
+
+```js
+// hacer que todas las instancias de Socket se unan a la sala "room1"
+io.socketsJoin("room1");
+
+// hacer que todas las instancias de Socket en la sala "room1" se unan a las salas "room2" y "room3"
+io.in("room1").socketsJoin(["room2", "room3"]);
+
+// hacer que todas las instancias de Socket en la sala "room1" del namespace "admin" se unan a la sala "room2"
+io.of("/admin").in("room1").socketsJoin("room2");
+
+// esto también funciona con un solo ID de socket
+io.in(theSocketId).socketsJoin("room1");
+```
+
+### `socketsLeave`
+
+Este método hace que las instancias de Socket coincidentes abandonen las salas especificadas:
+
+```js
+// hacer que todas las instancias de Socket abandonen la sala "room1"
+io.socketsLeave("room1");
+
+// hacer que todas las instancias de Socket en la sala "room1" abandonen las salas "room2" y "room3"
+io.in("room1").socketsLeave(["room2", "room3"]);
+
+// hacer que todas las instancias de Socket en la sala "room1" del namespace "admin" abandonen la sala "room2"
+io.of("/admin").in("room1").socketsLeave("room2");
+
+// esto también funciona con un solo ID de socket
+io.in(theSocketId).socketsLeave("room1");
+```
+
+### `disconnectSockets`
+
+Este método hace que las instancias de Socket coincidentes se desconecten:
+
+```js
+// hacer que todas las instancias de Socket se desconecten
+io.disconnectSockets();
+
+// hacer que todas las instancias de Socket en la sala "room1" se desconecten (y descartar la conexión de bajo nivel)
+io.in("room1").disconnectSockets(true);
+
+// hacer que todas las instancias de Socket en la sala "room1" del namespace "admin" se desconecten
+io.of("/admin").in("room1").disconnectSockets();
+
+// esto también funciona con un solo ID de socket
+io.of("/admin").in(theSocketId).disconnectSockets();
+```
+
+### `fetchSockets`
+
+Este método devuelve las instancias de Socket coincidentes:
+
+```js
+// devolver todas las instancias de Socket del namespace principal
+const sockets = await io.fetchSockets();
+
+// devolver todas las instancias de Socket en la sala "room1" del namespace principal
+const sockets = await io.in("room1").fetchSockets();
+
+// devolver todas las instancias de Socket en la sala "room1" del namespace "admin"
+const sockets = await io.of("/admin").in("room1").fetchSockets();
+
+// esto también funciona con un solo ID de socket
+const sockets = await io.in(theSocketId).fetchSockets();
+```
+
+La variable `sockets` en el ejemplo anterior es un array de objetos que exponen un subconjunto de la clase Socket habitual:
+
+```js
+for (const socket of sockets) {
+ console.log(socket.id);
+ console.log(socket.handshake);
+ console.log(socket.rooms);
+ console.log(socket.data);
+ socket.emit(/* ... */);
+ socket.join(/* ... */);
+ socket.leave(/* ... */);
+ socket.disconnect(/* ... */);
+}
+```
+
+El atributo `data` es un objeto arbitrario que puede usarse para compartir información entre servidores Socket.IO:
+
+```js
+// servidor A
+io.on("connection", (socket) => {
+ socket.data.username = "alice";
+});
+
+// servidor B
+const sockets = await io.fetchSockets();
+console.log(sockets[0].data.username); // "alice"
+```
+
+### `serverSideEmit`
+
+Este método permite emitir eventos a los otros servidores Socket.IO del clúster, en una [configuración de múltiples servidores](using-multiple-nodes.md).
+
+Sintaxis:
+
+```js
+io.serverSideEmit("hello", "world");
+```
+
+Y en el lado receptor:
+
+```js
+io.on("hello", (arg1) => {
+ console.log(arg1); // imprime "world"
+});
+```
+
+Las confirmaciones también son soportadas:
+
+```js
+// servidor A
+io.serverSideEmit("ping", (err, responses) => {
+ console.log(responses[0]); // imprime "pong"
+});
+
+// servidor B
+io.on("ping", (cb) => {
+ cb("pong");
+});
+```
+
+Notas:
+
+- las cadenas `connection`, `connect` y `new_namespace` están reservadas y no pueden usarse en tu aplicación.
+
+- puedes enviar cualquier número de argumentos, pero las estructuras binarias actualmente no son soportadas (el array de argumentos será `JSON.stringify`)
+
+Ejemplo:
+
+```js
+io.serverSideEmit("hello", "world", 1, "2", { 3: "4" });
+```
+
+- el callback de confirmación podría ser llamado con un error, si los otros servidores Socket.IO no responden después de un retraso dado
+
+```js
+io.serverSideEmit("ping", (err, responses) => {
+ if (err) {
+ // al menos un servidor Socket.IO no ha respondido
+ // el array 'responses' contiene todas las respuestas ya recibidas sin embargo
+ } else {
+ // ¡éxito! el array 'responses' contiene un objeto por cada otro servidor Socket.IO en el clúster
+ }
+});
+```
+
+
+## Eventos
+
+La instancia del Servidor emite un solo evento (bueno, técnicamente dos, pero `connect` es un alias de `connection`):
+
+- [`connection`](#connection)
+
+### `connection`
+
+Este evento se dispara ante una nueva conexión. El primer argumento es una [instancia de Socket](server-socket-instance.md).
+
+```js
+io.on("connection", (socket) => {
+ // ...
+});
+```
+
+## API completa
+
+La API completa expuesta por la instancia del Servidor se puede encontrar [aquí](../../server-api.md#server).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-socket-instance.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-socket-instance.md
new file mode 100644
index 00000000..ef99843c
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-socket-instance.md
@@ -0,0 +1,291 @@
+---
+title: La instancia de Socket (lado del servidor)
+sidebar_label: La instancia de Socket
+sidebar_position: 4
+slug: /server-socket-instance/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Un `Socket` es la clase fundamental para interactuar con el cliente. Hereda todos los métodos del [EventEmitter](https://nodejs.org/api/events.html#class-eventemitter) de Node.js, como [emit](../../server-api.md#socketemiteventname-args), [on](../../server-api.md#socketoneventname-callback), [once](../../server-api.md#socketonceeventname-listener) o [removeListener](../../server-api.md#socketremovelistenereventname-listener).
+
+
+
+
+
+
+Además de:
+
+- [emitir](../04-Events/emitting-events.md#basic-emit) y [escuchar](../04-Events/listening-to-events.md) eventos
+- [transmitir eventos](../04-Events/broadcasting-events.md#to-all-connected-clients-except-the-sender)
+- [unirse y abandonar salas](../04-Events/rooms.md#joining-and-leaving)
+
+La instancia de Socket tiene algunos atributos que pueden ser útiles en tu aplicación:
+
+## Socket#id
+
+A cada nueva conexión se le asigna un identificador aleatorio de 20 caracteres.
+
+Este identificador está sincronizado con el valor en el lado del cliente.
+
+```js
+// lado del servidor
+io.on("connection", (socket) => {
+ console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
+});
+
+// lado del cliente
+socket.on("connect", () => {
+ console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
+});
+```
+
+:::caution
+
+Por favor nota que, a menos que la [recuperación del estado de conexión](../01-Documentation/connection-state-recovery.md) esté habilitada, el atributo `id` es un ID **efímero** que no está destinado a ser usado en tu aplicación (o solo para propósitos de depuración) porque:
+
+- este ID se regenera después de cada reconexión (por ejemplo cuando la conexión WebSocket se corta, o cuando el usuario actualiza la página)
+- dos pestañas diferentes del navegador tendrán dos IDs diferentes
+- no hay cola de mensajes almacenada para un ID dado en el servidor (es decir, si el cliente está desconectado, los mensajes enviados desde el servidor a este ID se pierden)
+
+Por favor usa un ID de sesión regular en su lugar (ya sea enviado en una cookie, o almacenado en localStorage y enviado en el payload de [`auth`](../../client-options.md#auth)).
+
+Ver también:
+
+- [Parte II de nuestra guía de mensajes privados](/get-started/private-messaging-part-2/)
+- [Cómo lidiar con cookies](/how-to/deal-with-cookies)
+
+:::
+
+Nota: no puedes sobrescribir este identificador, ya que se usa en varias partes del código base de Socket.IO.
+
+## Socket#handshake
+
+Este objeto contiene algunos detalles sobre el handshake que ocurre al comienzo de la sesión Socket.IO.
+
+```
+{
+ headers: /* los encabezados de la solicitud inicial */
+ query: /* los parámetros de consulta de la solicitud inicial */
+ auth: /* el payload de autenticación */
+ time: /* la fecha de creación (como cadena) */
+ issued: /* la fecha de creación (timestamp unix) */
+ url: /* la cadena URL de la solicitud */
+ address: /* la ip del cliente */
+ xdomain: /* si la conexión es de dominio cruzado */
+ secure: /* si la conexión es segura */
+}
+```
+
+Ejemplo:
+
+```json
+{
+ "headers": {
+ "user-agent": "xxxx",
+ "accept": "*/*",
+ "host": "example.com",
+ "connection": "close"
+ },
+ "query": {
+ "EIO": "4",
+ "transport": "polling",
+ "t": "NNjNltH"
+ },
+ "auth": {
+ "token": "123"
+ },
+ "time": "Sun Nov 22 2020 01:33:46 GMT+0100 (Central European Standard Time)",
+ "issued": 1606005226969,
+ "url": "/socket.io/?EIO=4&transport=polling&t=NNjNltH",
+ "address": "::ffff:1.2.3.4",
+ "xdomain": false,
+ "secure": true
+}
+```
+
+## Socket#rooms
+
+Esta es una referencia a las [salas](../04-Events/rooms.md) en las que el Socket está actualmente.
+
+```js
+io.on("connection", (socket) => {
+ console.log(socket.rooms); // Set { }
+ socket.join("room1");
+ console.log(socket.rooms); // Set { , "room1" }
+});
+```
+
+## Socket#data
+
+Un objeto arbitrario que puede usarse junto con el método de utilidad `fetchSockets()`:
+
+```js
+// servidor A
+io.on("connection", (socket) => {
+ socket.data.username = "alice";
+});
+
+// servidor B
+const sockets = await io.fetchSockets();
+console.log(sockets[0].data.username); // "alice"
+```
+
+Más información [aquí](server-instance.md#utility-methods).
+
+## Socket#conn
+
+Una referencia al socket Engine.IO subyacente (ver [aquí](../01-Documentation/how-it-works.md)).
+
+```js
+io.on("connection", (socket) => {
+ console.log("transporte inicial", socket.conn.transport.name); // imprime "polling"
+
+ socket.conn.once("upgrade", () => {
+ // se llama cuando el transporte se actualiza (es decir, de HTTP long-polling a WebSocket)
+ console.log("transporte actualizado", socket.conn.transport.name); // imprime "websocket"
+ });
+
+ socket.conn.on("packet", ({ type, data }) => {
+ // se llama por cada paquete recibido
+ });
+
+ socket.conn.on("packetCreate", ({ type, data }) => {
+ // se llama por cada paquete enviado
+ });
+
+ socket.conn.on("drain", () => {
+ // se llama cuando el búfer de escritura se vacía
+ });
+
+ socket.conn.on("close", (reason) => {
+ // se llama cuando la conexión subyacente se cierra
+ });
+});
+```
+
+## Atributos adicionales
+
+Siempre que no sobrescribas ningún atributo existente, puedes adjuntar cualquier atributo a la instancia de Socket y usarlo más tarde:
+
+```js
+// en un middleware
+io.use(async (socket, next) => {
+ try {
+ const user = await fetchUser(socket);
+ socket.user = user;
+ } catch (e) {
+ next(new Error("usuario desconocido"));
+ }
+});
+
+io.on("connection", (socket) => {
+ console.log(socket.user);
+
+ // en un listener
+ socket.on("set username", (username) => {
+ socket.username = username;
+ });
+});
+
+```
+
+## Middlewares de Socket
+
+Estos middlewares se parecen mucho a los [middlewares](middlewares.md) habituales, excepto que se llaman por cada paquete entrante:
+
+```js
+socket.use(([event, ...args], next) => {
+ // hacer algo con el paquete (registro, autorización, limitación de tasa...)
+ // no olvides llamar a next() al final
+ next();
+});
+```
+
+El método `next` también puede ser llamado con un objeto de error. En ese caso, el evento no llegará a los manejadores de eventos registrados y se emitirá un evento `error` en su lugar:
+
+```js
+io.on("connection", (socket) => {
+ socket.use(([event, ...args], next) => {
+ if (isUnauthorized(event)) {
+ return next(new Error("evento no autorizado"));
+ }
+ next();
+ });
+
+ socket.on("error", (err) => {
+ if (err && err.message === "evento no autorizado") {
+ socket.disconnect();
+ }
+ });
+});
+```
+
+Nota: esta característica solo existe en el lado del servidor. Para el lado del cliente, podrías estar interesado en [listeners catch-all](../04-Events/listening-to-events.md#catch-all-listeners).
+
+## Eventos
+
+En el lado del servidor, la instancia de Socket emite dos eventos especiales:
+
+- [`disconnect`](#disconnect)
+- [`disconnecting`](#disconnecting)
+
+### `disconnect`
+
+Este evento es disparado por la instancia de Socket al desconectarse.
+
+```js
+io.on("connection", (socket) => {
+ socket.on("disconnect", (reason) => {
+ // ...
+ });
+});
+```
+
+Aquí está la lista de posibles razones:
+
+| Razón | Descripción |
+|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------|
+| `server namespace disconnect` | El socket fue desconectado forzosamente con [socket.disconnect()](server-api.md#socketdisconnectclose). |
+| `client namespace disconnect` | El cliente ha desconectado manualmente el socket usando [socket.disconnect()](client-api.md#socketdisconnect). |
+| `server shutting down` | El servidor está, bueno, apagándose. |
+| `ping timeout` | El cliente no envió un paquete PONG en el retraso `pingTimeout`. |
+| `transport close` | La conexión fue cerrada (ejemplo: el usuario ha perdido la conexión, o la red cambió de WiFi a 4G). |
+| `transport error` | La conexión ha encontrado un error. |
+| `parse error` | El servidor ha recibido un paquete inválido del cliente. |
+| `forced close` | El servidor ha recibido un paquete inválido del cliente. |
+| `forced server close` | El cliente no se unió a un namespace a tiempo (ver la opción [`connectTimeout`](server-options.md#connecttimeout)) y fue cerrado forzosamente. |
+
+### `disconnecting`
+
+Este evento es similar a `disconnect` pero se dispara un poco antes, cuando el set [Socket#rooms](server-socket-instance.md#socketrooms) aún no está vacío
+
+```js
+io.on("connection", (socket) => {
+ socket.on("disconnecting", (reason) => {
+ for (const room of socket.rooms) {
+ if (room !== socket.id) {
+ socket.to(room).emit("el usuario se ha ido", socket.id);
+ }
+ }
+ });
+});
+```
+
+Nota: estos eventos, junto con `connect`, `connect_error`, `newListener` y `removeListener`, son eventos especiales que no deberían usarse en tu aplicación:
+
+```js
+// MAL, lanzará un error
+socket.emit("disconnect");
+```
+
+## API completa
+
+La API completa expuesta por la instancia de Socket se puede encontrar [aquí](../../server-api.md#socket).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-structure.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-structure.md
new file mode 100644
index 00000000..51b27cd6
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-structure.md
@@ -0,0 +1,92 @@
+---
+title: Estructura de la aplicación
+sidebar_position: 9
+slug: /server-application-structure/
+---
+
+## Registrar manejadores de eventos
+
+A continuación encontrarás dos sugerencias sobre cómo registrar tus manejadores de eventos.
+
+Por favor nota que estas son meramente sugerencias y no pautas estrictas que debas seguir. ¡Por favor adáptalas a tu gusto!
+
+### Cada archivo registra sus propios manejadores de eventos
+
+Aquí, el punto de entrada se mantiene ordenado, pero los listeners de eventos pueden ser menos descubribles (aunque una convención de nombres sólida/ctrl+f ayudará).
+
+`index.js`
+
+```js
+const httpServer = require("http").createServer();
+const io = require("socket.io")(httpServer);
+
+const registerOrderHandlers = require("./orderHandler");
+const registerUserHandlers = require("./userHandler");
+
+const onConnection = (socket) => {
+ registerOrderHandlers(io, socket);
+ registerUserHandlers(io, socket);
+}
+
+io.on("connection", onConnection);
+```
+
+`orderHandler.js`
+
+```js
+module.exports = (io, socket) => {
+ const createOrder = (payload) => {
+ // ...
+ }
+
+ const readOrder = (orderId, callback) => {
+ // ...
+ }
+
+ socket.on("order:create", createOrder);
+ socket.on("order:read", readOrder);
+}
+```
+
+### Todos los manejadores de eventos se registran en el archivo `index.js`
+
+Aquí, cada nombre de evento se encuentra en el mismo lugar, lo cual es excelente para la descubribilidad, pero podría salirse de control en una aplicación mediana/grande.
+
+`index.js`
+
+```js
+const httpServer = require("http").createServer();
+const io = require("socket.io")(httpServer);
+
+const { createOrder, readOrder } = require("./orderHandler")(io);
+const { updatePassword } = require("./userHandler")(io);
+
+const onConnection = (socket) => {
+ socket.on("order:create", createOrder);
+ socket.on("order:read", readOrder);
+
+ socket.on("user:update-password", updatePassword);
+}
+
+io.on("connection", onConnection);
+```
+
+`orderHandler.js`
+
+```js
+module.exports = (io) => {
+ const createOrder = function (payload) {
+ const socket = this; // de ahí la 'function' arriba, ya que una arrow function no funcionará
+ // ...
+ };
+
+ const readOrder = function (orderId, callback) {
+ // ...
+ };
+
+ return {
+ createOrder,
+ readOrder
+ }
+}
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-with-bundlers.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-with-bundlers.md
new file mode 100644
index 00000000..56d72bc8
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/server-with-bundlers.md
@@ -0,0 +1,128 @@
+---
+title: Uso con bundlers
+sidebar_position: 10
+slug: /server-with-bundlers/
+---
+
+Aunque menos común que el bundling del frontend, es totalmente posible crear un bundle para el servidor.
+
+## Webpack 5
+
+### Sin servir los archivos del cliente
+
+Instalación:
+
+```
+npm install -D webpack webpack-cli socket.io bufferutil utf-8-validate
+```
+
+`index.js`
+
+```js
+const { Server } = require("socket.io");
+
+const io = new Server({
+ serveClient: false
+});
+
+io.on("connection", socket => {
+ console.log(`conectado ${socket.id}`);
+
+ socket.on("disconnect", (reason) => {
+ console.log(`desconectado ${socket.id} debido a ${reason}`);
+ });
+});
+
+io.listen(3000);
+```
+
+`webpack.config.js`
+
+```js
+const path = require("path");
+
+module.exports = {
+ entry: "./index.js",
+ target: "node",
+ mode: "production",
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "index.js",
+ }
+};
+```
+
+Nota: `bufferutil` y `utf-8-validate` son dos dependencias opcionales del paquete `ws`. También puedes establecerlas como "external" con:
+
+```js
+const path = require("path");
+
+module.exports = {
+ entry: "./index.js",
+ target: "node",
+ mode: "production",
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "index.js",
+ },
+ externals: {
+ bufferutil: "bufferutil",
+ "utf-8-validate": "utf-8-validate",
+ },
+};
+```
+
+Documentación: https://webpack.js.org/configuration/externals/
+
+### Incluyendo servir los archivos del cliente
+
+En ese caso, tendremos que usar [Asset modules](https://webpack.js.org/guides/asset-modules/) y sobrescribir la función `sendFile` del servidor Socket.IO:
+
+`index.js`
+
+```js
+const { Server } = require("socket.io");
+
+const clientFile = require("./node_modules/socket.io/client-dist/socket.io.min?raw");
+const clientMap = require("./node_modules/socket.io/client-dist/socket.io.min.js.map?raw");
+
+Server.sendFile = (filename, req, res) => {
+ res.end(filename.endsWith(".map") ? clientMap : clientFile);
+};
+
+const io = new Server();
+
+io.on("connection", socket => {
+ console.log(`conectado ${socket.id}`);
+
+ socket.on("disconnect", (reason) => {
+ console.log(`desconectado ${socket.id} debido a ${reason}`);
+ });
+});
+
+io.listen(3000);
+```
+
+`webpack.config.js`
+
+```js
+const path = require("path");
+
+module.exports = {
+ entry: "./index.js",
+ target: "node",
+ mode: "production",
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ filename: "index.js",
+ },
+ module: {
+ rules: [
+ {
+ resourceQuery: /raw/,
+ type: "asset/source",
+ },
+ ],
+ },
+};
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/using-multiple-nodes.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/using-multiple-nodes.md
new file mode 100644
index 00000000..337eeffd
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/02-Server/using-multiple-nodes.md
@@ -0,0 +1,383 @@
+---
+title: Usando múltiples nodos
+sidebar_position: 7
+slug: /using-multiple-nodes/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Al desplegar múltiples servidores Socket.IO, hay dos cosas que tener en cuenta:
+
+- habilitar sesiones sticky, si HTTP long-polling está habilitado (que es el predeterminado): ver [abajo](#habilitando-sesiones-sticky)
+- usar un adaptador compatible, ver [aquí](../05-Adapters/adapter.md)
+
+## Balanceo de carga sticky
+
+Si planeas distribuir la carga de conexiones entre diferentes procesos o máquinas, tienes que asegurarte de que todas las solicitudes asociadas con un ID de sesión particular lleguen al proceso que las originó.
+
+### Por qué se requiere sesión sticky
+
+Esto es porque el transporte HTTP long-polling envía múltiples solicitudes HTTP durante la vida útil de la sesión Socket.IO.
+
+De hecho, Socket.IO podría técnicamente funcionar sin sesiones sticky, con la siguiente sincronización (en líneas punteadas):
+
+
+
+Aunque obviamente es posible de implementar, creemos que este proceso de sincronización entre los servidores Socket.IO resultaría en un gran impacto en el rendimiento de tu aplicación.
+
+Notas:
+
+- sin habilitar sesión sticky, experimentarás errores HTTP 400 debido a "Session ID unknown"
+- el transporte WebSocket no tiene esta limitación, ya que depende de una única conexión TCP para toda la sesión. Lo que significa que si deshabilitas el transporte HTTP long-polling (que es una elección perfectamente válida en 2021), no necesitarás sesiones sticky:
+
+```js
+const socket = io("https://io.yourhost.com", {
+ // ADVERTENCIA: en ese caso, no hay fallback a long-polling
+ transports: [ "websocket" ] // o [ "websocket", "polling" ] (el orden importa)
+});
+```
+
+Documentación: [`transports`](../../client-options.md#transports)
+
+### Habilitando sesiones sticky
+
+Para lograr sesión sticky, hay dos soluciones principales:
+
+- enrutar clientes basándose en una cookie (solución recomendada)
+- enrutar clientes basándose en su dirección de origen
+
+Encontrarás a continuación algunos ejemplos con soluciones comunes de balanceo de carga:
+
+- [nginx](#configuración-de-nginx) (basado en IP)
+- [nginx Ingress (Kubernetes)](#nginx-ingress-kubernetes) (basado en IP)
+- [Apache HTTPD](#configuración-de-apache-httpd) (basado en cookie)
+- [HAProxy](#configuración-de-haproxy) (basado en cookie)
+- [Traefik](#traefik) (basado en cookie)
+- [Módulo `cluster` de Node.js](#usando-cluster-de-nodejs)
+
+Para otras plataformas, por favor consulta la documentación relevante:
+
+- Kubernetes: https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/
+- AWS (Application Load Balancers): https://docs.aws.amazon.com/elasticloadbalancing/latest/application/sticky-sessions.html
+- GCP: https://cloud.google.com/load-balancing/docs/backend-service#session_affinity
+- Heroku: https://devcenter.heroku.com/articles/session-affinity
+
+**Nota importante**: si estás en una situación de CORS (el dominio del frontend es diferente del dominio del servidor) y la afinidad de sesión se logra con una cookie, necesitas permitir credenciales:
+
+*Servidor*
+
+```js
+const io = require("socket.io")(httpServer, {
+ cors: {
+ origin: "https://front-domain.com",
+ methods: ["GET", "POST"],
+ credentials: true
+ }
+});
+```
+
+*Cliente*
+
+```js
+const io = require("socket.io-client");
+const socket = io("https://server-domain.com", {
+ withCredentials: true
+});
+```
+
+Sin esto, la cookie no será enviada por el navegador y experimentarás respuestas HTTP 400 "Session ID unknown". Más información [aquí](https://developer.mozilla.org/es/docs/Web/API/XMLHttpRequest/withCredentials).
+
+### Configuración de nginx
+
+Dentro de la sección `http { }` de tu archivo `nginx.conf`, puedes declarar una sección `upstream` con una lista de procesos Socket.IO entre los cuales quieres balancear la carga:
+
+```nginx
+http {
+ server {
+ listen 3000;
+ server_name io.yourhost.com;
+
+ location / {
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header Host $host;
+
+ proxy_pass http://nodes;
+
+ # habilitar WebSockets
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "upgrade";
+ }
+ }
+
+ upstream nodes {
+ # habilitar sesión sticky con "hash" (usa la dirección IP completa)
+ hash $remote_addr consistent;
+ # o "ip_hash" (usa los primeros tres octetos de la dirección IPv4 del cliente, o la dirección IPv6 completa)
+ # ip_hash;
+ # o "sticky" (necesita suscripción comercial)
+ # sticky cookie srv_id expires=1h domain=.example.com path=/;
+
+ server app01:3000;
+ server app02:3000;
+ server app03:3000;
+ }
+}
+```
+
+Nota la instrucción `hash` que indica que las conexiones serán sticky.
+
+Asegúrate de también configurar `worker_processes` en el nivel más alto para indicar cuántos workers debe usar nginx. También podrías querer ajustar la configuración `worker_connections` dentro del bloque `events { }`.
+
+Enlaces:
+
+- [Ejemplo](https://github.com/socketio/socket.io/tree/main/examples/cluster-nginx)
+- [Documentación de nginx](http://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash)
+
+:::caution
+
+El valor de [`proxy_read_timeout`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout) de nginx (60 segundos por defecto) debe ser mayor que [`pingInterval + pingTimeout`](../../server-options.md#pinginterval) de Socket.IO (45 segundos por defecto), de lo contrario nginx cerrará forzosamente la conexión si no se envían datos después del retraso dado y el cliente obtendrá un error "transport close".
+
+:::
+
+### nginx Ingress (Kubernetes)
+
+Dentro de la sección `annotations` de tu configuración de Ingress, puedes declarar un hash upstream basado en la dirección IP del cliente, para que el controlador Ingress siempre asigne las solicitudes de una dirección IP dada al mismo pod:
+
+```yaml
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: your-ingress
+ namespace: your-namespace
+ annotations:
+ nginx.ingress.kubernetes.io/configuration-snippet: |
+ set $forwarded_client_ip "";
+ if ($http_x_forwarded_for ~ "^([^,]+)") {
+ set $forwarded_client_ip $1;
+ }
+ set $client_ip $remote_addr;
+ if ($forwarded_client_ip != "") {
+ set $client_ip $forwarded_client_ip;
+ }
+ nginx.ingress.kubernetes.io/upstream-hash-by: "$client_ip"
+spec:
+ ingressClassName: nginx
+ rules:
+ - host: io.yourhost.com
+ http:
+ paths:
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: your-service
+ port:
+ number: 80
+```
+
+Notas:
+
+- `nginx.ingress.kubernetes.io/upstream-hash-by: "$client_ip"`
+
+Esta anotación instruye al controlador NGINX Ingress a usar la dirección IP del cliente para enrutar el tráfico entrante a un Pod específico en tu clúster de Kubernetes. Esto es crucial para mantener sesiones sticky.
+
+- `nginx.ingress.kubernetes.io/configuration-snippet`
+
+Este fragmento de configuración NGINX personalizado sirve un doble propósito:
+
+1. Si la solicitud pasa a través de proxies inversos o API gateways upstream que añaden un encabezado `X-Forwarded-For`, este fragmento extrae la primera dirección IP de ese encabezado y la usa para actualizar $client_ip.
+
+2. En ausencia de tales proxies o gateways, el fragmento simplemente usa remote_addr, que es la dirección IP del cliente directamente conectado al ingress.
+
+Esto asegura que la IP del cliente correcta se use para la lógica de sesión sticky, habilitada por la anotación `nginx.ingress.kubernetes.io/upstream-hash-by: "$client_ip"`. El fragmento es particularmente importante cuando tu arquitectura incluye componentes de red upstream como proxies inversos o API gateways.
+
+Enlaces:
+
+- [Documentación de Ingress Nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#custom-nginx-upstream-hashing)
+- [Encabezado X-Forwarded-For](https://developer.mozilla.org/es/docs/Web/HTTP/Headers/X-Forwarded-For)
+
+### Configuración de Apache HTTPD
+
+```apache
+Header add Set-Cookie "SERVERID=sticky.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
+
+
+ BalancerMember "http://app01:3000" route=app01
+ BalancerMember "http://app02:3000" route=app02
+ BalancerMember "http://app03:3000" route=app03
+ ProxySet stickysession=SERVERID
+
+
+
+ BalancerMember "ws://app01:3000" route=app01
+ BalancerMember "ws://app02:3000" route=app02
+ BalancerMember "ws://app03:3000" route=app03
+ ProxySet stickysession=SERVERID
+
+
+RewriteEngine On
+RewriteCond %{HTTP:Upgrade} =websocket [NC]
+RewriteRule /(.*) balancer://nodes_ws/$1 [P,L]
+RewriteCond %{HTTP:Upgrade} !=websocket [NC]
+RewriteRule /(.*) balancer://nodes_polling/$1 [P,L]
+
+# debe ser mayor que pingInterval (25s por defecto) + pingTimeout (20s por defecto)
+ProxyTimeout 60
+```
+
+Enlaces:
+
+- [Ejemplo](https://github.com/socketio/socket.io/tree/main/examples/cluster-httpd)
+- [Documentación](https://httpd.apache.org/docs/2.4/en/mod/mod_proxy.html#proxypass)
+
+### Configuración de HAProxy
+
+```
+# Referencia: http://blog.haproxy.com/2012/11/07/websockets-load-balancing-with-haproxy/
+
+listen chat
+ bind *:80
+ default_backend nodes
+
+backend nodes
+ option httpchk HEAD /health
+ http-check expect status 200
+ cookie io prefix indirect nocache # usando la cookie `io` establecida en el handshake
+ server app01 app01:3000 check cookie app01
+ server app02 app02:3000 check cookie app02
+ server app03 app03:3000 check cookie app03
+```
+
+Enlaces:
+
+- [Ejemplo](https://github.com/socketio/socket.io/tree/main/examples/cluster-haproxy)
+- [Documentación](http://cbonte.github.io/haproxy-dconv/2.4/configuration.html#cookie)
+
+### Traefik
+
+Usando etiquetas de contenedor:
+
+```yaml
+# docker-compose.yml
+services:
+ traefik:
+ image: traefik:2.4
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ links:
+ - server
+
+ server:
+ image: my-image:latest
+ labels:
+ - "traefik.http.routers.my-service.rule=PathPrefix(`/`)"
+ - traefik.http.services.my-service.loadBalancer.sticky.cookie.name=server_id
+ - traefik.http.services.my-service.loadBalancer.sticky.cookie.httpOnly=true
+```
+
+Con el [proveedor de archivo](https://doc.traefik.io/traefik/v2.0/providers/file/):
+
+```yaml
+## Configuración dinámica
+http:
+ services:
+ my-service:
+ rule: "PathPrefix(`/`)"
+ loadBalancer:
+ sticky:
+ cookie:
+ name: server_id
+ httpOnly: true
+```
+
+Enlaces:
+
+- [Ejemplo](https://github.com/socketio/socket.io/tree/main/examples/cluster-traefik)
+- [Documentación](https://doc.traefik.io/traefik/v2.0/routing/services/#sticky-sessions)
+
+### Usando cluster de Node.js
+
+Al igual que nginx, Node.js viene con soporte de clustering incorporado a través del módulo `cluster`.
+
+Hay varias soluciones, dependiendo de tu caso de uso:
+
+| Paquete NPM | Cómo funciona |
+|:------:| ------------ |
+| [`@socket.io/sticky`](https://github.com/darrachequesne/socket.io-sticky) | el enrutamiento se basa en el parámetro de consulta `sid` |
+| [`sticky-session`](https://github.com/indutny/sticky-session) | el enrutamiento se basa en `connection.remoteAddress` |
+| [`socketio-sticky-session`](https://github.com/wzrdtales/socket-io-sticky-session) | el enrutamiento basado en el encabezado `x-forwarded-for` |
+
+Ejemplo con `@socket.io/sticky`:
+
+```js
+const cluster = require("cluster");
+const http = require("http");
+const { Server } = require("socket.io");
+const numCPUs = require("os").cpus().length;
+const { setupMaster, setupWorker } = require("@socket.io/sticky");
+const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
+
+if (cluster.isMaster) {
+ console.log(`Master ${process.pid} está ejecutándose`);
+
+ const httpServer = http.createServer();
+
+ // configurar sesiones sticky
+ setupMaster(httpServer, {
+ loadBalancingMethod: "least-connection",
+ });
+
+ // configurar conexiones entre los workers
+ setupPrimary();
+
+ // necesario para paquetes que contienen buffers (puedes ignorarlo si solo envías objetos de texto plano)
+ // Node.js < 16.0.0
+ cluster.setupMaster({
+ serialization: "advanced",
+ });
+ // Node.js > 16.0.0
+ // cluster.setupPrimary({
+ // serialization: "advanced",
+ // });
+
+ httpServer.listen(3000);
+
+ for (let i = 0; i < numCPUs; i++) {
+ cluster.fork();
+ }
+
+ cluster.on("exit", (worker) => {
+ console.log(`Worker ${worker.process.pid} murió`);
+ cluster.fork();
+ });
+} else {
+ console.log(`Worker ${process.pid} iniciado`);
+
+ const httpServer = http.createServer();
+ const io = new Server(httpServer);
+
+ // usar el adaptador cluster
+ io.adapter(createAdapter());
+
+ // configurar conexión con el proceso primario
+ setupWorker(io);
+
+ io.on("connection", (socket) => {
+ /* ... */
+ });
+}
+```
+
+## Pasar eventos entre nodos
+
+Ahora que tienes múltiples nodos Socket.IO aceptando conexiones, si quieres transmitir eventos a todos los clientes (o a los clientes en una cierta [sala](../04-Events/rooms.md)) necesitarás alguna forma de pasar mensajes entre procesos o computadoras.
+
+La interfaz encargada de enrutar mensajes es lo que llamamos el [Adaptador](../05-Adapters/adapter.md).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-initialization.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-initialization.md
new file mode 100644
index 00000000..ef460898
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-initialization.md
@@ -0,0 +1,109 @@
+---
+title: Inicialización del cliente
+sidebar_label: Inicialización
+sidebar_position: 2
+slug: /client-initialization/
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+Una vez que hayas [instalado](client-installation.md) la biblioteca del cliente Socket.IO, ahora puedes inicializar el cliente. La lista completa de opciones se puede encontrar [aquí](../../client-options.md).
+
+:::tip
+
+Para usuarios de TypeScript, es posible proporcionar sugerencias de tipo para los eventos. Por favor revisa [esto](../01-Documentation/typescript.md).
+
+:::
+
+En los ejemplos a continuación, el objeto `io` proviene de:
+
+- la importación por `
+```
+
+- una importación ESM
+
+```html
+
+```
+
+- NPM
+
+
+
+
+```js
+const { io } = require("socket.io-client");
+```
+
+
+
+
+```js
+import { io } from "socket.io-client";
+```
+
+
+
+
+```ts
+import { io } from "socket.io-client";
+```
+
+
+
+
+## Desde el mismo dominio
+
+Si tu frontend se sirve desde el mismo dominio que tu servidor, puedes simplemente usar:
+
+```js
+const socket = io();
+```
+
+La URL del servidor se deducirá del objeto [window.location](https://developer.mozilla.org/es/docs/Web/API/Window/location).
+
+## Desde un dominio diferente
+
+En caso de que tu frontend no se sirva desde el mismo dominio que tu servidor, tienes que pasar la URL de tu servidor.
+
+```js
+const socket = io("https://server-domain.com");
+```
+
+En ese caso, por favor asegúrate de habilitar [Cross-Origin Resource Sharing (CORS)](../02-Server/handling-cors.md) en el servidor.
+
+:::info
+
+Puedes usar tanto `https` como `wss` (respectivamente, `http` o `ws`).
+
+:::
+
+```js
+// las siguientes formas son similares
+const socket = io("https://server-domain.com");
+const socket = io("wss://server-domain.com");
+const socket = io("server-domain.com"); // solo en el navegador cuando la página se sirve sobre https (no funcionará en Node.js)
+```
+
+## Namespace personalizado
+
+En los ejemplos anteriores, el cliente se conectará al namespace principal. Usar solo el namespace principal debería ser suficiente para la mayoría de los casos de uso, pero puedes especificar el namespace con:
+
+```js
+// versión del mismo origen
+const socket = io("/admin");
+// versión cross origin
+const socket = io("https://server-domain.com/admin");
+```
+
+Puedes encontrar más detalles sobre namespaces [aquí](../06-Advanced/namespaces.md).
+
+## Opciones
+
+La lista completa de opciones disponibles se puede encontrar [aquí](../../client-options.md).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-installation.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-installation.md
new file mode 100644
index 00000000..1c04aead
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-installation.md
@@ -0,0 +1,221 @@
+---
+title: Instalación del cliente
+sidebar_label: Instalación
+sidebar_position: 1
+slug: /client-installation/
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+:::info
+
+La última versión actual es `4.8.1`, lanzada en octubre de 2024.
+
+Puedes encontrar las notas de la versión [aquí](../../changelog/4.8.1.md).
+
+:::
+
+## Compatibilidad de versiones
+
+Aquí está la tabla de compatibilidad entre el servidor y el cliente JS:
+
+
+
+
Versión del cliente JS
+
Versión del servidor Socket.IO
+
+
+
1.x
+
2.x
+
3.x
+
4.x
+
+
+
1.x
+
SÍ
+
NO
+
NO
+
NO
+
+
+
2.x
+
NO
+
SÍ
+
SÍ1
+
SÍ1
+
+
+
3.x
+
NO
+
NO
+
SÍ
+
SÍ
+
+
+
4.x
+
NO
+
NO
+
SÍ
+
SÍ
+
+
+
+[1] Sí, con [allowEIO3: true](../../server-options.md#alloweio3)
+
+Por favor revisa las guías de migración asociadas:
+
+- [v2 a v3](../07-Migrations/migrating-from-2-to-3.md)
+- [v3 a v4](../07-Migrations/migrating-from-3-to-4.md)
+
+## Soporte de navegadores
+
+Socket.IO soporta IE9 y superiores. IE 6/7/8 ya no son soportados.
+
+La compatibilidad de navegadores se prueba gracias a la increíble plataforma Sauce Labs:
+
+
+
+## Instalación
+
+### Build independiente
+
+Por defecto, el servidor Socket.IO expone un bundle del cliente en `/socket.io/socket.io.js`.
+
+`io` se registrará como una variable global:
+
+```html
+
+
+```
+
+Si no necesitas esto (ver otras opciones abajo), puedes deshabilitar la funcionalidad en el lado del servidor:
+
+```js
+const { Server } = require("socket.io");
+
+const io = new Server({
+ serveClient: false
+});
+```
+
+### Desde un CDN
+
+También puedes incluir el bundle del cliente desde un CDN:
+
+```html
+
+```
+
+Socket.IO también está disponible desde otros CDN:
+
+- cdnjs: https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.min.js
+- jsDelivr: https://cdn.jsdelivr.net/npm/socket.io-client@4.8.1/dist/socket.io.min.js
+- unpkg: https://unpkg.com/socket.io-client@4.8.1/dist/socket.io.min.js
+
+Hay varios bundles disponibles:
+
+| Nombre | Tamaño | Descripción |
+|:------------------|:-----------------|:------------|
+| socket.io.js | 34.7 kB gzip | Versión sin minificar, con [debug](https://www.npmjs.com/package/debug) |
+| socket.io.min.js | 14.7 kB min+gzip | Versión de producción, sin [debug](https://www.npmjs.com/package/debug) |
+| socket.io.msgpack.min.js | 15.3 kB min+gzip | Versión de producción, sin [debug](https://www.npmjs.com/package/debug) y con el [parser msgpack](https://github.com/socketio/socket.io-msgpack-parser) |
+
+El paquete [debug](https://www.npmjs.com/package/debug) permite imprimir información de depuración en la consola. Puedes encontrar más información [aquí](../01-Documentation/logging-and-debugging.md).
+
+Durante el desarrollo, recomendamos usar el bundle `socket.io.js`. Configurando `localStorage.debug = 'socket.io-client:socket'`, cualquier evento recibido por el cliente se imprimirá en la consola.
+
+Para producción, por favor usa el bundle `socket.io.min.js`, que es un build optimizado que excluye el paquete debug.
+
+### Desde NPM
+
+El cliente Socket.IO es compatible con bundlers como [webpack](https://webpack.js.org/) o [browserify](http://browserify.org/).
+
+
+
+
+```sh
+npm install socket.io-client
+```
+
+
+
+
+```sh
+yarn add socket.io-client
+```
+
+
+
+
+```sh
+pnpm add socket.io-client
+```
+
+
+
+
+```sh
+bun add socket.io-client
+```
+
+
+
+
+El cliente también puede ejecutarse desde Node.js.
+
+Nota: por las razones citadas arriba, podrías querer excluir debug de tu bundle del navegador. Con webpack, puedes usar [webpack-remove-debug](https://github.com/johngodley/webpack-remove-debug).
+
+Nota para usuarios de TypeScript: los tipos ahora están incluidos en el paquete `socket.io-client` y por lo tanto los tipos de `@types/socket.io-client` ya no son necesarios y de hecho podrían causar errores:
+
+```
+Object literal may only specify known properties, and 'extraHeaders' does not exist in type 'ConnectOpts'
+```
+
+## Miscelánea
+
+### Árbol de dependencias
+
+Una instalación básica del cliente incluye **9** paquetes, de los cuales **5** son mantenidos por nuestro equipo:
+
+```
+└─┬ socket.io-client@4.8.1
+ ├── @socket.io/component-emitter@3.1.2
+ ├─┬ debug@4.3.7
+ │ └── ms@2.1.3
+ ├─┬ engine.io-client@6.6.3
+ │ ├── @socket.io/component-emitter@3.1.2 deduped
+ │ ├── debug@4.3.7 deduped
+ │ ├── engine.io-parser@5.2.3
+ │ ├─┬ ws@8.17.1
+ │ │ ├── UNMET OPTIONAL DEPENDENCY bufferutil@^4.0.1
+ │ │ └── UNMET OPTIONAL DEPENDENCY utf-8-validate@>=5.0.2
+ │ └── xmlhttprequest-ssl@2.1.2
+ └─┬ socket.io-parser@4.2.4
+ ├── @socket.io/component-emitter@3.1.2 deduped
+ └── debug@4.3.7 deduped
+```
+
+### Versiones transitivas
+
+El paquete `engine.io-client` trae el motor que es responsable de manejar las conexiones de bajo nivel (HTTP long-polling o WebSocket). Ver también: [Cómo funciona](../01-Documentation/how-it-works.md)
+
+| Versión de `socket.io-client` | Versión de `engine.io-client` | Versión de `ws`1 |
+|----------------------------|----------------------------|--------------------------|
+| `4.8.x` | `6.6.x` | `8.17.x` |
+| `4.7.x` | `6.5.x` | `8.17.x` |
+| `4.6.x` | `6.4.x` | `8.11.x` |
+| `4.5.x` | `6.2.x` | `8.2.x` |
+| `4.4.x` | `6.1.x` | `8.2.x` |
+| `4.3.x` | `6.0.x` | `8.2.x` |
+| `4.2.x` | `5.2.x` | `7.4.x` |
+| `4.1.x` | `5.1.x` | `7.4.x` |
+| `4.0.x` | `5.0.x` | `7.4.x` |
+| `3.1.x` | `4.1.x` | `7.4.x` |
+| `3.0.x` | `4.0.x` | `7.4.x` |
+| `2.5.x` | `3.5.x` | `7.5.x` |
+| `2.4.x` | `3.5.x` | `7.5.x` |
+
+[1] solo para usuarios de Node.js. En el navegador, se usa la API nativa de WebSocket.
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-offline-behavior.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-offline-behavior.md
new file mode 100644
index 00000000..1b642d2f
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-offline-behavior.md
@@ -0,0 +1,29 @@
+---
+title: Comportamiento offline
+sidebar_position: 4
+slug: /client-offline-behavior/
+---
+
+## Eventos almacenados en buffer
+
+Por defecto, cualquier evento emitido mientras el Socket no está conectado será almacenado en buffer hasta la reconexión.
+
+Aunque es útil en la mayoría de los casos (cuando el retraso de reconexión es corto), podría resultar en un enorme pico de eventos cuando la conexión se restaura.
+
+Hay varias soluciones para prevenir este comportamiento, dependiendo de tu caso de uso:
+
+- usa el atributo [connected](client-socket-instance.md#socketconnected) de la instancia Socket
+
+```js
+if (socket.connected) {
+ socket.emit( /* ... */ );
+} else {
+ // ...
+}
+```
+
+- usa [eventos volátiles](../04-Events/emitting-events.md#volatile-events)
+
+```js
+socket.volatile.emit( /* ... */ );
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-socket-instance.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-socket-instance.md
new file mode 100644
index 00000000..38a2db98
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-socket-instance.md
@@ -0,0 +1,263 @@
+---
+title: La instancia Socket (lado del cliente)
+sidebar_label: La instancia Socket
+sidebar_position: 3
+slug: /client-socket-instance/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Un `Socket` es la clase fundamental para interactuar con el servidor. Hereda la mayoría de los métodos del [EventEmitter](https://nodejs.org/api/events.html#class-eventemitter) de Node.js, como [emit](../../client-api.md#socketemiteventname-args), [on](../../client-api.md#socketoneventname-callback), [once](../../client-api.md#socketonceeventname-callback) u [off](../../client-api.md#socketoffeventname).
+
+
+
+
+
+
+Además de [emitir](../04-Events/emitting-events.md) y [escuchar](../04-Events/listening-to-events.md) eventos, la instancia Socket tiene algunos atributos que pueden ser útiles en tu aplicación:
+
+## Socket#id
+
+A cada nueva conexión se le asigna un identificador aleatorio de 20 caracteres.
+
+Este identificador está sincronizado con el valor en el lado del servidor.
+
+```js
+// lado del servidor
+io.on("connection", (socket) => {
+ console.log(socket.id); // x8WIv7-mJelg7on_ALbx
+});
+
+// lado del cliente
+socket.on("connect", () => {
+ console.log(socket.id); // x8WIv7-mJelg7on_ALbx
+});
+
+socket.on("disconnect", () => {
+ console.log(socket.id); // undefined
+});
+```
+
+:::caution
+
+Por favor nota que, a menos que la [recuperación del estado de conexión](../01-Documentation/connection-state-recovery.md) esté habilitada, el atributo `id` es un ID **efímero** que no está destinado a ser usado en tu aplicación (o solo con propósitos de depuración) porque:
+
+- este ID se regenera después de cada reconexión (por ejemplo cuando la conexión WebSocket se corta, o cuando el usuario actualiza la página)
+- dos pestañas de navegador diferentes tendrán dos IDs diferentes
+- no hay cola de mensajes almacenada para un ID dado en el servidor (es decir, si el cliente está desconectado, los mensajes enviados desde el servidor a este ID se pierden)
+
+Por favor usa un ID de sesión regular en su lugar (ya sea enviado en una cookie, o almacenado en el localStorage y enviado en el payload de [`auth`](../../client-options.md#auth)).
+
+Ver también:
+
+- [Parte II de nuestra guía de mensajes privados](/get-started/private-messaging-part-2/)
+- [Cómo manejar cookies](/how-to/deal-with-cookies)
+
+:::
+
+## Socket#connected
+
+Este atributo describe si el socket está actualmente conectado al servidor.
+
+```js
+socket.on("connect", () => {
+ console.log(socket.connected); // true
+});
+
+socket.on("disconnect", () => {
+ console.log(socket.connected); // false
+});
+```
+
+## Socket#io
+
+Una referencia al [Manager](../../client-api.md#manager) subyacente.
+
+```js
+socket.on("connect", () => {
+ const engine = socket.io.engine;
+ console.log(engine.transport.name); // en la mayoría de los casos, imprime "polling"
+
+ engine.once("upgrade", () => {
+ // se llama cuando el transporte se actualiza (es decir, de HTTP long-polling a WebSocket)
+ console.log(engine.transport.name); // en la mayoría de los casos, imprime "websocket"
+ });
+
+ engine.on("packet", ({ type, data }) => {
+ // se llama para cada paquete recibido
+ });
+
+ engine.on("packetCreate", ({ type, data }) => {
+ // se llama para cada paquete enviado
+ });
+
+ engine.on("drain", () => {
+ // se llama cuando el buffer de escritura se vacía
+ });
+
+ engine.on("close", (reason) => {
+ // se llama cuando la conexión subyacente se cierra
+ });
+});
+```
+
+## Ciclo de vida
+
+
+
+## Eventos
+
+La instancia Socket emite tres eventos especiales:
+
+- [`connect`](#connect)
+- [`connect_error`](#connect_error)
+- [`disconnect`](#disconnect)
+
+:::tip
+
+Desde Socket.IO v3, la instancia Socket ya no emite ningún evento relacionado con la lógica de reconexión. Puedes escuchar los eventos en la instancia Manager directamente:
+
+```js
+socket.io.on("reconnect_attempt", () => {
+ // ...
+});
+
+socket.io.on("reconnect", () => {
+ // ...
+});
+```
+
+Más información se puede encontrar en la [guía de migración](../07-Migrations/migrating-from-2-to-3.md#the-socket-instance-will-no-longer-forward-the-events-emitted-by-its-manager).
+
+:::
+
+### `connect`
+
+Este evento es disparado por la instancia Socket al conectarse **y** reconectarse.
+
+```js
+socket.on("connect", () => {
+ // ...
+});
+```
+
+:::caution
+
+Los manejadores de eventos no deben registrarse en el manejador `connect` mismo, ya que se registrará un nuevo manejador cada vez que la instancia socket se reconecte:
+
+MAL :warning:
+
+```js
+socket.on("connect", () => {
+ socket.on("data", () => { /* ... */ });
+});
+```
+
+BIEN :+1:
+
+```js
+socket.on("connect", () => {
+ // ...
+});
+
+socket.on("data", () => { /* ... */ });
+```
+
+:::
+
+### `connect_error`
+
+- `error` [``](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Error)
+
+Este evento se dispara al fallar la conexión.
+
+| Razón | ¿Reconexión automática? |
+|-------------------------------------------------------------------------------------------------|-------------------------|
+| La conexión de bajo nivel no puede establecerse (fallo temporal) | :white_check_mark: SÍ |
+| La conexión fue denegada por el servidor en una [función middleware](../02-Server/middlewares.md) | :x: NO |
+
+El atributo [`socket.active`](../../client-api.md#socketactive) indica si el socket intentará reconectarse automáticamente después de un pequeño [retraso aleatorizado](../../client-options.md#reconnectiondelay):
+
+```js
+socket.on("connect_error", (error) => {
+ if (socket.active) {
+ // fallo temporal, el socket intentará reconectarse automáticamente
+ } else {
+ // la conexión fue denegada por el servidor
+ // en ese caso, `socket.connect()` debe llamarse manualmente para reconectarse
+ console.log(error.message);
+ }
+});
+```
+
+### `disconnect`
+
+- `reason` [``](https://developer.mozilla.org/es/docs/Web/JavaScript/Data_structures#string_type)
+- `details` ``
+
+Este evento se dispara al desconectarse.
+
+```js
+socket.on("disconnect", (reason, details) => {
+ // ...
+});
+```
+
+Aquí está la lista de posibles razones:
+
+| Razón | Descripción | ¿Reconexión automática? |
+|------------------------|-------------------------------------------------------------------------------------------------------------------------|:------------------------|
+| `io server disconnect` | El servidor ha desconectado forzosamente el socket con [socket.disconnect()](../../server-api.md#socketdisconnectclose) | :x: NO |
+| `io client disconnect` | El socket fue desconectado manualmente usando [socket.disconnect()](../../client-api.md#socketdisconnect) | :x: NO |
+| `ping timeout` | El servidor no envió un PING dentro del rango `pingInterval + pingTimeout` | :white_check_mark: SÍ |
+| `transport close` | La conexión se cerró (ejemplo: el usuario perdió conexión, o la red cambió de WiFi a 4G) | :white_check_mark: SÍ |
+| `transport error` | La conexión encontró un error (ejemplo: el servidor fue detenido durante un ciclo HTTP long-polling) | :white_check_mark: SÍ |
+
+El atributo [`socket.active`](../../client-api.md#socketactive) indica si el socket intentará reconectarse automáticamente después de un pequeño [retraso aleatorizado](../../client-options.md#reconnectiondelay):
+
+```js
+socket.on("disconnect", (reason) => {
+ if (socket.active) {
+ // desconexión temporal, el socket intentará reconectarse automáticamente
+ } else {
+ // la conexión fue cerrada forzosamente por el servidor o el cliente mismo
+ // en ese caso, `socket.connect()` debe llamarse manualmente para reconectarse
+ console.log(reason);
+ }
+});
+```
+
+:::caution
+
+Los siguientes nombres de eventos están reservados y no deben usarse en tu aplicación:
+
+- `connect`
+- `connect_error`
+- `disconnect`
+- `disconnecting`
+- `newListener`
+- `removeListener`
+
+```js
+// MAL, lanzará un error
+socket.emit("disconnect");
+```
+
+:::
+
+## API completa
+
+La API completa expuesta por la instancia Socket se puede encontrar [aquí](../../client-api.md#socket).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-with-bundlers.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-with-bundlers.md
new file mode 100644
index 00000000..2e5eed80
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/03-Client/client-with-bundlers.md
@@ -0,0 +1,170 @@
+---
+title: Uso del cliente con bundlers
+sidebar_label: Uso con bundlers
+sidebar_position: 5
+slug: /client-with-bundlers/
+---
+
+A continuación encontrarás la configuración para empaquetar la biblioteca del cliente con diferentes bundlers:
+
+- [Webpack 5](#webpack-5)
+ - [Navegador](#navegador)
+ - [Node.js](#nodejs)
+- [Rollup.js](#rollupjs)
+ - [Navegador](#navegador-1)
+ - [Node.js](#nodejs-1)
+
+## Webpack 5
+
+Documentación: https://webpack.js.org/concepts/
+
+### Navegador
+
+Instalación:
+
+```
+npm i -D socket.io-client webpack webpack-cli babel-loader @babel/core @babel/preset-env \
+ @babel/plugin-transform-object-assign webpack-remove-debug
+```
+
+`webpack.config.js`
+
+```js
+module.exports = {
+ entry: "./index.js",
+ output: {
+ filename: "bundle.js",
+ },
+ mode: "production",
+ node: false,
+ module: {
+ rules: [
+ {
+ test: /\.m?js$/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ presets: ["@babel/preset-env"], // asegurar compatibilidad con navegadores antiguos
+ plugins: ["@babel/plugin-transform-object-assign"], // asegurar compatibilidad con IE 11
+ },
+ },
+ },
+ {
+ test: /\.js$/,
+ loader: "webpack-remove-debug", // eliminar paquete "debug"
+ },
+ ],
+ },
+};
+```
+
+Para referencia, aquí está la salida del paquete [`webpack-bundle-analyzer`](https://www.npmjs.com/package/webpack-bundle-analyzer):
+
+
+
+### Node.js
+
+Para usar el cliente en un entorno Node.js (conexión servidor a servidor), aquí está la configuración:
+
+Instalación:
+
+```
+npm i -D socket.io-client webpack webpack-cli
+```
+
+`webpack.config.js`
+
+```js
+module.exports = {
+ entry: "./index.js",
+ output: {
+ filename: "bundle.js",
+ },
+ mode: "production",
+ target: "node",
+ externals: {
+ bufferutil: "bufferutil",
+ "utf-8-validate": "utf-8-validate",
+ },
+};
+```
+
+Nota: sin establecer `target: "node"`, probablemente encontrarás el siguiente error:
+
+```
+ReferenceError: document is not defined
+```
+
+## Rollup.js
+
+Documentación: https://rollupjs.org/guide/en/
+
+### Navegador
+
+Instalación:
+
+```
+npm i -D socket.io-client rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-commonjs \
+ @rollup/plugin-babel rollup-plugin-uglify babel @babel/core @babel/preset-env
+```
+
+`rollup.config.js`
+
+```js
+import resolve from "@rollup/plugin-node-resolve";
+import commonjs from "@rollup/plugin-commonjs";
+import babel from "@rollup/plugin-babel";
+import { uglify } from "rollup-plugin-uglify";
+
+export default {
+ input: "index.js",
+ output: {
+ file: "bundle.js",
+ format: "cjs",
+ },
+ plugins: [
+ resolve({
+ browser: true,
+ }),
+ commonjs(),
+ babel({
+ include: ["**.js", "node_modules/**"],
+ babelHelpers: "bundled",
+ presets: ["@babel/preset-env"],
+ }),
+ uglify(),
+ ],
+};
+```
+
+
+### Node.js
+
+Instalación:
+
+```
+npm i -D socket.io-client rollup @rollup/plugin-node-resolve @rollup/plugin-commonjs rollup-plugin-uglify
+```
+
+`rollup.config.js`
+
+```js
+import resolve from "@rollup/plugin-node-resolve";
+import commonjs from "@rollup/plugin-commonjs";
+import { uglify } from "rollup-plugin-uglify";
+
+export default {
+ input: "index.js",
+ output: {
+ file: "bundle.js",
+ format: "cjs",
+ },
+ plugins: [
+ resolve({
+ preferBuiltins: true,
+ }),
+ commonjs(),
+ uglify(),
+ ],
+};
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/broadcasting-events.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/broadcasting-events.md
new file mode 100644
index 00000000..44b42382
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/broadcasting-events.md
@@ -0,0 +1,128 @@
+---
+title: Transmitir eventos
+sidebar_position: 3
+slug: /broadcasting-events/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Socket.IO facilita el envío de eventos a todos los clientes conectados.
+
+:::info
+
+Por favor nota que la transmisión (broadcasting) es una característica **solo del servidor**.
+
+:::
+
+## A todos los clientes conectados
+
+
+
+```js
+io.emit("hello", "world");
+```
+
+:::caution
+
+Los clientes que están actualmente desconectados (o en proceso de reconexión) no recibirán el evento. Almacenar este evento en algún lugar (en una base de datos, por ejemplo) depende de ti, según tu caso de uso.
+
+:::
+
+## A todos los clientes conectados excepto el emisor
+
+
+
+```js
+io.on("connection", (socket) => {
+ socket.broadcast.emit("hello", "world");
+});
+```
+
+:::note
+
+En el ejemplo anterior, usar `socket.emit("hello", "world")` (sin la bandera `broadcast`) enviaría el evento al "cliente A". Puedes encontrar la lista de todas las formas de enviar un evento en la [hoja de referencia](emit-cheatsheet.md).
+
+:::
+
+## Con acknowledgements
+
+A partir de Socket.IO 4.5.0, ahora puedes transmitir un evento a múltiples clientes y esperar un acknowledgement de cada uno de ellos:
+
+```js
+io.timeout(5000).emit("hello", "world", (err, responses) => {
+ if (err) {
+ // algunos clientes no confirmaron el evento en el tiempo dado
+ } else {
+ console.log(responses); // una respuesta por cliente
+ }
+});
+```
+
+Todas las formas de transmisión son soportadas:
+
+- en una sala
+
+```js
+io.to("room123").timeout(5000).emit("hello", "world", (err, responses) => {
+ // ...
+});
+```
+
+- desde un `socket` específico
+
+```js
+socket.broadcast.timeout(5000).emit("hello", "world", (err, responses) => {
+ // ...
+});
+```
+
+- en un namespace
+
+```js
+io.of("/the-namespace").timeout(5000).emit("hello", "world", (err, responses) => {
+ // ...
+});
+```
+
+## Con múltiples servidores Socket.IO
+
+La transmisión también funciona con múltiples servidores Socket.IO.
+
+Solo necesitas reemplazar el adaptador predeterminado por el [Adaptador Redis](../05-Adapters/adapter-redis.md) u otro [adaptador compatible](../05-Adapters/adapter.md).
+
+
+
+En ciertos casos, podrías querer transmitir solo a clientes que están conectados al servidor actual. Puedes lograr esto con la bandera `local`:
+
+```js
+io.local.emit("hello", "world");
+```
+
+
+
+Para dirigirse a clientes específicos al transmitir, por favor consulta la documentación sobre [Salas](rooms.md).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/emitting-events.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/emitting-events.md
new file mode 100644
index 00000000..efdaf54c
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/emitting-events.md
@@ -0,0 +1,234 @@
+---
+title: Emitir eventos
+sidebar_position: 1
+slug: /emitting-events/
+---
+
+Hay varias formas de enviar eventos entre el servidor y el cliente.
+
+:::tip
+
+Para usuarios de TypeScript, es posible proporcionar sugerencias de tipo para los eventos. Por favor revisa [esto](../01-Documentation/typescript.md).
+
+:::
+
+## Emit básico
+
+La API de Socket.IO está inspirada en el [EventEmitter](https://nodejs.org/docs/latest/api/events.html#events_events) de Node.js, lo que significa que puedes emitir eventos de un lado y registrar listeners del otro:
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ socket.emit("hello", "world");
+});
+```
+
+*Cliente*
+
+```js
+socket.on("hello", (arg) => {
+ console.log(arg); // world
+});
+```
+
+Esto también funciona en la otra dirección:
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ socket.on("hello", (arg) => {
+ console.log(arg); // world
+ });
+});
+```
+
+*Cliente*
+
+```js
+socket.emit("hello", "world");
+```
+
+Puedes enviar cualquier número de argumentos, y todas las estructuras de datos serializables son soportadas, incluyendo objetos binarios como [Buffer](https://nodejs.org/docs/latest/api/buffer.html#buffer_buffer) o [TypedArray](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/TypedArray).
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ socket.emit("hello", 1, "2", { 3: '4', 5: Buffer.from([6]) });
+});
+```
+
+*Cliente*
+
+```js
+// lado del cliente
+socket.on("hello", (arg1, arg2, arg3) => {
+ console.log(arg1); // 1
+ console.log(arg2); // "2"
+ console.log(arg3); // { 3: '4', 5: ArrayBuffer (1) [ 6 ] }
+});
+```
+
+No hay necesidad de ejecutar `JSON.stringify()` en objetos ya que se hará por ti.
+
+```js
+// MAL
+socket.emit("hello", JSON.stringify({ name: "John" }));
+
+// BIEN
+socket.emit("hello", { name: "John" });
+```
+
+Notas:
+
+- Los objetos [Date](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date) serán convertidos a (y recibidos como) su representación en string, ej. `1970-01-01T00:00:00.000Z`
+
+- [Map](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Map) y [Set](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Set) deben ser serializados manualmente:
+
+```js
+const serializedMap = [...myMap.entries()];
+const serializedSet = [...mySet.keys()];
+```
+
+- puedes usar el método [`toJSON()`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#tojson_behavior) para personalizar la serialización de un objeto
+
+Ejemplo con una clase:
+
+```js
+class Hero {
+ #hp;
+
+ constructor() {
+ this.#hp = 42;
+ }
+
+ toJSON() {
+ return { hp: this.#hp };
+ }
+}
+
+socket.emit("here's a hero", new Hero());
+```
+
+## Acknowledgements
+
+Los eventos son geniales, pero en algunos casos podrías querer una API más clásica de solicitud-respuesta. En Socket.IO, esta característica se llama acknowledgements.
+
+Puedes agregar un callback como último argumento del `emit()`, y este callback será llamado una vez que el otro lado confirme el evento:
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ socket.on("update item", (arg1, arg2, callback) => {
+ console.log(arg1); // 1
+ console.log(arg2); // { name: "updated" }
+ callback({
+ status: "ok"
+ });
+ });
+});
+```
+
+*Cliente*
+
+```js
+socket.emit("update item", "1", { name: "updated" }, (response) => {
+ console.log(response.status); // ok
+});
+```
+
+## Con timeout
+
+A partir de Socket.IO v4.4.0, ahora puedes asignar un timeout a cada emit:
+
+```js
+socket.timeout(5000).emit("my-event", (err) => {
+ if (err) {
+ // el otro lado no confirmó el evento en el tiempo dado
+ }
+});
+```
+
+También puedes usar tanto un timeout como un [acknowledgement](#acknowledgements):
+
+```js
+socket.timeout(5000).emit("my-event", (err, response) => {
+ if (err) {
+ // el otro lado no confirmó el evento en el tiempo dado
+ } else {
+ console.log(response);
+ }
+});
+```
+
+## Eventos volátiles
+
+Los eventos volátiles son eventos que no serán enviados si la conexión subyacente no está lista (un poco como [UDP](https://es.wikipedia.org/wiki/Protocolo_de_datagramas_de_usuario), en términos de confiabilidad).
+
+Esto puede ser interesante por ejemplo si necesitas enviar la posición de los personajes en un juego en línea (ya que solo los últimos valores son útiles).
+
+```js
+socket.volatile.emit("hello", "podría o no ser recibido");
+```
+
+Otro caso de uso es descartar eventos cuando el cliente no está conectado (por defecto, los eventos se almacenan en buffer hasta la reconexión).
+
+Ejemplo:
+
+*Servidor*
+
+```js
+io.on("connection", (socket) => {
+ console.log("connect");
+
+ socket.on("ping", (count) => {
+ console.log(count);
+ });
+});
+```
+
+*Cliente*
+
+```js
+let count = 0;
+setInterval(() => {
+ socket.volatile.emit("ping", ++count);
+}, 1000);
+```
+
+Si reinicias el servidor, verás en la consola:
+
+```
+connect
+1
+2
+3
+4
+# el servidor se reinicia, el cliente se reconecta automáticamente
+connect
+9
+10
+11
+```
+
+Sin la bandera `volatile`, verías:
+
+```
+connect
+1
+2
+3
+4
+# el servidor se reinicia, el cliente se reconecta automáticamente y envía sus eventos almacenados en buffer
+connect
+5
+6
+7
+8
+9
+10
+11
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/listening-to-events.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/listening-to-events.md
new file mode 100644
index 00000000..40a8535b
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/listening-to-events.md
@@ -0,0 +1,274 @@
+---
+title: Escuchar eventos
+sidebar_position: 2
+slug: /listening-to-events/
+---
+
+Hay varias formas de manejar eventos que se transmiten entre el servidor y el cliente.
+
+## Métodos de EventEmitter
+
+En el lado del servidor, la instancia Socket extiende la clase [EventEmitter](https://nodejs.org/docs/latest/api/events.html#events_events) de Node.js.
+
+En el lado del cliente, la instancia Socket usa el emisor de eventos proporcionado por la biblioteca [component-emitter](https://github.com/component/emitter), que expone un subconjunto de los métodos de EventEmitter.
+
+### socket.on(eventName, listener)
+
+Agrega la función *listener* al final del array de listeners para el evento llamado *eventName*.
+
+```js
+socket.on("details", (...args) => {
+ // ...
+});
+```
+
+### socket.once(eventName, listener)
+
+Agrega una función *listener* **de una sola vez** para el evento llamado *eventName*
+
+```js
+socket.once("details", (...args) => {
+ // ...
+});
+```
+
+### socket.off(eventName, listener)
+
+Elimina el *listener* especificado del array de listeners para el evento llamado *eventName*.
+
+```js
+const listener = (...args) => {
+ console.log(args);
+}
+
+socket.on("details", listener);
+
+// y luego más tarde...
+socket.off("details", listener);
+```
+
+### socket.removeAllListeners([eventName])
+
+Elimina todos los listeners, o aquellos del *eventName* especificado.
+
+```js
+// para un evento específico
+socket.removeAllListeners("details");
+// para todos los eventos
+socket.removeAllListeners();
+```
+
+## Listeners catch-all
+
+Desde Socket.IO v3, una nueva API inspirada en la biblioteca [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) permite declarar listeners catch-all.
+
+Esta característica está disponible tanto en el cliente como en el servidor.
+
+### socket.onAny(listener)
+
+Agrega un listener que será disparado cuando cualquier evento sea emitido.
+
+```js
+socket.onAny((eventName, ...args) => {
+ // ...
+});
+```
+
+:::caution
+
+Los [Acknowledgements](./emitting-events.md#acknowledgements) no son capturados en el listener catch-all.
+
+```js
+socket.emit("foo", (value) => {
+ // ...
+});
+
+socket.onAnyOutgoing(() => {
+ // se activa cuando el evento es enviado
+});
+
+socket.onAny(() => {
+ // no se activa cuando se recibe el acknowledgement
+});
+```
+
+:::
+
+### socket.prependAny(listener)
+
+Agrega un listener que será disparado cuando cualquier evento sea emitido. El listener se agrega al principio del array de listeners.
+
+```js
+socket.prependAny((eventName, ...args) => {
+ // ...
+});
+```
+
+### socket.offAny([listener])
+
+Elimina todos los listeners catch-all, o el listener dado.
+
+```js
+const listener = (eventName, ...args) => {
+ console.log(eventName, args);
+}
+
+socket.onAny(listener);
+
+// y luego más tarde...
+socket.offAny(listener);
+
+// o todos los listeners
+socket.offAny();
+```
+
+### socket.onAnyOutgoing(listener)
+
+Registra un nuevo listener catch-all para paquetes salientes.
+
+```js
+socket.onAnyOutgoing((event, ...args) => {
+ // ...
+});
+```
+
+:::caution
+
+Los [Acknowledgements](./emitting-events.md#acknowledgements) no son capturados en el listener catch-all.
+
+```js
+socket.on("foo", (value, callback) => {
+ callback("OK");
+});
+
+socket.onAny(() => {
+ // se activa cuando el evento es recibido
+});
+
+socket.onAnyOutgoing(() => {
+ // no se activa cuando el acknowledgement es enviado
+});
+```
+
+:::
+
+### socket.prependAnyOutgoing(listener)
+
+Registra un nuevo listener catch-all para paquetes salientes. El listener se agrega al principio del array de listeners.
+
+```js
+socket.prependAnyOutgoing((event, ...args) => {
+ // ...
+});
+```
+
+### socket.offAnyOutgoing([listener])
+
+Elimina el listener previamente registrado. Si no se proporciona un listener, se eliminan todos los listeners catch-all.
+
+```js
+const listener = (eventName, ...args) => {
+ console.log(eventName, args);
+}
+
+socket.onAnyOutgoing(listener);
+
+// eliminar un solo listener
+socket.offAnyOutgoing(listener);
+
+// eliminar todos los listeners
+socket.offAnyOutgoing();
+```
+
+## Validación
+
+La validación de los argumentos de eventos está fuera del alcance de la biblioteca Socket.IO.
+
+Hay muchos paquetes en el ecosistema JS que cubren este caso de uso, entre ellos:
+
+- [zod](https://zod.dev/)
+- [joi](https://www.npmjs.com/package/joi)
+- [ajv](https://www.npmjs.com/package/ajv)
+- [validatorjs](https://www.npmjs.com/package/validatorjs)
+
+Ejemplo con [joi](https://joi.dev/api/) y [acknowledgements](emitting-events.md#acknowledgements):
+
+```js
+const Joi = require("joi");
+
+const userSchema = Joi.object({
+ username: Joi.string().max(30).required(),
+ email: Joi.string().email().required()
+});
+
+io.on("connection", (socket) => {
+ socket.on("create user", (payload, callback) => {
+ if (typeof callback !== "function") {
+ // no es un acknowledgement
+ return socket.disconnect();
+ }
+ const { error, value } = userSchema.validate(payload);
+ if (error) {
+ return callback({
+ status: "Bad Request",
+ error
+ });
+ }
+ // hacer algo con el valor, y luego
+ callback({
+ status: "OK"
+ });
+ });
+
+});
+```
+
+## Manejo de errores
+
+Actualmente no hay manejo de errores incorporado en la biblioteca Socket.IO, lo que significa que debes capturar cualquier error que pueda lanzarse en un listener.
+
+```js
+io.on("connection", (socket) => {
+ socket.on("list items", async (callback) => {
+ try {
+ const items = await findItems();
+ callback({
+ status: "OK",
+ items
+ });
+ } catch (e) {
+ callback({
+ status: "NOK"
+ });
+ }
+ });
+});
+```
+
+Esto puede ser refactorizado en:
+
+```js
+const errorHandler = (handler) => {
+ const handleError = (err) => {
+ console.error("por favor manéjame", err);
+ };
+
+ return (...args) => {
+ try {
+ const ret = handler.apply(this, args);
+ if (ret && typeof ret.catch === "function") {
+ // handler asíncrono
+ ret.catch(handleError);
+ }
+ } catch (e) {
+ // handler síncrono
+ handleError(e);
+ }
+ };
+};
+
+// lado del servidor o cliente
+socket.on("hello", errorHandler(() => {
+ throw new Error("entremos en pánico");
+}));
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/rooms.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/rooms.md
new file mode 100644
index 00000000..b6256d32
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/04-Events/rooms.md
@@ -0,0 +1,200 @@
+---
+title: Salas
+sidebar_position: 4
+slug: /rooms/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Una *sala* es un canal arbitrario al que los sockets pueden `unirse` y `abandonar`. Se puede usar para transmitir eventos a un subconjunto de clientes:
+
+
+
+:::info
+
+Por favor nota que las salas son un concepto **solo del servidor** (es decir, el cliente no tiene acceso a la lista de salas a las que se ha unido).
+
+:::
+
+## Unirse y abandonar
+
+Puedes llamar a `join` para suscribir el socket a un canal dado:
+
+```js
+io.on("connection", (socket) => {
+ socket.join("some room");
+});
+```
+
+Y luego simplemente usa `to` o `in` (son lo mismo) al transmitir o emitir:
+
+```js
+io.to("some room").emit("some event");
+```
+
+O excluye una sala:
+
+```js
+io.except("some room").emit("some event");
+```
+
+También puedes emitir a varias salas al mismo tiempo:
+
+```js
+io.to("room1").to("room2").to("room3").emit("some event");
+```
+
+En ese caso, se realiza una unión: cada socket que esté al menos en una de las salas recibirá el evento **una vez** (incluso si el socket está en dos o más salas).
+
+También puedes transmitir a una sala desde un socket dado:
+
+```js
+io.on("connection", (socket) => {
+ socket.to("some room").emit("some event");
+});
+```
+
+En ese caso, cada socket en la sala **excluyendo** al emisor recibirá el evento.
+
+
+
+Para abandonar un canal llamas a `leave` de la misma manera que `join`.
+
+## Casos de uso de ejemplo
+
+- transmitir datos a cada dispositivo / pestaña de un usuario dado
+
+```js
+function computeUserIdFromHeaders(headers) {
+ // a implementar
+}
+
+io.on("connection", async (socket) => {
+ const userId = await computeUserIdFromHeaders(socket.handshake.headers);
+
+ socket.join(userId);
+
+ // y luego más tarde
+ io.to(userId).emit("hi");
+});
+```
+
+- enviar notificaciones sobre una entidad dada
+
+```js
+io.on("connection", async (socket) => {
+ const projects = await fetchProjects(socket);
+
+ projects.forEach(project => socket.join("project:" + project.id));
+
+ // y luego más tarde
+ io.to("project:4321").emit("project updated");
+});
+```
+
+## Desconexión
+
+Al desconectarse, los sockets `abandonan` todos los canales de los que formaban parte automáticamente, y no se necesita ninguna limpieza especial de tu parte.
+
+Puedes obtener las salas en las que estaba el Socket escuchando el evento `disconnecting`:
+
+```js
+io.on("connection", socket => {
+ socket.on("disconnecting", () => {
+ console.log(socket.rooms); // el Set contiene al menos el ID del socket
+ });
+
+ socket.on("disconnect", () => {
+ // socket.rooms.size === 0
+ });
+});
+```
+
+## Con múltiples servidores Socket.IO
+
+Al igual que la [transmisión global](broadcasting-events.md#with-multiple-socketio-servers), la transmisión a salas también funciona con múltiples servidores Socket.IO.
+
+Solo necesitas reemplazar el [Adaptador](../08-Miscellaneous/glossary.md#adapter) predeterminado por el Adaptador Redis. Más información al respecto [aquí](../05-Adapters/adapter-redis.md).
+
+
+
+## Detalles de implementación
+
+La característica de "sala" es implementada por lo que llamamos un Adaptador. Este Adaptador es un componente del lado del servidor que es responsable de:
+
+- almacenar las relaciones entre las instancias de Socket y las salas
+- transmitir eventos a todos (o un subconjunto de) clientes
+
+Puedes encontrar el código del adaptador predeterminado en memoria [aquí](https://github.com/socketio/socket.io-adapter).
+
+Básicamente, consiste en dos [Maps de ES6](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Map):
+
+- `sids`: `Map>`
+- `rooms`: `Map>`
+
+Llamar a `socket.join("the-room")` resultará en:
+
+- en el Map `sids`, agregar "the-room" al Set identificado por el ID del socket
+- en el Map `rooms`, agregar el ID del socket en el Set identificado por el string "the-room"
+
+Esos dos maps se usan luego al transmitir:
+
+- una transmisión a todos los sockets (`io.emit()`) recorre el Map `sids`, y envía el paquete a todos los sockets
+- una transmisión a una sala dada (`io.to("room21").emit()`) recorre el Set en el Map `rooms`, y envía el paquete a todos los sockets coincidentes
+
+Puedes acceder a esos objetos con:
+
+```js
+// namespace principal
+const rooms = io.of("/").adapter.rooms;
+const sids = io.of("/").adapter.sids;
+
+// namespace personalizado
+const rooms = io.of("/my-namespace").adapter.rooms;
+const sids = io.of("/my-namespace").adapter.sids;
+```
+
+Notas:
+
+- esos objetos no están destinados a ser modificados directamente, siempre debes usar [`socket.join(...)`](../../server-api.md#socketjoinroom) y [`socket.leave(...)`](../../server-api.md#socketleaveroom) en su lugar.
+- en una configuración [multi-servidor](../02-Server/using-multiple-nodes.md), los objetos `rooms` y `sids` no se comparten entre los servidores Socket.IO (una sala puede solo "existir" en un servidor y no en otro).
+
+## Eventos de sala
+
+A partir de `socket.io@3.1.0`, el Adaptador subyacente emitirá los siguientes eventos:
+
+- `create-room` (argumento: room)
+- `delete-room` (argumento: room)
+- `join-room` (argumento: room, id)
+- `leave-room` (argumento: room, id)
+
+Ejemplo:
+
+```js
+io.of("/").adapter.on("create-room", (room) => {
+ console.log(`la sala ${room} fue creada`);
+});
+
+io.of("/").adapter.on("join-room", (room, id) => {
+ console.log(`el socket ${id} se ha unido a la sala ${room}`);
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-aws-sqs.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-aws-sqs.md
new file mode 100644
index 00000000..3de019e8
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-aws-sqs.md
@@ -0,0 +1,69 @@
+---
+title: Adaptador AWS SQS
+sidebar_position: 8
+slug: /aws-sqs-adapter/
+---
+
+## Cómo funciona
+
+Este adaptador usa [AWS Simple Queue Service](https://aws.amazon.com/sqs/) para reenviar mensajes entre los nodos de un clúster Socket.IO.
+
+A diferencia del paquete existente [`socket.io-sqs`](https://github.com/thinkalpha/socket.io-sqs), este paquete soporta payloads binarios y namespaces dinámicos.
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-aws-sqs-adapter).
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación
+
+```
+npm install @socket.io/aws-sqs-adapter
+```
+
+## Uso
+
+```js
+import { SNS } from "@aws-sdk/client-sns";
+import { SQS } from "@aws-sdk/client-sqs";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/aws-sqs-adapter";
+
+const snsClient = new SNS();
+const sqsClient = new SQS();
+
+const io = new Server({
+ adapter: createAdapter(snsClient, sqsClient)
+});
+
+// esperar la creación de la cola SQS
+await io.of("/").adapter.init();
+
+io.listen(3000);
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado |
+|---------------------|--------------------------------------------------------------------|----------------------|
+| `topicName` | El nombre del topic SNS. | `socket.io` |
+| `topicTags` | Las etiquetas a aplicar al nuevo topic SNS. | `-` |
+| `queuePrefix` | El prefijo de la cola SQS. | `socket.io` |
+| `queueTags` | Las etiquetas a aplicar a la nueva cola SQS. | `-` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats. | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído. | `10_000` |
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
+| `0.1.1` | Junio 2024 | [link](https://github.com/socketio/socket.io-aws-sqs-adapter/releases/tag/0.1.1) | [`0.1.0...0.1.1`](https://github.com/socketio/socket.io-aws-sqs-adapter/compare/0.1.0...0.1.1) |
+| `0.1.0` | Marzo 2024 | [link](https://github.com/socketio/socket.io-aws-sqs-adapter/releases/tag/0.1.0) | `-` |
+
+[Changelog completo](https://github.com/socketio/socket.io-aws-sqs-adapter/blob/main/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-azure-service-bus.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-azure-service-bus.md
new file mode 100644
index 00000000..89ce2f68
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-azure-service-bus.md
@@ -0,0 +1,68 @@
+---
+title: Adaptador Azure Service Bus
+sidebar_position: 9
+slug: /azure-service-bus-adapter/
+---
+
+## Cómo funciona {#how-it-works}
+
+Este adaptador usa el [servicio Azure Service Bus](https://learn.microsoft.com/en-us/azure/service-bus-messaging) para reenviar mensajes entre los nodos de un clúster Socket.IO.
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-azure-service-bus-adapter).
+
+## Características soportadas {#supported-features}
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación {#installation}
+
+```
+npm install @socket.io/azure-service-bus-adapter
+```
+
+## Uso {#usage}
+
+```js
+import { ServiceBusClient, ServiceBusAdministrationClient } from "@azure/service-bus";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/azure-service-bus-adapter";
+
+const connectionString = "Endpoint=...";
+
+const serviceBusClient = new ServiceBusClient(connectionString);
+const serviceBusAdminClient = new ServiceBusAdministrationClient(connectionString);
+
+const io = new Server({
+ adapter: createAdapter(serviceBusClient, serviceBusAdminClient)
+});
+
+// esperar la creación de la suscripción
+await io.of("/").adapter.init();
+
+io.listen(3000);
+```
+
+## Opciones {#options}
+
+| Nombre | Descripción | Valor predeterminado |
+|----------------------|--------------------------------------------------------------------------------------------------------|----------------------|
+| `topicName` | El nombre del topic. | `socket.io` |
+| `topicOptions` | Las opciones usadas para crear el topic. | `-` |
+| `subscriptionPrefix` | El prefijo de la suscripción (se creará una suscripción por servidor Socket.IO en el clúster). | `socket.io` |
+| `receiverOptions` | Las opciones usadas para crear la suscripción. | `-` |
+| `topicOptions` | Las opciones usadas para crear el receiver. | `-` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats. | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído. | `10_000` |
+
+## Últimas versiones {#latest-releases}
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|--------------------------------------------------------------------------------------------|------|
+| `0.1.0` | Marzo 2024 | [link](https://github.com/socketio/socket.io-azure-service-bus-adapter/releases/tag/0.1.0) | `-` |
+
+[Changelog completo](https://github.com/socketio/socket.io-azure-service-bus-adapter/blob/main/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-cluster.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-cluster.md
new file mode 100644
index 00000000..bf990c78
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-cluster.md
@@ -0,0 +1,175 @@
+---
+title: Adaptador Cluster
+sidebar_position: 6
+slug: /cluster-adapter/
+---
+
+## Cómo funciona
+
+El adaptador Cluster permite usar Socket.IO dentro de un [clúster de Node.js](https://nodejs.org/api/cluster.html).
+
+Cada paquete enviado a múltiples clientes (ej. `io.to("room1").emit()` o `socket.broadcast.emit()`) también se envía a otros workers a través del canal IPC.
+
+El código fuente de este adaptador se puede encontrar aquí: https://github.com/socketio/socket.io/tree/main/packages/socket.io-cluster-adapter
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.2.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación
+
+```
+npm install @socket.io/cluster-adapter
+```
+
+## Uso
+
+### Con cluster de Node.js
+
+```js
+const cluster = require("cluster");
+const http = require("http");
+const { Server } = require("socket.io");
+const numCPUs = require("os").cpus().length;
+const { setupMaster, setupWorker } = require("@socket.io/sticky");
+const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
+
+if (cluster.isMaster) {
+ console.log(`Master ${process.pid} está ejecutándose`);
+
+ const httpServer = http.createServer();
+
+ // configurar sesiones sticky
+ setupMaster(httpServer, {
+ loadBalancingMethod: "least-connection",
+ });
+
+ // configurar conexiones entre los workers
+ setupPrimary();
+
+ // necesario para paquetes que contienen buffers (puedes ignorarlo si solo envías objetos de texto plano)
+ // Node.js < 16.0.0
+ cluster.setupMaster({
+ serialization: "advanced",
+ });
+ // Node.js > 16.0.0
+ // cluster.setupPrimary({
+ // serialization: "advanced",
+ // });
+
+ httpServer.listen(3000);
+
+ for (let i = 0; i < numCPUs; i++) {
+ cluster.fork();
+ }
+
+ cluster.on("exit", (worker) => {
+ console.log(`Worker ${worker.process.pid} murió`);
+ cluster.fork();
+ });
+} else {
+ console.log(`Worker ${process.pid} iniciado`);
+
+ const httpServer = http.createServer();
+ const io = new Server(httpServer);
+
+ // usar el adaptador cluster
+ io.adapter(createAdapter());
+
+ // configurar conexión con el proceso primario
+ setupWorker(io);
+
+ io.on("connection", (socket) => {
+ /* ... */
+ });
+}
+```
+
+### Con PM2
+
+Ver la [documentación asociada](../06-Advanced/usage-with-pm2.md).
+
+### Con `recluster`
+
+`cluster.js`
+
+```js
+const cluster = require("cluster");
+const http = require("http");
+const { setupMaster } = require("@socket.io/sticky");
+const { setupPrimary } = require("@socket.io/cluster-adapter");
+const recluster = require("recluster");
+const path = require("path");
+
+const httpServer = http.createServer();
+
+// configurar sesiones sticky
+setupMaster(httpServer, {
+ loadBalancingMethod: "least-connection",
+});
+
+// configurar conexiones entre los workers
+setupPrimary();
+
+// necesario para paquetes que contienen buffers (puedes ignorarlo si solo envías objetos de texto plano)
+// Node.js < 16.0.0
+cluster.setupMaster({
+ serialization: "advanced",
+});
+// Node.js > 16.0.0
+// cluster.setupPrimary({
+// serialization: "advanced",
+// });
+
+httpServer.listen(3000);
+
+const balancer = recluster(path.join(__dirname, "worker.js"));
+
+balancer.run();
+```
+
+`worker.js`
+
+```js
+const http = require("http");
+const { Server } = require("socket.io");
+const { setupWorker } = require("@socket.io/sticky");
+const { createAdapter } = require("@socket.io/cluster-adapter");
+
+const httpServer = http.createServer();
+const io = new Server(httpServer);
+
+// usar el adaptador cluster
+io.adapter(createAdapter());
+
+// configurar conexión con el proceso primario
+setupWorker(io);
+
+io.on("connection", (socket) => {
+ /* ... */
+});
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado |
+|---------------------|--------------------------------------------------------------------|----------------------|
+| `heartbeatInterval` | El número de ms entre dos heartbeats. | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído. | `10_000` |
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------|
+| `0.3.0` | Octubre 2025 | [link](https://github.com/socketio/socket.io/releases/tag/%40socket.io%2Fcluster-adapter%400.3.0) | [`0.2.2...0c43124`](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.2...0c43124) |
+| `0.2.2` | Marzo 2022 | [link](https://github.com/socketio/socket.io-cluster-adapter/releases/tag/0.2.2) | [`0.2.1...0.2.2`](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.1...0.2.2) |
+| `0.2.1` | Octubre 2022 | [link](https://github.com/socketio/socket.io-cluster-adapter/releases/tag/0.2.1) | [`0.2.0...0.2.1`](https://github.com/socketio/socket.io-cluster-adapter/compare/0.2.0...0.2.1) |
+| `0.2.0` | Abril 2022 | [link](https://github.com/socketio/socket.io-cluster-adapter/releases/tag/0.2.0) | [`0.1.0...0.2.0`](https://github.com/socketio/socket.io-cluster-adapter/compare/0.1.0...0.2.0) |
+| `0.1.0` | Junio 2021 | [link](https://github.com/socketio/socket.io-cluster-adapter/releases/tag/0.1.0) | |
+
+Changelog completo: https://github.com/socketio/socket.io/blob/main/packages/socket.io-cluster-adapter/CHANGELOG.md
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-gcp-pubsub.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-gcp-pubsub.md
new file mode 100644
index 00000000..8e16ef1c
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-gcp-pubsub.md
@@ -0,0 +1,66 @@
+---
+title: Adaptador Google Cloud Pub/Sub
+sidebar_position: 7
+slug: /gcp-pubsub-adapter/
+---
+
+## Cómo funciona
+
+Este adaptador usa el [servicio Google Cloud Pub/Sub](https://cloud.google.com/pubsub/docs/overview) para reenviar mensajes entre los nodos de un clúster Socket.IO.
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-gcp-pubsub-adapter).
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación
+
+```
+npm install @socket.io/gcp-pubsub-adapter
+```
+
+## Uso
+
+```js
+import { PubSub } from "@google-cloud/pubsub";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/gcp-pubsub-adapter";
+
+const pubsub = new PubSub({
+ projectId: "your-project-id"
+});
+
+const topic = pubsub.topic(topicNameOrId);
+
+const io = new Server({
+ adapter: createAdapter(topic)
+});
+
+// esperar la creación de la suscripción pub/sub
+await io.of("/").adapter.init();
+
+io.listen(3000);
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado |
+|-----------------------|-------------------------------------------------------------------------------------------------------------------|----------------------|
+| `subscriptionPrefix` | El prefijo para la nueva suscripción a crear. | `socket.io` |
+| `subscriptionOptions` | Las opciones usadas para crear la suscripción. | `-` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats. | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído. | `10_000` |
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|-------------------------------------------------------------------------------------|------|
+| `0.1.0` | Marzo 2024 | [link](https://github.com/socketio/socket.io-gcp-pubsub-adapter/releases/tag/0.1.0) | `-` |
+
+[Changelog completo](https://github.com/socketio/socket.io-gcp-pubsub-adapter/blob/main/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-mongo.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-mongo.md
new file mode 100644
index 00000000..c6b80a04
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-mongo.md
@@ -0,0 +1,194 @@
+---
+title: Adaptador MongoDB
+sidebar_position: 4
+slug: /mongo-adapter/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+## Cómo funciona
+
+El adaptador MongoDB se basa en los [Change Streams](https://docs.mongodb.com/manual/changeStreams/) de MongoDB (y por lo tanto requiere un replica set o un clúster sharded).
+
+Cada paquete que se envía a múltiples clientes (ej. `io.to("room1").emit()` o `socket.broadcast.emit()`) es:
+
+- enviado a todos los clientes coincidentes conectados al servidor actual
+- insertado en una colección capped de MongoDB, y recibido por los otros servidores Socket.IO del clúster
+
+
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-mongo-adapter).
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.2.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :white_check_mark: SÍ (desde versión `0.3.0`) |
+
+## Instalación
+
+```
+npm install @socket.io/mongo-adapter mongodb
+```
+
+Para usuarios de TypeScript, también podrías necesitar `@types/mongodb`.
+
+## Uso
+
+La transmisión de paquetes dentro de un clúster Socket.IO se logra creando documentos MongoDB y usando un [change stream](https://docs.mongodb.com/manual/changeStreams/) en cada servidor Socket.IO.
+
+Hay dos formas de limpiar los documentos en MongoDB:
+
+- una [colección capped](https://www.mongodb.com/docs/manual/core/capped-collections/)
+- un [índice TTL](https://www.mongodb.com/docs/manual/core/index-ttl/)
+
+### Uso con una colección capped
+
+```js
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/mongo-adapter";
+import { MongoClient } from "mongodb";
+
+const DB = "mydb";
+const COLLECTION = "socket.io-adapter-events";
+
+const io = new Server();
+
+const mongoClient = new MongoClient("mongodb://localhost:27017/?replicaSet=rs0");
+
+await mongoClient.connect();
+
+try {
+ await mongoClient.db(DB).createCollection(COLLECTION, {
+ capped: true,
+ size: 1e6
+ });
+} catch (e) {
+ // la colección ya existe
+}
+const mongoCollection = mongoClient.db(DB).collection(COLLECTION);
+
+io.adapter(createAdapter(mongoCollection));
+io.listen(3000);
+```
+
+### Uso con un índice TTL
+
+```js
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/mongo-adapter";
+import { MongoClient } from "mongodb";
+
+const DB = "mydb";
+const COLLECTION = "socket.io-adapter-events";
+
+const io = new Server();
+
+const mongoClient = new MongoClient("mongodb://localhost:27017/?replicaSet=rs0");
+
+await mongoClient.connect();
+
+const mongoCollection = mongoClient.db(DB).collection(COLLECTION);
+
+await mongoCollection.createIndex(
+ { createdAt: 1 },
+ { expireAfterSeconds: 3600, background: true }
+);
+
+io.adapter(createAdapter(mongoCollection, {
+ addCreatedAtField: true
+}));
+
+io.listen(3000);
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado | Agregado en |
+|-----------------------|-----------------------------------------------------------------------------------------------|----------------------|-------------|
+| `uid` | El ID de este nodo | un id aleatorio | `v0.1.0` |
+| `requestsTimeout` | El timeout para solicitudes entre servidores como `fetchSockets()` o `serverSideEmit()` con ack | `5000` | `v0.1.0` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats | `5000` | `v0.1.0` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído | `10000` | `v0.1.0` |
+| `addCreatedAtField` | Si agregar un campo `createdAt` a cada documento MongoDB | `false` | `v0.2.0` |
+| `changeStreamOptions` | Opciones a pasar al change stream de MongoDB | `{}` | `v0.4.0` |
+
+## Preguntas frecuentes
+
+### ¿Todavía necesito habilitar sesiones sticky al usar el adaptador MongoDB?
+
+Sí. No hacerlo resultará en respuestas HTTP 400 (estás llegando a un servidor que no conoce la sesión Socket.IO).
+
+Más información se puede encontrar [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+### ¿Qué pasa cuando el clúster MongoDB está caído?
+
+En caso de que la conexión al clúster MongoDB se corte, el comportamiento dependerá del valor de la opción `bufferMaxEntries` del cliente MongoDB:
+
+- si su valor es `-1` (predeterminado), los paquetes se almacenarán en buffer hasta la reconexión.
+- si su valor es `0`, los paquetes solo se enviarán a los clientes que están conectados al servidor actual.
+
+Documentación: http://mongodb.github.io/node-mongodb-native/3.6/api/global.html#MongoClientOptions
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
+| `0.4.0` | Agosto 2025 | [link](https://github.com/socketio/socket.io-mongo-adapter/releases/tag/0.4.0) | [`0.3.2...0.4.0`](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.2...0.4.0) |
+| `0.3.2` | Enero 2024 | [link](https://github.com/socketio/socket.io-mongo-adapter/releases/tag/0.3.2) | [`0.3.1...0.3.2`](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.1...0.3.2) |
+| `0.3.1` | Enero 2024 | [link](https://github.com/socketio/socket.io-mongo-adapter/releases/tag/0.3.1) | [`0.3.0...0.3.1`](https://github.com/socketio/socket.io-mongo-adapter/compare/0.3.0...0.3.1) |
+| `0.3.0` | Febrero 2023 | [link](https://github.com/socketio/socket.io-mongo-adapter/releases/tag/0.3.0) | [`0.2.1...0.3.0`](https://github.com/socketio/socket.io-mongo-adapter/compare/0.2.1...0.3.0) |
+
+[Changelog completo](https://github.com/socketio/socket.io-mongo-adapter/blob/main/CHANGELOG.md)
+
+## Emitter
+
+El emitter MongoDB permite enviar paquetes a los clientes conectados desde otro proceso Node.js:
+
+
+
+### Instalación
+
+```
+npm install @socket.io/mongo-emitter mongodb
+```
+
+### Uso
+
+```js
+const { Emitter } = require("@socket.io/mongo-emitter");
+const { MongoClient } = require("mongodb");
+
+const mongoClient = new MongoClient("mongodb://localhost:27017/?replicaSet=rs0");
+
+const main = async () => {
+ await mongoClient.connect();
+
+ const mongoCollection = mongoClient.db("mydb").collection("socket.io-adapter-events");
+ const emitter = new Emitter(mongoCollection);
+
+ setInterval(() => {
+ emitter.emit("ping", new Date());
+ }, 1000);
+}
+
+main();
+```
+
+Por favor consulta la hoja de referencia [aquí](adapter.md#emitter-cheatsheet).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-postgres.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-postgres.md
new file mode 100644
index 00000000..f4677746
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-postgres.md
@@ -0,0 +1,164 @@
+---
+title: Adaptador Postgres
+sidebar_position: 5
+slug: /postgres-adapter/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+## Cómo funciona
+
+El adaptador Postgres se basa en los comandos [NOTIFY](https://www.postgresql.org/docs/current/sql-notify.html) y [LISTEN](https://www.postgresql.org/docs/current/sql-listen.html).
+
+Cada paquete que se envía a múltiples clientes (ej. `io.to("room1").emit()` o `socket.broadcast.emit()`) es:
+
+- enviado a todos los clientes coincidentes conectados al servidor actual
+- si el paquete contiene datos binarios o supera el límite de 8000 bytes, el paquete es:
+ - codificado con [msgpack](https://msgpack.org/) e insertado en una tabla auxiliar
+ - el ID de fila se envía dentro de un comando NOTIFY
+ - este ID de fila es recibido por los otros servidores Socket.IO del clúster, que consultan la tabla, decodifican el paquete y luego lo transmiten a su propio conjunto de clientes conectados
+- de lo contrario, el paquete simplemente se envía dentro de un comando NOTIFY y es recibido por los otros servidores Socket.IO del clúster
+
+
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-postgres-adapter).
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.3.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación
+
+```
+npm install @socket.io/postgres-adapter pg
+```
+
+Para usuarios de TypeScript, también podrías necesitar `@types/pg`.
+
+## Uso
+
+### Independiente
+
+```js
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/postgres-adapter";
+import pg from "pg";
+
+const io = new Server();
+
+const pool = new pg.Pool({
+ user: "postgres",
+ host: "localhost",
+ database: "postgres",
+ password: "changeit",
+ port: 5432,
+});
+
+pool.query(`
+ CREATE TABLE IF NOT EXISTS socket_io_attachments (
+ id bigserial UNIQUE,
+ created_at timestamptz DEFAULT NOW(),
+ payload bytea
+ );
+`);
+
+pool.on("error", (err) => {
+ console.error("Error de Postgres", err);
+});
+
+io.adapter(createAdapter(pool));
+io.listen(3000);
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado |
+|---------------------|-----------------------------------------------------------------------------------------------|-------------------------|
+| `channelPrefix` | El prefijo del canal de notificación | `socket.io` |
+| `tableName` | El nombre de la tabla para payloads sobre el límite de 8000 bytes o que contienen datos binarios | `socket_io_attachments` |
+| `payloadThreshold` | El umbral para el tamaño del payload en bytes | `8_000` |
+| `cleanupInterval` | El número de ms entre dos consultas de limpieza | `30_000` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído | `10_000` |
+
+## Preguntas frecuentes
+
+### ¿Todavía necesito habilitar sesiones sticky al usar el adaptador Postgres?
+
+Sí. No hacerlo resultará en respuestas HTTP 400 (estás llegando a un servidor que no conoce la sesión Socket.IO).
+
+Más información se puede encontrar [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+### ¿Qué pasa cuando el servidor Postgres está caído?
+
+En caso de que la conexión al servidor Postgres se corte, los paquetes solo se enviarán a los clientes que están conectados al servidor actual.
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|-----------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
+| `0.5.0` | Noviembre 2025 | [link](https://github.com/socketio/socket.io-postgres-adapter/releases/tag/0.5.0) | [`0.4.0...0.5.0`](https://github.com/socketio/socket.io-postgres-adapter/compare/0.4.0...0.5.0) |
+| `0.4.0` | Julio 2024 | [link](https://github.com/socketio/socket.io-postgres-adapter/releases/tag/0.4.0) | [`0.3.1...0.4.0`](https://github.com/socketio/socket.io-postgres-adapter/compare/0.3.1...0.4.0) |
+| `0.3.1` | Febrero 2023 | [link](https://github.com/socketio/socket.io-postgres-adapter/releases/tag/0.3.1) | [`0.3.0...0.3.1`](https://github.com/socketio/socket.io-postgres-adapter/compare/0.3.0...0.3.1) |
+| `0.3.0` | Abril 2022 | [link](https://github.com/socketio/socket.io-postgres-adapter/releases/tag/0.3.0) | [`0.2.0...0.3.0`](https://github.com/socketio/socket.io-postgres-adapter/compare/0.2.0...0.3.0) |
+| `0.2.0` | Diciembre 2021 | [link](https://github.com/socketio/socket.io-postgres-adapter/releases/tag/0.2.0) | [`0.1.1...0.2.0`](https://github.com/socketio/socket.io-postgres-adapter/compare/0.1.1...0.2.0) |
+
+[Changelog completo](https://github.com/socketio/socket.io-postgres-adapter/blob/main/CHANGELOG.md)
+
+## Emitter
+
+El emitter Postgres permite enviar paquetes a los clientes conectados desde otro proceso Node.js:
+
+
+
+### Instalación
+
+```
+npm install @socket.io/postgres-emitter pg
+```
+
+### Uso
+
+```js
+import { Emitter } from "@socket.io/postgres-emitter";
+import { Pool } from "pg";
+
+const pool = new Pool({
+ user: "postgres",
+ password: "changeit",
+});
+
+const emitter = new Emitter(pool);
+
+setInterval(() => {
+ emitter.emit("ping", new Date());
+}, 1000);
+```
+
+Por favor consulta la hoja de referencia [aquí](adapter.md#emitter-cheatsheet).
+
+### Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|-----------------------------------------------------------------------------------|------|
+| `0.1.0` | Junio 2021 | [link](https://github.com/socketio/socket.io-postgres-emitter/releases/tag/0.1.0) | |
+
+[Changelog completo](https://github.com/socketio/socket.io/blob/main/packages/socket.io-postgres-emitter/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis-streams.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis-streams.md
new file mode 100644
index 00000000..32e9f277
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis-streams.md
@@ -0,0 +1,172 @@
+---
+title: Adaptador Redis Streams
+sidebar_position: 3
+slug: /redis-streams-adapter/
+---
+
+## Cómo funciona
+
+El adaptador usará un [stream de Redis](https://redis.io/docs/data-types/streams/) para reenviar paquetes entre los servidores Socket.IO.
+
+La principal diferencia con el adaptador Redis existente (que usa el [mecanismo Redis Pub/Sub](https://redis.io/docs/manual/pubsub/)) es que este adaptador manejará correctamente cualquier desconexión temporal al servidor Redis y reanudará el stream sin perder ningún paquete.
+
+:::info
+
+- un único stream se usa para todos los namespaces
+- la opción `maxLen` permite limitar el tamaño del stream
+- a diferencia del adaptador basado en el mecanismo Redis PUB/SUB, este adaptador manejará correctamente cualquier desconexión temporal al servidor Redis y reanudará el stream
+- si la [recuperación del estado de conexión](../01-Documentation/connection-state-recovery.md) está habilitada, las sesiones se almacenarán en Redis como un par clave/valor clásico
+
+:::
+
+:::tip
+
+Este adaptador también es compatible con [Valkey](https://valkey.io/).
+
+:::
+
+Código fuente: https://github.com/socketio/socket.io-redis-streams-adapter
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `0.1.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :white_check_mark: SÍ (desde versión `0.1.0`) |
+
+## Instalación
+
+```
+npm install @socket.io/redis-streams-adapter redis
+```
+
+## Uso
+
+### Con el paquete `redis`
+
+```js
+import { createClient } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-streams-adapter";
+
+const redisClient = createClient({ url: "redis://localhost:6379" });
+
+await redisClient.connect();
+
+const io = new Server({
+ adapter: createAdapter(redisClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `redis` y un clúster Redis
+
+```js
+import { createCluster } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-streams-adapter";
+
+const redisClient = createCluster({
+ rootNodes: [
+ {
+ url: "redis://localhost:7000",
+ },
+ {
+ url: "redis://localhost:7001",
+ },
+ {
+ url: "redis://localhost:7002",
+ },
+ ],
+});
+
+await redisClient.connect();
+
+const io = new Server({
+ adapter: createAdapter(redisClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `ioredis`
+
+```js
+import { Redis } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-streams-adapter";
+
+const redisClient = new Redis();
+
+const io = new Server({
+ adapter: createAdapter(redisClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `ioredis` y un clúster Redis
+
+```js
+import { Cluster } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-streams-adapter";
+
+const redisClient = new Cluster([
+ {
+ host: "localhost",
+ port: 7000,
+ },
+ {
+ host: "localhost",
+ port: 7001,
+ },
+ {
+ host: "localhost",
+ port: 7002,
+ },
+]);
+
+const io = new Server({
+ adapter: createAdapter(redisClient)
+});
+
+io.listen(3000);
+```
+
+## Opciones
+
+| Nombre | Descripción | Valor predeterminado |
+|---------------------|-------------------------------------------------------------------------------------------------------------------|----------------------|
+| `streamName` | El nombre del stream de Redis. | `socket.io` |
+| `maxLen` | El tamaño máximo del stream. Se usa recorte casi exacto (~). | `10_000` |
+| `readCount` | El número de elementos a obtener por llamada XREAD. | `100` |
+| `sessionKeyPrefix` | El prefijo de la clave usada para almacenar la sesión Socket.IO, cuando la característica de recuperación del estado de conexión está habilitada. | `sio:session:` |
+| `heartbeatInterval` | El número de ms entre dos heartbeats. | `5_000` |
+| `heartbeatTimeout` | El número de ms sin heartbeat antes de considerar un nodo caído. | `10_000` |
+
+## Preguntas frecuentes
+
+### ¿Todavía necesito habilitar sesiones sticky al usar el adaptador Redis Streams?
+
+Sí. No hacerlo resultará en respuestas HTTP 400 (estás llegando a un servidor que no conoce la sesión Socket.IO).
+
+Más información se puede encontrar [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+### ¿Qué pasa cuando el servidor Redis está caído?
+
+A diferencia del [adaptador Redis](./adapter-redis.md) clásico, este adaptador manejará correctamente cualquier desconexión temporal al servidor Redis y reanudará el stream sin perder ningún paquete.
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|----------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
+| `0.2.2` | Mayo 2024 | [link](https://github.com/socketio/socket.io-redis-streams-adapter/releases/tag/0.2.2) | [`0.2.1...0.2.2`](https://github.com/socketio/socket.io-redis-streams-adapter/compare/0.2.1...0.2.2) |
+| `0.2.1` | Marzo 2024 | [link](https://github.com/socketio/socket.io-redis-streams-adapter/releases/tag/0.2.1) | [`0.2.0...0.2.1`](https://github.com/socketio/socket.io-redis-streams-adapter/compare/0.2.0...0.2.1) |
+| `0.2.0` | Febrero 2024 | [link](https://github.com/socketio/socket.io-redis-streams-adapter/releases/tag/0.2.0) | [`0.1.0...0.2.0`](https://github.com/socketio/socket.io-redis-streams-adapter/compare/0.1.0...0.2.0) |
+| `0.1.0` | Abril 2023 | [link](https://github.com/socketio/socket.io-redis-streams-adapter/releases/tag/0.1.0) | |
+
+[Changelog completo](https://github.com/socketio/socket.io-redis-streams-adapter/blob/main/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis.md
new file mode 100644
index 00000000..6d43a04b
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter-redis.md
@@ -0,0 +1,371 @@
+---
+title: Adaptador Redis
+sidebar_position: 2
+slug: /redis-adapter/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+## Cómo funciona
+
+El adaptador Redis se basa en el mecanismo [Pub/Sub](https://redis.io/topics/pubsub) de Redis.
+
+Cada paquete que se envía a múltiples clientes (ej. `io.to("room1").emit()` o `socket.broadcast.emit()`) es:
+
+- enviado a todos los clientes coincidentes conectados al servidor actual
+- publicado en un canal Redis, y recibido por los otros servidores Socket.IO del clúster
+
+
+
+El código fuente de este adaptador se puede encontrar [aquí](https://github.com/socketio/socket.io-redis-adapter).
+
+## Características soportadas
+
+| Característica | Versión de `socket.io` | Soporte |
+|------------------------------------|-------------------------------------|------------------------------------------------|
+| Gestión de sockets | `4.0.0` | :white_check_mark: SÍ (desde versión `6.1.0`) |
+| Comunicación entre servidores | `4.1.0` | :white_check_mark: SÍ (desde versión `7.0.0`) |
+| Broadcast con acknowledgements | [`4.5.0`](../../changelog/4.5.0.md) | :white_check_mark: SÍ (desde versión `7.2.0`) |
+| Recuperación del estado de conexión| [`4.6.0`](../../changelog/4.6.0.md) | :x: NO |
+
+## Instalación
+
+```
+npm install @socket.io/redis-adapter
+```
+
+## Tabla de compatibilidad
+
+| Versión del adaptador Redis | Versión del servidor Socket.IO |
+|-----------------------------|--------------------------------|
+| 4.x | 1.x |
+| 5.x | 2.x |
+| 6.0.x | 3.x |
+| 6.1.x | 4.x |
+| 7.x y superior | 4.3.1 y superior |
+
+## Uso
+
+:::tip
+
+Para nuevos desarrollos, recomendamos usar el [adaptador sharded](#with-redis-sharded-pubsub), que aprovecha la [característica sharded Pub/Sub](https://redis.io/docs/latest/develop/interact/pubsub/#sharded-pubsub) introducida en Redis 7.0.
+
+:::
+
+### Con el paquete `redis`
+
+:::caution
+
+El paquete `redis` parece tener problemas restaurando las suscripciones Redis después de reconectarse:
+
+- https://github.com/redis/node-redis/issues/2155
+- https://github.com/redis/node-redis/issues/1252
+
+Podrías querer usar el paquete [`ioredis`](#with-the-ioredis-package) en su lugar.
+
+:::
+
+```js
+import { createClient } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = createClient({ url: "redis://localhost:6379" });
+const subClient = pubClient.duplicate();
+
+await Promise.all([
+ pubClient.connect(),
+ subClient.connect()
+]);
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `redis` y un clúster Redis
+
+```js
+import { createCluster } from "redis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = createCluster({
+ rootNodes: [
+ {
+ url: "redis://localhost:7000",
+ },
+ {
+ url: "redis://localhost:7001",
+ },
+ {
+ url: "redis://localhost:7002",
+ },
+ ],
+});
+const subClient = pubClient.duplicate();
+
+await Promise.all([
+ pubClient.connect(),
+ subClient.connect()
+]);
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `ioredis`
+
+```js
+import { Redis } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = new Redis();
+const subClient = pubClient.duplicate();
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
+
+io.listen(3000);
+```
+
+### Con el paquete `ioredis` y un clúster Redis
+
+```js
+import { Cluster } from "ioredis";
+import { Server } from "socket.io";
+import { createAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = new Cluster([
+ {
+ host: "localhost",
+ port: 7000,
+ },
+ {
+ host: "localhost",
+ port: 7001,
+ },
+ {
+ host: "localhost",
+ port: 7002,
+ },
+]);
+const subClient = pubClient.duplicate();
+
+const io = new Server({
+ adapter: createAdapter(pubClient, subClient)
+});
+
+io.listen(3000);
+```
+
+### Con Redis sharded Pub/Sub
+
+Sharded Pub/Sub fue introducido en Redis 7.0 para ayudar a escalar el uso de Pub/Sub en modo clúster.
+
+Referencia: https://redis.io/docs/interact/pubsub/#sharded-pubsub
+
+Se puede crear un adaptador dedicado con el método `createShardedAdapter()`:
+
+```js
+import { Server } from "socket.io";
+import { createClient } from "redis";
+import { createShardedAdapter } from "@socket.io/redis-adapter";
+
+const pubClient = createClient({ host: "localhost", port: 6379 });
+const subClient = pubClient.duplicate();
+
+await Promise.all([
+ pubClient.connect(),
+ subClient.connect()
+]);
+
+const io = new Server({
+ adapter: createShardedAdapter(pubClient, subClient)
+});
+
+io.listen(3000);
+```
+
+Requisitos mínimos:
+
+- Redis 7.0
+- [`redis@4.6.0`](https://github.com/redis/node-redis/commit/3b1bad229674b421b2bc6424155b20d4d3e45bd1)
+
+:::caution
+
+Actualmente no es posible usar el adaptador sharded con el paquete `ioredis` y un clúster Redis ([referencia](https://github.com/luin/ioredis/issues/1759)).
+
+:::
+
+## Opciones
+
+### Adaptador predeterminado
+
+| Nombre | Descripción | Valor predeterminado |
+|-------------------------------------|-------------------------------------------------------------------------------|----------------------|
+| `key` | El prefijo para los canales Redis Pub/Sub. | `socket.io` |
+| `requestsTimeout` | Después de este timeout el adaptador dejará de esperar respuestas a solicitudes. | `5_000` |
+| `publishOnSpecificResponseChannel` | Si publicar una respuesta al canal específico del nodo solicitante. | `false` |
+| `parser` | El parser a usar para codificar y decodificar mensajes enviados a Redis. | `-` |
+
+:::tip
+
+Establecer la opción `publishOnSpecificResponseChannel` a `true` es más eficiente ya que las respuestas (por ejemplo al llamar `fetchSockets()` o `serverSideEmit()`) solo se envían al servidor solicitante, y no a todos los servidores.
+
+Sin embargo, actualmente está en `false` por defecto para compatibilidad hacia atrás.
+
+:::
+
+### Adaptador sharded
+
+| Nombre | Descripción | Valor predeterminado |
+|---------------------|------------------------------------------------------------------------------------------|----------------------|
+| `channelPrefix` | El prefijo para los canales Redis Pub/Sub. | `socket.io` |
+| `subscriptionMode` | El modo de suscripción impacta el número de canales Redis Pub/Sub usados por el adaptador. | `dynamic` |
+
+Valores disponibles para la opción `subscriptionMode`:
+
+| Valor | # de canales Pub/Sub | Descripción |
+|---------------------|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------|
+| `static` | 2 por namespace | Útil cuando se usan namespaces dinámicos. |
+| `dynamic` (defecto) | (2 + 1 por sala pública) por namespace| Útil cuando algunas salas tienen un bajo número de clientes (así solo unos pocos servidores Socket.IO son notificados). |
+| `dynamic-private` | (2 + 1 por sala) por namespace | Como `dynamic` pero crea canales separados para salas privadas también. Útil cuando hay mucha comunicación 1:1 vía llamadas `socket.emit()`. |
+
+## Preguntas frecuentes
+
+### ¿Se almacena algún dato en Redis?
+
+No, el adaptador Redis usa el mecanismo [Pub/Sub](https://redis.io/topics/pubsub) para reenviar los paquetes entre los servidores Socket.IO, así que no hay claves almacenadas en Redis.
+
+### ¿Todavía necesito habilitar sesiones sticky al usar el adaptador Redis?
+
+Sí. No hacerlo resultará en respuestas HTTP 400 (estás llegando a un servidor que no conoce la sesión Socket.IO).
+
+Más información se puede encontrar [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+### ¿Qué pasa cuando el servidor Redis está caído?
+
+En caso de que la conexión al servidor Redis se corte, los paquetes solo se enviarán a los clientes que están conectados al servidor actual.
+
+## Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
+| `8.3.0` | Marzo 2024 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/8.3.0) | [`8.2.1...8.3.0`](https://github.com/socketio/socket.io-redis-adapter/compare/8.2.1...8.3.0) |
+| `8.2.1` | Mayo 2023 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/8.2.1) | [`8.2.0...8.2.1`](https://github.com/socketio/socket.io-redis-adapter/compare/8.2.0...8.2.1) |
+| `8.2.0` | Mayo 2023 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/8.2.0) | [`8.1.0...8.2.0`](https://github.com/socketio/socket.io-redis-adapter/compare/8.1.0...8.2.0) |
+| `8.1.0` | Febrero 2023 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/8.1.0) | [`8.0.0...8.1.0`](https://github.com/socketio/socket.io-redis-adapter/compare/8.0.0...8.1.0) |
+| `8.0.0` | Diciembre 2022 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/8.0.0) | [`7.2.0...8.0.0`](https://github.com/socketio/socket.io-redis-adapter/compare/7.2.0...8.0.0) |
+| `7.2.0` | Mayo 2022 | [link](https://github.com/socketio/socket.io-redis-adapter/releases/tag/7.2.0) | [`7.1.0...7.2.0`](https://github.com/socketio/socket.io-redis-adapter/compare/7.1.0...7.2.0) |
+
+[Changelog completo](https://github.com/socketio/socket.io-redis-adapter/blob/main/CHANGELOG.md)
+
+## Emitter
+
+El emitter Redis permite enviar paquetes a los clientes conectados desde otro proceso Node.js:
+
+
+
+Este emitter también está disponible en varios lenguajes:
+
+- Javascript: https://github.com/socketio/socket.io-redis-emitter
+- Java: https://github.com/sunsus/socket.io-java-emitter
+- Python: https://pypi.org/project/socket.io-emitter/
+- PHP: https://github.com/rase-/socket.io-php-emitter
+- Golang: https://github.com/yosuke-furukawa/socket.io-go-emitter
+- Perl: https://metacpan.org/pod/SocketIO::Emitter
+- Rust: https://github.com/epli2/socketio-rust-emitter
+
+### Instalación
+
+```
+npm install @socket.io/redis-emitter redis
+```
+
+### Uso
+
+```js
+import { Emitter } from "@socket.io/redis-emitter";
+import { createClient } from "redis";
+
+const redisClient = createClient({ url: "redis://localhost:6379" });
+
+redisClient.connect().then(() => {
+ const emitter = new Emitter(redisClient);
+
+ setInterval(() => {
+ emitter.emit("time", new Date);
+ }, 5000);
+});
+```
+
+Nota: con `redis@3`, no es necesario llamar a `connect()` en el cliente Redis:
+
+```js
+import { Emitter } from "@socket.io/redis-emitter";
+import { createClient } from "redis";
+
+const redisClient = createClient({ url: "redis://localhost:6379" });
+const emitter = new Emitter(redisClient);
+
+setInterval(() => {
+ emitter.emit("time", new Date);
+}, 5000);
+```
+
+Por favor consulta la hoja de referencia [aquí](adapter.md#emitter-cheatsheet).
+
+### Migrando desde `socket.io-emitter`
+
+El paquete fue renombrado de `socket.io-emitter` a `@socket.io/redis-emitter` en [v4](https://github.com/socketio/socket.io-redis-emitter/releases/tag/4.0.0), para reflejar mejor la relación con Redis.
+
+Para migrar al nuevo paquete, deberás asegurarte de proporcionar tus propios clientes Redis, ya que el paquete ya no creará clientes Redis en nombre del usuario.
+
+Antes:
+
+```js
+const io = require("socket.io-emitter")({ host: "127.0.0.1", port: 6379 });
+```
+
+Después:
+
+```js
+const { Emitter } = require("@socket.io/redis-emitter");
+const { createClient } = require("redis");
+
+const redisClient = createClient();
+const io = new Emitter(redisClient);
+```
+
+### Últimas versiones
+
+| Versión | Fecha de lanzamiento | Notas de lanzamiento | Diff |
+|---------|----------------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
+| `5.1.0` | Enero 2023 | [link](https://github.com/socketio/socket.io-redis-emitter/releases/tag/5.1.0) | [`5.0.0...5.1.0`](https://github.com/socketio/socket.io-redis-emitter/compare/5.0.0...5.1.0) |
+| `5.0.0` | Septiembre 2022 | [link](https://github.com/socketio/socket.io-redis-emitter/releases/tag/5.0.1) | [`4.1.1...5.0.0`](https://github.com/socketio/socket.io-redis-emitter/compare/4.1.1...5.0.0) |
+| `4.1.1` | Enero 2022 | [link](https://github.com/socketio/socket.io-redis-emitter/releases/tag/4.1.1) | [`4.1.0...4.1.1`](https://github.com/socketio/socket.io-redis-emitter/compare/4.1.0...4.1.1) |
+| `4.1.0` | Mayo 2021 | [link](https://github.com/socketio/socket.io-redis-emitter/releases/tag/4.1.0) | [`4.0.0...4.1.0`](https://github.com/socketio/socket.io-redis-emitter/compare/4.0.0...4.1.0) |
+| `4.0.0` | Marzo 2021 | [link](https://github.com/socketio/socket.io-redis-emitter/releases/tag/4.0.0) | [`3.2.0...4.0.0`](https://github.com/socketio/socket.io-redis-emitter/compare/3.2.0...4.0.0) |
+
+[Changelog completo](https://github.com/socketio/socket.io-redis-emitter/blob/main/CHANGELOG.md)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter.md
new file mode 100644
index 00000000..b3d33ea4
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/05-Adapters/adapter.md
@@ -0,0 +1,150 @@
+---
+title: Adaptador
+sidebar_label: Introducción
+sidebar_position: 1
+slug: /adapter/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Un Adaptador es un componente del lado del servidor que es responsable de transmitir eventos a todos o un subconjunto de clientes.
+
+Al escalar a múltiples servidores Socket.IO, necesitarás reemplazar el adaptador en memoria predeterminado por otra implementación, para que los eventos se enruten correctamente a todos los clientes.
+
+Aquí está la lista de adaptadores que son mantenidos por nuestro equipo:
+
+- el [adaptador Redis](adapter-redis.md)
+- el [adaptador Redis Streams](adapter-redis-streams.md)
+- el [adaptador MongoDB](adapter-mongo.md)
+- el [adaptador Postgres](adapter-postgres.md)
+- el [adaptador Cluster](adapter-cluster.md)
+- el [adaptador Google Cloud Pub/Sub](adapter-gcp-pubsub.md)
+- el [adaptador AWS SQS](adapter-aws-sqs.md)
+- el [adaptador Azure Service Bus](adapter-azure-service-bus.md)
+
+También hay varias otras opciones que son mantenidas por la (¡increíble!) comunidad:
+
+- [AMQP](https://github.com/sensibill/socket.io-amqp) (ej. RabbitMQ)
+- [NATS](https://github.com/MickL/socket.io-nats-adapter)
+- [NATS](https://github.com/distrue/socket.io-nats-adapter)
+
+Por favor nota que habilitar sesiones sticky sigue siendo necesario al usar múltiples servidores Socket.IO y HTTP long-polling. Más información [aquí](../02-Server/using-multiple-nodes.md#why-is-sticky-session-required).
+
+## API
+
+Puedes acceder a la instancia del adaptador con:
+
+```js
+// namespace principal
+const mainAdapter = io.of("/").adapter; // ¡ADVERTENCIA! io.adapter() no funcionará
+// namespace personalizado
+const adminAdapter = io.of("/admin").adapter;
+```
+
+A partir de `socket.io@3.1.0`, cada instancia de Adaptador emite los siguientes eventos:
+
+- `create-room` (argumento: room)
+- `delete-room` (argumento: room)
+- `join-room` (argumento: room, id)
+- `leave-room` (argumento: room, id)
+
+Ejemplo:
+
+```js
+io.of("/").adapter.on("create-room", (room) => {
+ console.log(`la sala ${room} fue creada`);
+});
+
+io.of("/").adapter.on("join-room", (room, id) => {
+ console.log(`el socket ${id} se ha unido a la sala ${room}`);
+});
+```
+
+## Emitter
+
+La mayoría de las implementaciones de adaptadores vienen con su paquete emitter asociado, que permite comunicarse con el grupo de servidores Socket.IO desde otro proceso Node.js.
+
+
+
+Esto puede ser útil por ejemplo en una configuración de microservicios, donde todos los clientes se conectan al microservicio M1, mientras que el microservicio M2 usa el emitter para transmitir paquetes (comunicación unidireccional).
+
+## Hoja de referencia del Emitter
+
+```js
+// a todos los clientes
+emitter.emit(/* ... */);
+
+// a todos los clientes en "room1"
+emitter.to("room1").emit(/* ... */);
+
+// a todos los clientes en "room1" excepto aquellos en "room2"
+emitter.to("room1").except("room2").emit(/* ... */);
+
+const adminEmitter = emitter.of("/admin");
+
+// a todos los clientes en el namespace "admin"
+adminEmitter.emit(/* ... */);
+
+// a todos los clientes en el namespace "admin" y en la sala "room1"
+adminEmitter.to("room1").emit(/* ... */);
+```
+
+El emitter también soporta los métodos de utilidad que fueron agregados en `socket.io@4.0.0`:
+
+- `socketsJoin()`
+
+```js
+// hacer que todas las instancias de Socket se unan a la sala "room1"
+emitter.socketsJoin("room1");
+
+// hacer que todas las instancias de Socket del namespace "admin" en la sala "room1" se unan a la sala "room2"
+emitter.of("/admin").in("room1").socketsJoin("room2");
+```
+
+- `socketsLeave()`
+
+```js
+// hacer que todas las instancias de Socket abandonen la sala "room1"
+emitter.socketsLeave("room1");
+
+// hacer que todas las instancias de Socket en la sala "room1" abandonen las salas "room2" y "room3"
+emitter.in("room1").socketsLeave(["room2", "room3"]);
+
+// hacer que todas las instancias de Socket en la sala "room1" del namespace "admin" abandonen la sala "room2"
+emitter.of("/admin").in("room1").socketsLeave("room2");
+```
+
+- `disconnectSockets()`
+
+```js
+// hacer que todas las instancias de Socket se desconecten
+emitter.disconnectSockets();
+
+// hacer que todas las instancias de Socket en la sala "room1" se desconecten (y descartar la conexión de bajo nivel)
+emitter.in("room1").disconnectSockets(true);
+
+// hacer que todas las instancias de Socket en la sala "room1" del namespace "admin" se desconecten
+emitter.of("/admin").in("room1").disconnectSockets();
+
+// esto también funciona con un solo ID de socket
+emitter.of("/admin").in(theSocketId).disconnectSockets();
+```
+
+- `serverSideEmit()`
+
+```js
+// emitir un evento a todos los servidores Socket.IO del clúster
+emitter.serverSideEmit("hello", "world");
+
+// servidor Socket.IO (lado del servidor)
+io.on("hello", (arg) => {
+ console.log(arg); // imprime "world"
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/admin-ui.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/admin-ui.md
new file mode 100644
index 00000000..de7f1953
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/admin-ui.md
@@ -0,0 +1,247 @@
+---
+title: Admin UI
+sidebar_position: 3
+slug: /admin-ui/
+---
+
+La UI de administración de Socket.IO se puede usar para tener una visión general del estado de tu despliegue de Socket.IO.
+
+El código fuente se puede encontrar aquí: https://github.com/socketio/socket.io-admin-ui/
+
+Enlace a la versión hospedada: https://admin.socket.io/
+
+## Características actuales
+
+- visión general de los servidores y los clientes que están actualmente conectados
+
+
+
+- detalles de cada instancia de socket (transporte activo, handshake, salas, ...)
+
+
+
+- detalles de cada sala
+
+
+
+- detalles de cada evento emitido o recibido por el servidor
+
+
+
+- operaciones administrativas (join, leave, disconnect)
+
+¡Si tienes algún comentario / sugerencia, no dudes en compartirlo!
+
+## Instalación
+
+### Lado del servidor
+
+Primero, instala el paquete `@socket.io/admin-ui`:
+
+```
+npm i @socket.io/admin-ui
+```
+
+Y luego invoca el método `instrument` en tu servidor Socket.IO:
+
+```js
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+const { instrument } = require("@socket.io/admin-ui");
+
+const httpServer = createServer();
+
+const io = new Server(httpServer, {
+ cors: {
+ origin: ["https://admin.socket.io"],
+ credentials: true
+ }
+});
+
+instrument(io, {
+ auth: false,
+ mode: "development",
+});
+
+httpServer.listen(3000);
+```
+
+El módulo es compatible con:
+
+- servidor Socket.IO v4
+- servidor Socket.IO v3 (>= 3.1.0), pero sin las operaciones en salas (join, leave, desconexión)
+
+Ejemplo con [NestJS](https://docs.nestjs.com/websockets/gateways):
+
+```ts
+import { instrument } from "@socket.io/admin-ui";
+
+@WebSocketGateway()
+export class MyGateway {
+ // ...
+ afterInit() {
+ instrument(this.server, {
+ auth: false,
+ mode: "development",
+ });
+ }
+}
+```
+
+### Lado del cliente
+
+Luego puedes ir a https://admin.socket.io, o hospedar los archivos encontrados en la carpeta `ui/dist` [aquí](https://github.com/socketio/socket.io-admin-ui/tree/main/ui/dist).
+
+**Nota importante**: el sitio web en https://admin.socket.io es totalmente estático (hospedado en [Vercel](https://vercel.com)), no almacenamos (y nunca lo haremos) ninguna información sobre ti o tu navegador (sin tracking, sin analytics, ...). Dicho esto, hospedar los archivos tú mismo es totalmente válido.
+
+Deberías ver el siguiente modal:
+
+
+
+Por favor ingresa la URL de tu servidor (por ejemplo, `http://localhost:3000` o `https://example.com`) y las credenciales, si aplican (ver la opción `auth` [abajo](#auth)).
+
+### Opciones disponibles
+
+#### `auth`
+
+Valor por defecto: `-`
+
+Esta opción es obligatoria. Puedes deshabilitar la autenticación (por favor usa con precaución):
+
+```js
+instrument(io, {
+ auth: false
+});
+```
+
+O usar autenticación básica:
+
+```js
+instrument(io, {
+ auth: {
+ type: "basic",
+ username: "admin",
+ password: "$2b$10$heqvAkYMez.Va6Et2uXInOnkCT6/uQj1brkrbyG3LpopDklcq7ZOS" // "changeit" encriptado con bcrypt
+ },
+});
+```
+
+:::caution
+
+Por favor ten en cuenta que el paquete `bcrypt` actualmente no soporta hashes que comiencen con el prefijo `$2y$`, que es usado por algunas implementaciones de BCrypt (por ejemplo https://bcrypt-generator.com/ o https://www.bcrypt.fr/). Puedes verificar la validez del hash con:
+
+```
+$ node
+> require("bcryptjs").compareSync("", "")
+true
+```
+
+Puedes generar un hash válido con:
+
+```
+$ node
+> require("bcryptjs").hashSync("changeit", 10)
+'$2b$10$LQUE...'
+```
+
+Ver también:
+
+- https://github.com/kelektiv/node.bcrypt.js/issues/849
+- https://stackoverflow.com/a/36225192/5138796
+
+:::
+
+#### `namespaceName`
+
+Valor por defecto: `/admin`
+
+El nombre del namespace que se creará para manejar las tareas administrativas.
+
+```js
+instrument(io, {
+ namespaceName: "/custom"
+});
+```
+
+Este namespace es un namespace clásico de Socket.IO, puedes acceder a él con:
+
+```js
+const adminNamespace = io.of("/admin");
+```
+
+Más información [aquí](namespaces.md).
+
+#### `readonly`
+
+Valor por defecto: `false`
+
+Si poner la UI de admin en modo solo lectura (no se permite join, leave o disconnect).
+
+```js
+instrument(io, {
+ readonly: true
+});
+```
+
+#### `serverId`
+
+Valor por defecto: `require("os").hostname()`
+
+El ID del servidor dado. Si tienes varios servidores Socket.IO en la misma máquina, necesitarás darles un ID distinto:
+
+```js
+instrument(io, {
+ serverId: `${require("os").hostname()}#${process.pid}`
+});
+```
+
+#### `store`
+
+Valor por defecto: `new InMemoryStore()`
+
+El store se usa para almacenar los IDs de sesión para que el usuario no tenga que volver a escribir las credenciales al reconectarse.
+
+Si usas autenticación básica en una configuración multi-servidor, deberías proporcionar un store personalizado:
+
+```js
+const { instrument, RedisStore } = require("@socket.io/admin-ui");
+
+instrument(io, {
+ store: new RedisStore(redisClient)
+});
+```
+
+#### `mode`
+
+Valor por defecto: `development`
+
+En modo producción, el servidor no enviará todos los detalles sobre las instancias de socket y las salas, reduciendo así la huella de memoria de la instrumentación.
+
+```js
+instrument(io, {
+ mode: "production"
+});
+```
+
+El modo producción también puede habilitarse con la variable de entorno NODE_ENV:
+
+```
+NODE_ENV=production node index.js
+```
+
+## Cómo funciona
+
+El código fuente se puede encontrar aquí: https://github.com/socketio/socket.io-admin-ui/
+
+El método `instrument` simplemente:
+
+- crea un [namespace](namespaces.md) y añade un [middleware](../02-Server/middlewares.md) de autenticación si aplica
+- registra listeners para los eventos `connection` y `disconnect` para cada namespace existente para rastrear las instancias de socket
+- registra un temporizador que periódicamente enviará estadísticas del servidor a la UI
+- registra manejadores para los comandos `join`, `leave` y `_disconnect` enviados desde la UI
+
+## Últimas versiones
+
+- `0.5.1` (Oct 2022): [GitHub release](https://github.com/socketio/socket.io-admin-ui/releases/tag/0.5.1) / [diff](https://github.com/socketio/socket.io-admin-ui/compare/0.5.0...0.5.1)
+- `0.5.0` (Sep 2022): [GitHub release](https://github.com/socketio/socket.io-admin-ui/releases/tag/0.5.0) / [diff](https://github.com/socketio/socket.io-admin-ui/compare/0.4.0...0.5.0)
+- `0.4.0` (Jun 2022): [GitHub release](https://github.com/socketio/socket.io-admin-ui/releases/tag/0.4.0) / [diff](https://github.com/socketio/socket.io-admin-ui/compare/0.3.0...0.4.0)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/custom-parser.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/custom-parser.md
new file mode 100644
index 00000000..5ca0ed77
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/custom-parser.md
@@ -0,0 +1,209 @@
+---
+title: Parser personalizado
+sidebar_position: 2
+slug: /custom-parser/
+---
+
+Desde Socket.IO v2.0.0, ahora es posible proporcionar tu propio parser, para controlar el marshalling / unmarshalling de paquetes.
+
+*Servidor*
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({
+ parser: myParser
+});
+```
+
+*Cliente*
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io({
+ parser: myParser
+});
+```
+
+## Parsers disponibles
+
+Además del [parser por defecto](#el-parser-por-defecto), aquí está la lista de parsers disponibles:
+
+| Paquete | Descripción |
+|--------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [`socket.io-circular-parser`](https://www.npmjs.com/package/socket.io-circular-parser) | Similar al parser por defecto, pero maneja referencias circulares. |
+| [`socket.io-msgpack-parser`](https://www.npmjs.com/package/socket.io-msgpack-parser) | Usa [MessagePack](https://msgpack.org/) para codificar los paquetes (basado en el paquete [`notepack.io`](https://github.com/darrachequesne/notepack)). |
+| [`@skgdev/socket.io-msgpack-javascript`](https://www.npmjs.com/package/@skgdev/socket.io-msgpack-javascript) | Usa [MessagePack](https://msgpack.org/) para codificar los paquetes (basado en el paquete [`@msgpack/msgpack`](https://github.com/msgpack/msgpack-javascript)). |
+| [`socket.io-json-parser`](https://www.npmjs.com/package/socket.io-json-parser) | Usa `JSON.stringify()` y `JSON.parse()` para codificar los paquetes. |
+| [`socket.io-cbor-x-parser`](https://www.npmjs.com/package/socket.io-cbor-x-parser) | Usa [cbor-x](https://github.com/kriszyp/cbor-x) para codificar los paquetes. |
+| [`@socket.io/devalue-parser`](https://www.npmjs.com/package/@socket.io/devalue-parser) | Usa [devalue](https://github.com/Rich-Harris/devalue) para codificar los paquetes. |
+
+## Implementando tu propio parser
+
+Aquí hay un ejemplo básico con un parser que usa los métodos `JSON.stringify()` y `JSON.parse()`:
+
+```js
+import { Emitter } from "@socket.io/component-emitter"; // polyfill de Node.js EventEmitter en el navegador
+
+class Encoder {
+ /**
+ * Codificar un paquete en una lista de strings/buffers
+ */
+ encode(packet) {
+ return [JSON.stringify(packet)];
+ }
+}
+
+function isObject(value) {
+ return Object.prototype.toString.call(value) === "[object Object]";
+}
+
+class Decoder extends Emitter {
+ /**
+ * Recibir un fragmento (string o buffer) y opcionalmente emitir un evento "decoded" con el paquete reconstruido
+ */
+ add(chunk) {
+ const packet = JSON.parse(chunk);
+ if (this.isPacketValid(packet)) {
+ this.emit("decoded", packet);
+ } else {
+ throw new Error("formato inválido");
+ }
+ }
+ isPacketValid({ type, data, nsp, id }) {
+ const isNamespaceValid = typeof nsp === "string";
+ const isAckIdValid = id === undefined || Number.isInteger(id);
+ if (!isNamespaceValid || !isAckIdValid) {
+ return false;
+ }
+ switch (type) {
+ case 0: // CONNECT
+ return data === undefined || isObject(data);
+ case 1: // DISCONNECT
+ return data === undefined;
+ case 2: // EVENT
+ return Array.isArray(data) && typeof data[0] === "string";
+ case 3: // ACK
+ return Array.isArray(data);
+ case 4: // CONNECT_ERROR
+ return isObject(data);
+ default:
+ return false;
+ }
+ }
+ /**
+ * Limpiar buffers internos
+ */
+ destroy() {}
+}
+
+export const parser = { Encoder, Decoder };
+```
+
+## El parser por defecto
+
+El código fuente del parser por defecto (el paquete `socket.io-parser`) se puede encontrar aquí: https://github.com/socketio/socket.io-parser
+
+Ejemplo de salida:
+
+- emit básico
+
+```js
+socket.emit("test", 42);
+```
+
+será codificado como:
+
+```
+2["test",42]
+|||
+||└─ carga útil codificada en JSON
+|└─ tipo de paquete (2 => EVENT)
+```
+
+- emit con binario, acknowledgement y namespace personalizado
+
+```js
+socket.emit("test", Uint8Array.from([42]), () => {
+ console.log("ack recibido");
+});
+```
+
+será codificado como:
+
+```
+51-/admin,13["test",{"_placeholder":true,"num":0}]
+||||| || └─ carga útil codificada en JSON con placeholders para adjuntos binarios
+||||| |└─ id de acknowledgement
+||||| └─ separador
+||||└─ namespace (no incluido cuando es el namespace principal)
+|||└─ separador
+||└─ número de adjuntos binarios
+|└─ tipo de paquete (5 => BINARY EVENT)
+
+y un adjunto adicional (el Uint8Array extraído)
+```
+
+Pros:
+
+- el adjunto binario se codifica en base64, así que este parser es compatible con navegadores que [no soportan Arraybuffers](https://caniuse.com/mdn-javascript_builtins_arraybuffer), como IE9
+
+Contras:
+
+- los paquetes con contenido binario se envían como dos frames WebSocket distintos (si la conexión WebSocket está establecida)
+
+## El parser msgpack
+
+Este parser usa el formato de serialización [MessagePack](https://msgpack.org/).
+
+El código fuente de este parser se puede encontrar aquí: https://github.com/socketio/socket.io-msgpack-parser
+
+Uso de ejemplo:
+
+*Servidor*
+
+```js
+import { Server } from "socket.io";
+import customParser from "socket.io-msgpack-parser";
+
+const io = new Server({
+ parser: customParser
+});
+```
+
+*Cliente (Node.js)*
+
+```js
+import { io } from "socket.io-client";
+import customParser from "socket.io-msgpack-parser";
+
+const socket = io("https://example.com", {
+ parser: customParser
+});
+```
+
+En el navegador, ahora hay un bundle oficial que incluye este parser:
+
+- https://cdn.socket.io/4.8.1/socket.io.msgpack.min.js
+- cdnjs: https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.8.1/socket.io.msgpack.min.js
+- jsDelivr: https://cdn.jsdelivr.net/npm/socket.io-client@4.8.1/dist/socket.io.msgpack.min.js
+- unpkg: https://unpkg.com/socket.io-client@4.8.1/dist/socket.io.msgpack.min.js
+
+En ese caso, no necesitas especificar la opción `parser`.
+
+Pros:
+
+- los paquetes con contenido binario se envían como un solo frame WebSocket (si la conexión WebSocket está establecida)
+- puede resultar en cargas útiles más pequeñas (especialmente cuando se usan muchos números)
+
+Contras:
+
+- incompatible con navegadores que [no soportan Arraybuffers](https://caniuse.com/mdn-javascript_builtins_arraybuffer), como IE9
+- más difícil de depurar en la pestaña Network del navegador
+
+:::info
+
+Por favor ten en cuenta que `socket.io-msgpack-parser` se basa en la implementación de MessagePack [`notepack.io`](https://github.com/darrachequesne/notepack). Esta implementación se enfoca principalmente en el rendimiento y el tamaño mínimo del bundle, y por lo tanto no soporta características como tipos de extensión. Para un parser basado en la [implementación oficial de JavaScript](https://github.com/msgpack/msgpack-javascript), por favor revisa [este paquete](https://www.npmjs.com/package/@skgdev/socket.io-msgpack-javascript).
+
+:::
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/load-testing.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/load-testing.md
new file mode 100644
index 00000000..683071f1
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/load-testing.md
@@ -0,0 +1,152 @@
+---
+title: Pruebas de carga
+sidebar_position: 5
+slug: /load-testing/
+---
+
+Ya que Socket.IO tiene su [propio protocolo](https://github.com/socketio/socket.io-protocol), incluyendo handshake, heartbeats y codificación de paquetes personalizada, la forma más fácil de hacer pruebas de carga a tu servidor Socket.IO es usar la biblioteca cliente de Socket.IO y crear *muchos* clientes.
+
+Hay dos soluciones clásicas para hacer esto:
+
+- usando [Artillery](#artillery)
+- o [gestionar manualmente los clientes](#creación-manual-de-clientes)
+
+## Artillery
+
+Artillery es una gran herramienta para hacer pruebas de carga a tu aplicación. Permite crear conexiones, enviar eventos y verificar acknowledgements.
+
+La documentación se puede encontrar [aquí](https://artillery.io/docs/guides/guides/socketio-reference.html).
+
+**Nota importante**: la instalación predeterminada viene con un cliente v2, que no es [compatible](../03-Client/client-installation.md#version-compatibility) con un servidor v3/v4. Necesitas instalar un motor personalizado para esto: https://github.com/ptejada/artillery-engine-socketio-v3
+
+Instalación:
+
+```
+$ npm install artillery artillery-engine-socketio-v3
+```
+
+Escenario de ejemplo:
+
+```yaml
+# my-scenario.yml
+config:
+ target: "http://localhost:3000"
+ phases:
+ - duration: 60
+ arrivalRate: 10
+ engines:
+ socketio-v3: {}
+
+scenarios:
+ - name: Mi escenario de ejemplo
+ engine: socketio-v3
+ flow:
+ # esperar la actualización a WebSocket (opcional)
+ - think: 1
+
+ # emit básico
+ - emit:
+ channel: "hello"
+ data: "world"
+
+ # emitir un objeto
+ - emit:
+ channel: "hello"
+ data:
+ id: 42
+ status: "en progreso"
+ tags:
+ - "tag1"
+ - "tag2"
+
+ # emitir en un namespace personalizado
+ - namespace: "/my-namespace"
+ emit:
+ channel: "hello"
+ data: "world"
+
+ # emitir con acknowledgement
+ - emit:
+ channel: "ping"
+ acknowledge:
+ match:
+ value: "pong"
+
+ # no hacer nada por 30 segundos y luego desconectar
+ - think: 30
+```
+
+Para ejecutar este escenario:
+
+```
+$ npx artillery run my-scenario.yml
+```
+
+Artillery también viene con muchas características increíbles, como la capacidad de [publicar las métricas a varios endpoints](https://artillery.io/docs/guides/plugins/plugin-publish-metrics.html) o [ejecutar las pruebas desde AWS](https://artillery.io/docs/guides/guides/running-tests-with-artillery-pro.html).
+
+Su única limitación es que no puedes probar fácilmente eventos de servidor a cliente, ya que el DSL de Artillery está más orientado a la comunicación clásica de cliente a servidor. Lo que nos lleva a [nuestra siguiente sección](#creación-manual-de-clientes).
+
+## Creación manual de clientes
+
+Aquí hay un script básico para crear mil clientes Socket.IO y monitorear el número de paquetes recibidos por segundo:
+
+```js
+const { io } = require("socket.io-client");
+
+const URL = process.env.URL || "http://localhost:3000";
+const MAX_CLIENTS = 1000;
+const POLLING_PERCENTAGE = 0.05;
+const CLIENT_CREATION_INTERVAL_IN_MS = 10;
+const EMIT_INTERVAL_IN_MS = 1000;
+
+let clientCount = 0;
+let lastReport = new Date().getTime();
+let packetsSinceLastReport = 0;
+
+const createClient = () => {
+ // para fines de demostración, algunos clientes permanecen atascados en HTTP long-polling
+ const transports =
+ Math.random() < POLLING_PERCENTAGE ? ["polling"] : ["polling", "websocket"];
+
+ const socket = io(URL, {
+ transports,
+ });
+
+ setInterval(() => {
+ socket.emit("evento de cliente a servidor");
+ }, EMIT_INTERVAL_IN_MS);
+
+ socket.on("evento de servidor a cliente", () => {
+ packetsSinceLastReport++;
+ });
+
+ socket.on("disconnect", (reason) => {
+ console.log(`desconectado debido a ${reason}`);
+ });
+
+ if (++clientCount < MAX_CLIENTS) {
+ setTimeout(createClient, CLIENT_CREATION_INTERVAL_IN_MS);
+ }
+};
+
+createClient();
+
+const printReport = () => {
+ const now = new Date().getTime();
+ const durationSinceLastReport = (now - lastReport) / 1000;
+ const packetsPerSeconds = (
+ packetsSinceLastReport / durationSinceLastReport
+ ).toFixed(2);
+
+ console.log(
+ `cantidad de clientes: ${clientCount} ; promedio de paquetes recibidos por segundo: ${packetsPerSeconds}`
+ );
+
+ packetsSinceLastReport = 0;
+ lastReport = now;
+};
+
+setInterval(printReport, 5000);
+```
+
+Puedes usarlo como punto de partida para hacer pruebas de carga a tu propia aplicación.
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/namespaces.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/namespaces.md
new file mode 100644
index 00000000..15909a0f
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/namespaces.md
@@ -0,0 +1,242 @@
+---
+title: Namespaces
+sidebar_position: 1
+slug: /namespaces/
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+Un Namespace es un canal de comunicación que te permite dividir la lógica de tu aplicación sobre una única conexión compartida (también llamado "multiplexación").
+
+
+
+## Introducción
+
+Cada namespace tiene sus propios:
+
+- [manejadores de eventos](../04-Events/listening-to-events.md)
+
+```js
+io.of("/orders").on("connection", (socket) => {
+ socket.on("order:list", () => {});
+ socket.on("order:create", () => {});
+});
+
+io.of("/users").on("connection", (socket) => {
+ socket.on("user:list", () => {});
+});
+```
+
+- [salas](../04-Events/rooms.md)
+
+```js
+const orderNamespace = io.of("/orders");
+
+orderNamespace.on("connection", (socket) => {
+ socket.join("room1");
+ orderNamespace.to("room1").emit("hello");
+});
+
+const userNamespace = io.of("/users");
+
+userNamespace.on("connection", (socket) => {
+ socket.join("room1"); // distinta de la sala en el namespace "orders"
+ userNamespace.to("room1").emit("holà");
+});
+```
+
+- [middlewares](../02-Server/middlewares.md)
+
+```js
+const orderNamespace = io.of("/orders");
+
+orderNamespace.use((socket, next) => {
+ // asegurar que el socket tiene acceso al namespace "orders", y luego
+ next();
+});
+
+const userNamespace = io.of("/users");
+
+userNamespace.use((socket, next) => {
+ // asegurar que el socket tiene acceso al namespace "users", y luego
+ next();
+});
+```
+
+Posibles casos de uso:
+
+- quieres crear un namespace especial al que solo usuarios autorizados tienen acceso, así la lógica relacionada con esos usuarios está separada del resto de la aplicación
+
+```js
+const adminNamespace = io.of("/admin");
+
+adminNamespace.use((socket, next) => {
+ // asegurar que el usuario tiene suficientes derechos
+ next();
+});
+
+adminNamespace.on("connection", socket => {
+ socket.on("delete user", () => {
+ // ...
+ });
+});
+```
+
+- tu aplicación tiene múltiples tenants así que quieres crear dinámicamente un namespace por tenant
+
+```js
+const workspaces = io.of(/^\/\w+$/);
+
+workspaces.on("connection", socket => {
+ const workspace = socket.nsp;
+
+ workspace.emit("hello");
+});
+```
+
+## Namespace principal
+
+Hasta ahora, has interactuado con el namespace principal, llamado `/`. La instancia `io` hereda todos sus métodos:
+
+```js
+io.on("connection", (socket) => {});
+io.use((socket, next) => { next() });
+io.emit("hello");
+// son en realidad equivalentes a
+io.of("/").on("connection", (socket) => {});
+io.of("/").use((socket, next) => { next() });
+io.of("/").emit("hello");
+```
+
+Algunos tutoriales también pueden mencionar `io.sockets`, es simplemente un alias para `io.of("/")`.
+
+```js
+io.sockets === io.of("/")
+```
+
+## Namespaces personalizados
+
+Para configurar un namespace personalizado, puedes llamar a la función `of` en el lado del servidor:
+
+```js
+const nsp = io.of("/my-namespace");
+
+nsp.on("connection", socket => {
+ console.log("alguien se conectó");
+});
+
+nsp.emit("hi", "¡todos!");
+```
+
+## Inicialización del cliente
+
+Versión del mismo origen:
+
+```js
+const socket = io(); // o io("/"), el namespace principal
+const orderSocket = io("/orders"); // el namespace "orders"
+const userSocket = io("/users"); // el namespace "users"
+```
+
+Versión cross-origin/Node.js:
+
+```js
+const socket = io("https://example.com"); // o io("https://example.com/"), el namespace principal
+const orderSocket = io("https://example.com/orders"); // el namespace "orders"
+const userSocket = io("https://example.com/users"); // el namespace "users"
+```
+
+En el ejemplo anterior, solo se establecerá una conexión WebSocket, y los paquetes serán enrutados automáticamente al namespace correcto.
+
+Por favor nota que la multiplexación será deshabilitada en los siguientes casos:
+
+- múltiples creaciones para el mismo namespace
+
+```js
+const socket1 = io();
+const socket2 = io(); // sin multiplexación, dos conexiones WebSocket distintas
+```
+
+- diferentes dominios
+
+```js
+const socket1 = io("https://first.example.com");
+const socket2 = io("https://second.example.com"); // sin multiplexación, dos conexiones WebSocket distintas
+```
+
+- uso de la opción [forceNew](../../client-options.md#forcenew)
+
+```js
+const socket1 = io();
+const socket2 = io("/admin", { forceNew: true }); // sin multiplexación, dos conexiones WebSocket distintas
+```
+
+## Namespaces dinámicos
+
+También es posible crear namespaces dinámicamente, ya sea con una expresión regular:
+
+```js
+io.of(/^\/dynamic-\d+$/);
+```
+
+o con una función:
+
+```js
+io.of((name, auth, next) => {
+ next(null, true); // o false, cuando la creación es denegada
+});
+```
+
+Puedes tener acceso al nuevo namespace en el evento `connection`:
+
+```js
+io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
+ const namespace = socket.nsp;
+});
+```
+
+El valor de retorno del método `of()` es lo que llamamos el namespace padre, desde el cual puedes:
+
+- registrar [middlewares](../02-Server/middlewares.md)
+
+```js
+const parentNamespace = io.of(/^\/dynamic-\d+$/);
+
+parentNamespace.use((socket, next) => { next() });
+```
+
+El middleware se registrará automáticamente en cada namespace hijo.
+
+- [transmitir](../04-Events/broadcasting-events.md) eventos
+
+```js
+const parentNamespace = io.of(/^\/dynamic-\d+$/);
+
+parentNamespace.emit("hello"); // se enviará a usuarios en /dynamic-1, /dynamic-2, ...
+```
+
+:::caution
+
+Los namespaces existentes tienen prioridad sobre los namespaces dinámicos. Por ejemplo:
+
+```js
+// registrar namespace "dynamic-101"
+io.of("/dynamic-101");
+
+io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
+ // no se llamará para una conexión en el namespace "dynamic-101"
+});
+```
+
+:::
+
+## API completa
+
+La API completa expuesta por la instancia Namespace se puede encontrar [aquí](../../server-api.md#namespace).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/performance-tuning.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/performance-tuning.md
new file mode 100644
index 00000000..803dd1bd
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/performance-tuning.md
@@ -0,0 +1,139 @@
+---
+title: Ajuste de rendimiento
+sidebar_position: 6
+slug: /performance-tuning/
+---
+
+Aquí hay algunos consejos para mejorar el rendimiento de tu servidor Socket.IO:
+
+- [a nivel de Socket.IO](#a-nivel-de-socketio)
+- [a nivel del sistema operativo](#a-nivel-del-sistema-operativo)
+
+También podrías estar interesado en [escalar a múltiples nodos](../02-Server/using-multiple-nodes.md).
+
+## A nivel de Socket.IO
+
+Ya que, en la mayoría de los casos, la conexión Socket.IO se establecerá con WebSocket, el rendimiento de tu servidor Socket.IO estará fuertemente ligado al rendimiento del servidor WebSocket subyacente ([`ws`](https://github.com/websockets/ws), por defecto).
+
+### Instalar add-ons nativos de `ws`
+
+`ws` viene con dos add-ons binarios opcionales que mejoran ciertas operaciones. Los binarios precompilados están disponibles para las plataformas más populares, así que no necesariamente necesitas tener un compilador C++ instalado en tu máquina.
+
+- [bufferutil](https://www.npmjs.com/package/bufferutil): Permite realizar eficientemente operaciones como enmascarar y desenmascarar el payload de datos de los frames WebSocket.
+- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate): Permite verificar eficientemente si un mensaje contiene UTF-8 válido como lo requiere la especificación.
+
+Para instalar esos paquetes:
+
+```
+$ npm install --save-optional bufferutil utf-8-validate
+```
+
+Por favor nota que estos paquetes son opcionales, el servidor WebSocket usará la implementación en Javascript si no están disponibles. Más información se puede encontrar [aquí](https://github.com/websockets/ws/#opt-in-for-performance-and-spec-compliance).
+
+### Usar otra implementación de servidor WebSocket
+
+Por ejemplo, puedes usar el paquete [eiows](https://www.npmjs.com/package/eiows), que es un fork del paquete (ahora obsoleto) [uws](https://www.npmjs.com/package/uws):
+
+```
+$ npm install eiows
+```
+
+Y luego usar la opción [`wsEngine`](../../server-options.md#wsengine):
+
+```js
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ wsEngine: require("eiows").Server
+});
+```
+
+### Usar un parser personalizado
+
+Si envías datos binarios sobre la conexión Socket.IO, usar un [parser personalizado](custom-parser.md) como el basado en [msgpack](custom-parser.md#the-msgpack-parser) podría ser interesante, ya que por defecto cada buffer se enviará en su propio frame WebSocket.
+
+Uso:
+
+*Servidor*
+
+```js
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+const parser = require("socket.io-msgpack-parser");
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ parser
+});
+```
+
+*Cliente*
+
+```js
+const { io } = require("socket.io-client");
+const parser = require("socket.io-msgpack-parser");
+
+const socket = io("https://example.com", {
+ parser
+});
+```
+
+### Descartar la solicitud HTTP inicial
+
+Por defecto, se mantiene en memoria una referencia a la primera solicitud HTTP de cada sesión. Esta referencia es necesaria cuando se trabaja con `express-session` por ejemplo (ver [aquí](/how-to/use-with-express-session)), pero puede descartarse para ahorrar memoria:
+
+```js
+io.engine.on("connection", (rawSocket) => {
+ rawSocket.request = null;
+});
+```
+
+## A nivel del sistema operativo
+
+Hay muchos buenos artículos sobre cómo ajustar tu sistema operativo para aceptar un gran número de conexiones. Por favor consulta [este](https://medium.com/@elliekang/scaling-to-a-millions-websocket-concurrent-connections-at-spoon-radio-bbadd6ec1901) por ejemplo.
+
+Al hacer [pruebas de carga](load-testing.md) a tu servidor Socket.IO, probablemente alcanzarás los dos siguientes límites:
+
+- número máximo de archivos abiertos
+
+Si no puedes superar las 1000 conexiones concurrentes (nuevos clientes no pueden conectarse), muy probablemente has alcanzado el número máximo de archivos abiertos:
+
+```
+$ ulimit -n
+1024
+```
+
+Para aumentar este número, crea un nuevo archivo `/etc/security/limits.d/custom.conf` con el siguiente contenido (requiere privilegios de root):
+
+```
+* soft nofile 1048576
+* hard nofile 1048576
+```
+
+Y luego recarga tu sesión. Tu nuevo límite ahora debería estar actualizado:
+
+```
+$ ulimit -n
+1048576
+```
+
+- número máximo de puertos locales disponibles
+
+Si no puedes superar las 28000 conexiones concurrentes, muy probablemente has alcanzado el número máximo de puertos locales disponibles:
+
+```
+$ cat /proc/sys/net/ipv4/ip_local_port_range
+32768 60999
+```
+
+Para aumentar este número, crea un nuevo archivo `/etc/sysctl.d/net.ipv4.ip_local_port_range.conf` con el siguiente contenido (de nuevo, requiere privilegios de root):
+
+```
+net.ipv4.ip_local_port_range = 10000 65535
+```
+
+Nota: usamos `10000` como límite inferior para que no incluya los puertos que son usados por los servicios en la máquina (como `5432` para un servidor PostgreSQL), pero puedes totalmente usar un valor más bajo (hasta `1024`).
+
+Una vez que reinicies tu máquina, ahora podrás alegremente llegar a 55k conexiones concurrentes (por IP entrante).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/usage-with-pm2.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/usage-with-pm2.md
new file mode 100644
index 00000000..ac649db7
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/06-Advanced/usage-with-pm2.md
@@ -0,0 +1,98 @@
+---
+title: Uso con PM2
+sidebar_position: 4
+slug: /pm2/
+---
+
+PM2 es un gestor de procesos de producción para aplicaciones Node.js con un balanceador de carga incorporado. Te permite mantener las aplicaciones vivas para siempre, recargarlas sin tiempo de inactividad y facilitar tareas comunes de administración del sistema.
+
+Puedes encontrar su documentación aquí: https://pm2.keymetrics.io/docs/usage/pm2-doc-single-page/
+
+Para escalar un servidor Socket.IO con PM2, hay tres soluciones:
+
+- deshabilitar HTTP long-polling en el lado del cliente
+
+```js
+const socket = io({
+ transports: ["websocket"]
+});
+```
+
+Aunque en ese caso, no habrá fallback a HTTP long-polling si la conexión WebSocket no puede establecerse.
+
+- usar un puerto distinto para cada worker, y un balanceador de carga como nginx delante de ellos
+
+- usar `@socket.io/pm2`
+
+## Instalación
+
+```
+npm install -g @socket.io/pm2
+```
+
+Si `pm2` ya está instalado, tendrás que eliminarlo primero:
+
+```
+npm remove -g pm2
+```
+
+`@socket.io/pm2` puede usarse como un reemplazo directo de `pm2`, y soporta todos los comandos de la utilidad clásica `pm2`.
+
+La única diferencia viene de [este commit](https://github.com/socketio/pm2/commit/8c29a7feb6cbde3c8ef9eb072fee284686f1553f).
+
+## Uso
+
+`worker.js`
+
+```js
+const { createServer } = require("http");
+const { Server } = require("socket.io");
+const { createAdapter } = require("@socket.io/cluster-adapter");
+const { setupWorker } = require("@socket.io/sticky");
+
+const httpServer = createServer();
+const io = new Server(httpServer);
+
+io.adapter(createAdapter());
+
+setupWorker(io);
+
+io.on("connection", (socket) => {
+ console.log(`conectado ${socket.id}`);
+});
+```
+
+`ecosystem.config.js`
+
+```js
+module.exports = {
+ apps : [{
+ script : "worker.js",
+ instances : "max",
+ exec_mode : "cluster"
+ }]
+}
+```
+
+Y luego ejecuta `pm2 start ecosystem.config.js` (o `pm2 start worker.js -i 0`). ¡Eso es todo! Ahora puedes alcanzar el clúster Socket.IO en el puerto 8080.
+
+## Cómo funciona
+
+Al [escalar a múltiples nodos](../02-Server/using-multiple-nodes.md), hay dos cosas que hacer:
+
+- habilitar sesiones sticky, para que las solicitudes HTTP de una sesión Socket.IO sean enrutadas al mismo worker
+- usar un adaptador personalizado, para que los paquetes sean transmitidos a todos los clientes, incluso si están conectados a otro worker
+
+Para lograr esto, `@socket.io/pm2` incluye dos paquetes adicionales:
+
+- [`@socket.io/sticky`](https://github.com/socketio/socket.io-sticky)
+- [`@socket.io/cluster-adapter`](https://github.com/socketio/socket.io-cluster-adapter)
+
+La única diferencia con `pm2` viene de [este commit](https://github.com/socketio/pm2/commit/8c29a7feb6cbde3c8ef9eb072fee284686f1553f):
+
+- el proceso God ahora crea su propio servidor HTTP y enruta las solicitudes HTTP al worker correcto
+- el proceso God también retransmite los paquetes entre los workers, para que `io.emit()` alcance correctamente a todos los clientes
+
+Por favor nota que si tienes varios hosts cada uno ejecutando un clúster PM2, tendrás que usar otro adaptador, como el [adaptador Redis](../05-Adapters/adapter-redis.md).
+
+El código fuente del fork se puede encontrar [aquí](https://github.com/socketio/pm2). Intentaremos seguir de cerca las versiones del paquete `pm2`.
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-2-to-3.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-2-to-3.md
new file mode 100644
index 00000000..b1dc4706
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-2-to-3.md
@@ -0,0 +1,965 @@
+---
+title: Migrando de 2.x a 3.0
+sidebar_position: 1
+slug: /migrating-from-2-x-to-3-0/
+toc_max_heading_level: 4
+---
+
+Esta versión debería solucionar la mayoría de las inconsistencias de la biblioteca Socket.IO y proporcionar un comportamiento más intuitivo para los usuarios finales. Es el resultado de los comentarios de la comunidad a lo largo de los años. ¡Un gran agradecimiento a todos los involucrados!
+
+**TL;DR:** ~~debido a varios cambios incompatibles, un cliente v2 no podrá conectarse a un servidor v3 (y viceversa)~~
+
+Actualización: A partir de [Socket.IO 3.1.0](/blog/socket-io-3-1-0/), el servidor v3 ahora puede comunicarse con clientes v2. Más información [abajo](#cómo-actualizar-un-despliegue-en-producción-existente). Un cliente v3 todavía no puede conectarse a un servidor v2.
+
+Para los detalles de bajo nivel, por favor consulte:
+
+- [Protocolo Engine.IO v4](https://github.com/socketio/engine.io-protocol#difference-between-v3-and-v4)
+- [Protocolo Socket.IO v5](https://github.com/socketio/socket.io-protocol#difference-between-v5-and-v4)
+
+Aquí está la lista completa de cambios:
+
+- [Configuración](#configuración)
+ - [Valores predeterminados más sensatos](#valores-predeterminados-más-sensatos)
+ - [Manejo de CORS](#manejo-de-cors)
+ - [Ya no hay cookie por defecto](#ya-no-hay-cookie-por-defecto)
+- [Cambios en la API](#cambios-en-la-api)
+ - [io.set() ha sido eliminado](#ioset-ha-sido-eliminado)
+ - [Ya no hay conexión implícita al namespace por defecto](#ya-no-hay-conexión-implícita-al-namespace-por-defecto)
+ - [Namespace.connected ha sido renombrado a Namespace.sockets y ahora es un Map](#namespaceconnected-ha-sido-renombrado-a-namespacesockets-y-ahora-es-un-map)
+ - [Socket.rooms ahora es un Set](#socketrooms-ahora-es-un-set)
+ - [Socket.binary() ha sido eliminado](#socketbinary-ha-sido-eliminado)
+ - [Socket.join() y Socket.leave() ahora son síncronos](#socketjoin-y-socketleave-ahora-son-síncronos)
+ - [Socket.use() ha sido eliminado](#socketuse-ha-sido-eliminado)
+ - [Un error de middleware ahora emitirá un objeto Error](#un-error-de-middleware-ahora-emitirá-un-objeto-error)
+ - [Clara distinción entre la opción query del Manager y la opción query del Socket](#clara-distinción-entre-la-opción-query-del-manager-y-la-opción-query-del-socket)
+ - [La instancia Socket ya no reenviará los eventos emitidos por su Manager](#la-instancia-socket-ya-no-reenviará-los-eventos-emitidos-por-su-manager)
+ - [Namespace.clients() ha sido renombrado a Namespace.allSockets() y ahora devuelve una Promise](#namespaceclients-ha-sido-renombrado-a-namespaceallsockets-y-ahora-devuelve-una-promise)
+ - [Bundles del cliente](#bundles-del-cliente)
+ - [Ya no hay evento "pong" para recuperar la latencia](#ya-no-hay-evento-pong-para-recuperar-la-latencia)
+ - [Sintaxis de módulos ES](#sintaxis-de-módulos-es)
+ - [Las cadenas de `emit()` ya no son posibles](#las-cadenas-de-emit-ya-no-son-posibles)
+ - [Los nombres de las salas ya no se convierten a string](#los-nombres-de-las-salas-ya-no-se-convierten-a-string)
+- [Nuevas características](#nuevas-características)
+ - [Listeners catch-all](#listeners-catch-all)
+ - [Eventos volátiles (cliente)](#eventos-volátiles-cliente)
+ - [Bundle oficial con el parser msgpack](#bundle-oficial-con-el-parser-msgpack)
+- [Misceláneos](#misceláneos)
+ - [El código fuente de Socket.IO ha sido reescrito en TypeScript](#el-código-fuente-de-socketio-ha-sido-reescrito-en-typescript)
+ - [El soporte para IE8 y Node.js 8 ha sido oficialmente eliminado](#el-soporte-para-ie8-y-nodejs-8-ha-sido-oficialmente-eliminado)
+
+- [Cómo actualizar un despliegue en producción existente](#cómo-actualizar-un-despliegue-en-producción-existente)
+- [Problemas conocidos de migración](#problemas-conocidos-de-migración)
+
+### Configuración
+
+#### Valores predeterminados más sensatos
+
+- el valor predeterminado de `maxHttpBufferSize` se redujo de `100MB` a `1MB`.
+- la extensión WebSocket [permessage-deflate](https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-19) ahora está deshabilitada por defecto
+- ahora debes listar explícitamente los dominios permitidos (para CORS, ver [abajo](#manejo-de-cors))
+- la opción `withCredentials` ahora es `false` por defecto en el lado del cliente
+
+#### Manejo de CORS
+
+En v2, el servidor Socket.IO añadía automáticamente las cabeceras necesarias para permitir [Cross-Origin Resource Sharing](https://developer.mozilla.org/es/docs/Web/HTTP/CORS) (CORS).
+
+Este comportamiento, aunque conveniente, no era bueno en términos de seguridad, porque significaba que todos los dominios podían acceder a tu servidor Socket.IO, a menos que se especificara lo contrario con la opción `origins`.
+
+Por eso, a partir de Socket.IO v3:
+
+- CORS ahora está deshabilitado por defecto
+- la opción `origins` (usada para proporcionar una lista de dominios autorizados) y la opción `handlePreflightRequest` (usada para editar las cabeceras `Access-Control-Allow-xxx`) son reemplazadas por la opción `cors`, que se reenviará al paquete [cors](https://www.npmjs.com/package/cors).
+
+La lista completa de opciones se puede encontrar [aquí](https://github.com/expressjs/cors#configuration-options).
+
+Antes:
+
+```js
+const io = require("socket.io")(httpServer, {
+ origins: ["https://example.com"],
+
+ // opcional, útil para cabeceras personalizadas
+ handlePreflightRequest: (req, res) => {
+ res.writeHead(200, {
+ "Access-Control-Allow-Origin": "https://example.com",
+ "Access-Control-Allow-Methods": "GET,POST",
+ "Access-Control-Allow-Headers": "my-custom-header",
+ "Access-Control-Allow-Credentials": true
+ });
+ res.end();
+ }
+});
+```
+
+Después:
+
+```js
+const io = require("socket.io")(httpServer, {
+ cors: {
+ origin: "https://example.com",
+ methods: ["GET", "POST"],
+ allowedHeaders: ["my-custom-header"],
+ credentials: true
+ }
+});
+```
+
+
+#### Ya no hay cookie por defecto
+
+En versiones anteriores, una cookie `io` se enviaba por defecto. Esta cookie se puede usar para habilitar sticky-session, que todavía es necesario cuando tienes varios servidores y HTTP long-polling habilitado (más información [aquí](../02-Server/using-multiple-nodes.md)).
+
+Sin embargo, esta cookie no es necesaria en algunos casos (es decir, despliegue con un solo servidor, sticky-session basado en IP) por lo que ahora debe habilitarse explícitamente.
+
+Antes:
+
+```js
+const io = require("socket.io")(httpServer, {
+ cookieName: "io",
+ cookieHttpOnly: false,
+ cookiePath: "/custom"
+});
+```
+
+Después:
+
+```js
+const io = require("socket.io")(httpServer, {
+ cookie: {
+ name: "test",
+ httpOnly: false,
+ path: "/custom"
+ }
+});
+```
+
+Todas las demás opciones (domain, maxAge, sameSite, ...) ahora son soportadas. Por favor consulte [aquí](https://github.com/jshttp/cookie/) para la lista completa de opciones.
+
+
+### Cambios en la API
+
+A continuación se listan los cambios que no son compatibles hacia atrás.
+
+#### io.set() ha sido eliminado
+
+Este método fue deprecado en la versión 1.0 y mantenido para compatibilidad hacia atrás. Ahora ha sido eliminado.
+
+Fue reemplazado por middlewares.
+
+Antes:
+
+```js
+io.set("authorization", (handshakeData, callback) => {
+ // asegurarse de que los datos del handshake se vean bien
+ callback(null, true); // primero el error, segundo el booleano "autorizado"
+});
+```
+
+Después:
+
+```js
+io.use((socket, next) => {
+ var handshakeData = socket.request;
+ // asegurarse de que los datos del handshake se vean bien como antes
+ // si hay error hacer esto:
+ // next(new Error("no autorizado"));
+ // sino solo llamar next
+ next();
+});
+```
+
+#### Ya no hay conexión implícita al namespace por defecto
+
+Este cambio afecta a los usuarios de la funcionalidad de multiplexación (lo que llamamos Namespace en Socket.IO).
+
+En versiones anteriores, un cliente siempre se conectaba al namespace por defecto (`/`), incluso si solicitaba acceso a otro namespace. Esto significaba que los middlewares registrados para el namespace por defecto se activaban, lo cual puede ser bastante sorprendente.
+
+```js
+// lado del cliente
+const socket = io("/admin");
+
+// lado del servidor
+io.use((socket, next) => {
+ // ya no se activa
+});
+
+io.on("connection", socket => {
+ // ya no se activa
+})
+
+io.of("/admin").use((socket, next) => {
+ // se activa
+});
+```
+
+Además, ahora nos referiremos al namespace "principal" en lugar del namespace "por defecto".
+
+
+#### Namespace.connected ha sido renombrado a Namespace.sockets y ahora es un Map
+
+El objeto `connected` (usado para almacenar todos los Socket conectados al Namespace dado) podía usarse para recuperar un objeto Socket a partir de su id. Ahora es un [Map](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Map) de ES6.
+
+Antes:
+
+```js
+// obtener un socket por ID en el namespace principal
+const socket = io.of("/").connected[socketId];
+
+// obtener un socket por ID en el namespace "admin"
+const socket = io.of("/admin").connected[socketId];
+
+// recorrer todos los sockets
+const sockets = io.of("/").connected;
+for (const id in sockets) {
+ if (sockets.hasOwnProperty(id)) {
+ const socket = sockets[id];
+ // ...
+ }
+}
+
+// obtener el número de sockets conectados
+const count = Object.keys(io.of("/").connected).length;
+```
+
+Después:
+
+```js
+// obtener un socket por ID en el namespace principal
+const socket = io.of("/").sockets.get(socketId);
+
+// obtener un socket por ID en el namespace "admin"
+const socket = io.of("/admin").sockets.get(socketId);
+
+// recorrer todos los sockets
+for (const [_, socket] of io.of("/").sockets) {
+ // ...
+}
+
+// obtener el número de sockets conectados
+const count = io.of("/").sockets.size;
+```
+
+#### Socket.rooms ahora es un Set
+
+La propiedad `rooms` contiene la lista de salas en las que el Socket se encuentra actualmente. Era un objeto, ahora es un [Set](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Set) de ES6.
+
+Antes:
+
+```js
+io.on("connection", (socket) => {
+
+ console.log(Object.keys(socket.rooms)); // [ ]
+
+ socket.join("room1");
+
+ console.log(Object.keys(socket.rooms)); // [ , "room1" ]
+
+});
+```
+
+Después:
+
+```js
+io.on("connection", (socket) => {
+
+ console.log(socket.rooms); // Set { }
+
+ socket.join("room1");
+
+ console.log(socket.rooms); // Set { , "room1" }
+
+});
+```
+
+#### Socket.binary() ha sido eliminado
+
+El método `binary` podía usarse para indicar que un evento dado no contenía datos binarios (para omitir la búsqueda realizada por la biblioteca y mejorar el rendimiento en ciertas condiciones).
+
+Fue reemplazado por la capacidad de proporcionar tu propio parser, que fue añadida en Socket.IO 2.0.
+
+Antes:
+
+```js
+socket.binary(false).emit("hello", "sin binarios");
+```
+
+Después:
+
+```js
+const io = require("socket.io")(httpServer, {
+ parser: myCustomParser
+});
+```
+
+Por favor consulte [socket.io-msgpack-parser](https://github.com/socketio/socket.io-msgpack-parser) como ejemplo.
+
+
+#### Socket.join() y Socket.leave() ahora son síncronos
+
+La asincronicidad era necesaria para las primeras versiones del adaptador Redis, pero este ya no es el caso.
+
+Para referencia, un Adapter es un objeto que almacena las relaciones entre Sockets y [Rooms](../04-Events/rooms.md). Hay dos adaptadores oficiales: el adaptador en memoria (integrado) y el [adaptador Redis](https://github.com/socketio/socket.io-redis) basado en el mecanismo [pub-sub](https://redis.io/topics/pubsub) de Redis.
+
+Antes:
+
+```js
+socket.join("room1", () => {
+ io.to("room1").emit("hello");
+});
+
+socket.leave("room2", () => {
+ io.to("room2").emit("bye");
+});
+```
+
+Después:
+
+```js
+socket.join("room1");
+io.to("room1").emit("hello");
+
+socket.leave("room2");
+io.to("room2").emit("bye");
+```
+
+Nota: los adaptadores personalizados pueden devolver una Promise, así que el ejemplo anterior se convierte en:
+
+```js
+await socket.join("room1");
+io.to("room1").emit("hello");
+```
+
+
+#### ~~Socket.use() ha sido eliminado~~
+
+`socket.use()` podía usarse como un listener catch-all. Pero su API no era realmente intuitiva. Ha sido reemplazado por [socket.onAny()](#listeners-catch-all).
+
+**ACTUALIZACIÓN**: el método `Socket.use()` fue restaurado en [`socket.io@3.0.5`](https://github.com/socketio/socket.io/releases/3.0.5).
+
+Antes:
+
+```js
+socket.use((packet, next) => {
+ console.log(packet.data);
+ next();
+});
+```
+
+Después:
+
+```js
+socket.onAny((event, ...args) => {
+ console.log(event);
+});
+```
+
+
+#### Un error de middleware ahora emitirá un objeto Error
+
+El evento `error` ha sido renombrado a `connect_error` y el objeto emitido ahora es un Error real:
+
+Antes:
+
+```js
+// lado del servidor
+io.use((socket, next) => {
+ next(new Error("no autorizado"));
+});
+
+// lado del cliente
+socket.on("error", err => {
+ console.log(err); // no autorizado
+});
+
+// o con un objeto
+// lado del servidor
+io.use((socket, next) => {
+ const err = new Error("no autorizado");
+ err.data = { content: "Por favor inténtelo más tarde" }; // detalles adicionales
+ next(err);
+});
+
+// lado del cliente
+socket.on("error", err => {
+ console.log(err); // { content: "Por favor inténtelo más tarde" }
+});
+```
+
+Después:
+
+```js
+// lado del servidor
+io.use((socket, next) => {
+ const err = new Error("no autorizado");
+ err.data = { content: "Por favor inténtelo más tarde" }; // detalles adicionales
+ next(err);
+});
+
+// lado del cliente
+socket.on("connect_error", err => {
+ console.log(err instanceof Error); // true
+ console.log(err.message); // no autorizado
+ console.log(err.data); // { content: "Por favor inténtelo más tarde" }
+});
+```
+
+
+#### Clara distinción entre la opción query del Manager y la opción query del Socket
+
+En versiones anteriores, la opción `query` se usaba en dos lugares distintos:
+
+- en los parámetros de consulta de las solicitudes HTTP (`GET /socket.io/?EIO=3&abc=def`)
+- en el paquete `CONNECT`
+
+Tomemos el siguiente ejemplo:
+
+```js
+const socket = io({
+ query: {
+ token: "abc"
+ }
+});
+```
+
+Internamente, esto es lo que sucedía en el método `io()`:
+
+```js
+const { Manager } = require("socket.io-client");
+
+// se crea un nuevo Manager (que gestionará la conexión de bajo nivel)
+const manager = new Manager({
+ query: { // enviado en los parámetros de consulta
+ token: "abc"
+ }
+});
+
+// y luego se crea una instancia de Socket para el namespace (aquí, el namespace principal, "/")
+const socket = manager.socket("/", {
+ query: { // enviado en el paquete CONNECT
+ token: "abc"
+ }
+});
+```
+
+Este comportamiento podía llevar a comportamientos extraños, por ejemplo cuando el Manager se reutilizaba para otro namespace (multiplexación):
+
+```js
+// lado del cliente
+const socket1 = io({
+ query: {
+ token: "abc"
+ }
+});
+
+const socket2 = io("/my-namespace", {
+ query: {
+ token: "def"
+ }
+});
+
+// lado del servidor
+io.on("connection", (socket) => {
+ console.log(socket.handshake.query.token); // abc (¡ok!)
+});
+
+io.of("/my-namespace").on("connection", (socket) => {
+ console.log(socket.handshake.query.token); // abc (¿qué?)
+});
+```
+
+Por eso la opción `query` de la instancia Socket ha sido renombrada a `auth` en Socket.IO v3:
+
+```js
+// objeto plano
+const socket = io({
+ auth: {
+ token: "abc"
+ }
+});
+
+// o con una función
+const socket = io({
+ auth: (cb) => {
+ cb({
+ token: "abc"
+ });
+ }
+});
+
+// lado del servidor
+io.on("connection", (socket) => {
+ console.log(socket.handshake.auth.token); // abc
+});
+```
+
+Nota: la opción `query` del Manager todavía se puede usar para añadir un parámetro de consulta específico a las solicitudes HTTP.
+
+
+#### La instancia Socket ya no reenviará los eventos emitidos por su Manager
+
+En versiones anteriores, la instancia Socket emitía los eventos relacionados con el estado de la conexión subyacente. Esto ya no será el caso.
+
+Todavía puedes acceder a esos eventos en la instancia Manager (la propiedad `io` del socket):
+
+Antes:
+
+```js
+socket.on("reconnect_attempt", () => {});
+```
+
+Después:
+
+```js
+socket.io.on("reconnect_attempt", () => {});
+```
+
+Aquí está la lista actualizada de eventos emitidos por el Manager:
+
+| Nombre | Descripción | Anteriormente (si era diferente) |
+| ---- | ----------- | ------------------------- |
+| open | (re)conexión exitosa | - |
+| error | fallo de (re)conexión o error después de una conexión exitosa | connect_error |
+| close | desconexión | - |
+| ping | paquete ping | - |
+| packet | paquete de datos | - |
+| reconnect_attempt | intento de reconexión | reconnect_attempt & reconnecting | - |
+| reconnect | reconexión exitosa | - |
+| reconnect_error | fallo de reconexión | - |
+| reconnect_failed | fallo de reconexión después de todos los intentos | - |
+
+Aquí está la lista actualizada de eventos emitidos por el Socket:
+
+| Nombre | Descripción | Anteriormente (si era diferente) |
+| ---- | ----------- | ------------------------- |
+| connect | conexión exitosa a un Namespace | - |
+| connect_error | fallo de conexión | error |
+| disconnect | desconexión | - |
+
+
+Y finalmente, aquí está la lista actualizada de eventos reservados que no puedes usar en tu aplicación:
+
+- `connect` (usado en el lado del cliente)
+- `connect_error` (usado en el lado del cliente)
+- `disconnect` (usado en ambos lados)
+- `disconnecting` (usado en el lado del servidor)
+- `newListener` y `removeListener` ([eventos reservados](https://nodejs.org/api/events.html#events_event_newlistener) del EventEmitter)
+
+```js
+socket.emit("connect_error"); // ahora lanzará un Error
+```
+
+
+#### Namespace.clients() ha sido renombrado a Namespace.allSockets() y ahora devuelve una Promise
+
+Esta función devuelve la lista de IDs de socket que están conectados a este namespace.
+
+Antes:
+
+```js
+// todos los sockets en el namespace por defecto
+io.clients((error, clients) => {
+ console.log(clients); // => [6em3d4TJP8Et9EMNAAAA, G5p55dHhGgUnLUctAAAB]
+});
+
+// todos los sockets en el namespace "chat"
+io.of("/chat").clients((error, clients) => {
+ console.log(clients); // => [PZDoMHjiu8PYfRiKAAAF, Anw2LatarvGVVXEIAAAD]
+});
+
+// todos los sockets en el namespace "chat" y en la sala "general"
+io.of("/chat").in("general").clients((error, clients) => {
+ console.log(clients); // => [Anw2LatarvGVVXEIAAAD]
+});
+```
+
+Después:
+
+```js
+// todos los sockets en el namespace por defecto
+const ids = await io.allSockets();
+
+// todos los sockets en el namespace "chat"
+const ids = await io.of("/chat").allSockets();
+
+// todos los sockets en el namespace "chat" y en la sala "general"
+const ids = await io.of("/chat").in("general").allSockets();
+```
+
+Nota: esta función era (y todavía es) soportada por el adaptador Redis, lo que significa que devolverá la lista de IDs de socket a través de todos los servidores Socket.IO.
+
+#### Bundles del cliente
+
+Ahora hay 3 bundles distintos:
+
+| Nombre | Tamaño | Descripción |
+|:------------------|:-----------------|:------------|
+| socket.io.js | 34.7 kB gzip | Versión sin minificar, con [debug](https://www.npmjs.com/package/debug) |
+| socket.io.min.js | 14.7 kB min+gzip | Versión de producción, sin [debug](https://www.npmjs.com/package/debug) |
+| socket.io.msgpack.min.js | 15.3 kB min+gzip | Versión de producción, sin [debug](https://www.npmjs.com/package/debug) y con el [parser msgpack](https://github.com/socketio/socket.io-msgpack-parser) |
+
+Por defecto, todos ellos son servidos por el servidor, en `/socket.io/`.
+
+Antes:
+
+```html
+
+
+```
+
+Después:
+
+```html
+
+
+
+
+```
+
+#### Ya no hay evento "pong" para recuperar la latencia
+
+En Socket.IO v2, podías escuchar el evento `pong` en el lado del cliente, que incluía la duración del último viaje de ida y vuelta del health check.
+
+Debido a la inversión del mecanismo de heartbeat (más información [aquí](/blog/engine-io-4-release/#heartbeat-mechanism-reversal)), este evento ha sido eliminado.
+
+Antes:
+
+```js
+socket.on("pong", (latency) => {
+ console.log(latency);
+});
+```
+
+Después:
+
+```js
+// lado del servidor
+io.on("connection", (socket) => {
+ socket.on("ping", (cb) => {
+ if (typeof cb === "function")
+ cb();
+ });
+});
+
+// lado del cliente
+setInterval(() => {
+ const start = Date.now();
+
+ // volatile, así que el paquete será descartado si el socket no está conectado
+ socket.volatile.emit("ping", () => {
+ const latency = Date.now() - start;
+ // ...
+ });
+}, 5000);
+```
+
+#### Sintaxis de módulos ES
+
+La sintaxis de módulos ECMAScript ahora es similar a la de Typescript (ver [abajo](#el-código-fuente-de-socketio-ha-sido-reescrito-en-typescript)).
+
+Antes (usando import por defecto):
+
+```js
+// lado del servidor
+import Server from "socket.io";
+
+const io = new Server(8080);
+
+// lado del cliente
+import io from 'socket.io-client';
+
+const socket = io();
+```
+
+Después (con import nombrado):
+
+```js
+// lado del servidor
+import { Server } from "socket.io";
+
+const io = new Server(8080);
+
+// lado del cliente
+import { io } from 'socket.io-client';
+
+const socket = io();
+```
+
+#### Las cadenas de `emit()` ya no son posibles
+
+El método `emit()` ahora coincide con la firma del método [`EventEmitter.emit()`](https://nodejs.org/dist/latest/docs/api/events.html#events_emitter_emit_eventname_args), y devuelve `true` en lugar del objeto actual.
+
+Antes:
+
+```js
+socket.emit("event1").emit("event2");
+```
+
+Después:
+
+```js
+socket.emit("event1");
+socket.emit("event2");
+```
+
+#### Los nombres de las salas ya no se convierten a string
+
+Ahora usamos Maps y Sets internamente en lugar de objetos planos, por lo que los nombres de las salas ya no se convierten implícitamente a string.
+
+Antes:
+
+```js
+// los tipos mixtos eran posibles
+socket.join(42);
+io.to("42").emit("hello");
+// también funcionaba
+socket.join("42");
+io.to(42).emit("hello");
+```
+
+Después:
+
+```js
+// de una forma
+socket.join("42");
+io.to("42").emit("hello");
+// o de otra
+socket.join(42);
+io.to(42).emit("hello");
+```
+
+### Nuevas características
+
+Algunas de estas nuevas características pueden ser portadas a la rama `2.4.x`, dependiendo de los comentarios de los usuarios.
+
+
+#### Listeners catch-all
+
+Esta característica está inspirada en la biblioteca [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) (que no se usa directamente para no aumentar el tamaño del bundle del navegador).
+
+Está disponible tanto para el lado del servidor como del cliente:
+
+```js
+// servidor
+io.on("connection", (socket) => {
+ socket.onAny((event, ...args) => {});
+ socket.prependAny((event, ...args) => {});
+ socket.offAny(); // eliminar todos los listeners
+ socket.offAny(listener);
+ const listeners = socket.listenersAny();
+});
+
+// cliente
+const socket = io();
+socket.onAny((event, ...args) => {});
+socket.prependAny((event, ...args) => {});
+socket.offAny(); // eliminar todos los listeners
+socket.offAny(listener);
+const listeners = socket.listenersAny();
+```
+
+
+#### Eventos volátiles (cliente)
+
+Un evento volátil es un evento que se permite descartar si el transporte de bajo nivel aún no está listo (por ejemplo, cuando una solicitud HTTP POST ya está pendiente).
+
+Esta característica ya estaba disponible en el lado del servidor. Puede ser útil también en el lado del cliente, por ejemplo cuando el socket no está conectado (por defecto, los paquetes se almacenan en búfer hasta la reconexión).
+
+```js
+socket.volatile.emit("evento volátil", "puede o no ser enviado");
+```
+
+
+#### Bundle oficial con el parser msgpack
+
+Ahora se proporcionará un bundle con el [socket.io-msgpack-parser](https://github.com/socketio/socket.io-msgpack-parser) (ya sea en el CDN o servido por el servidor en `/socket.io/socket.io.msgpack.min.js`).
+
+Pros:
+
+- los eventos con contenido binario se envían como 1 frame WebSocket (en lugar de 2+ con el parser por defecto)
+- las cargas útiles con muchos números deberían ser más pequeñas
+
+Contras:
+
+- sin soporte para IE9 (https://caniuse.com/mdn-javascript_builtins_arraybuffer)
+- un tamaño de bundle ligeramente mayor
+
+```js
+// lado del servidor
+const io = require("socket.io")(httpServer, {
+ parser: require("socket.io-msgpack-parser")
+});
+```
+
+No se necesita configuración adicional en el lado del cliente.
+
+
+### Misceláneos
+
+#### El código fuente de Socket.IO ha sido reescrito en TypeScript
+
+Lo que significa que `npm i -D @types/socket.io` ya no debería ser necesario.
+
+Servidor:
+
+```ts
+import { Server, Socket } from "socket.io";
+
+const io = new Server(8080);
+
+io.on("connection", (socket: Socket) => {
+ console.log(`conectado ${socket.id}`);
+
+ socket.on("disconnect", () => {
+ console.log(`desconectado ${socket.id}`);
+ });
+});
+```
+
+Cliente:
+
+```ts
+import { io } from "socket.io-client";
+
+const socket = io("/");
+
+socket.on("connect", () => {
+ console.log(`conectado ${socket.id}`);
+});
+```
+
+JavaScript plano obviamente sigue siendo completamente soportado.
+
+
+#### El soporte para IE8 y Node.js 8 ha sido oficialmente eliminado
+
+IE8 ya no es testeable en la plataforma Sauce Labs, y requiere mucho esfuerzo para muy pocos usuarios (¿si acaso alguno?), así que estamos eliminando el soporte para él.
+
+Además, Node.js 8 ahora está [EOL](https://github.com/nodejs/Release). ¡Por favor actualiza lo antes posible!
+
+
+### Cómo actualizar un despliegue en producción existente
+
+- primero, actualiza los servidores con `allowEIO3` establecido en `true` (añadido en `socket.io@3.1.0`)
+
+```js
+const io = require("socket.io")({
+ allowEIO3: true // false por defecto
+});
+```
+
+Nota: Si estás usando el adaptador Redis para [difundir paquetes entre nodos](../04-Events/broadcasting-events.md#con-múltiples-servidores-socketio), debes usar `socket.io-redis@5` con `socket.io@2` y `socket.io-redis@6` con `socket.io@3`. Por favor ten en cuenta que ambas versiones son compatibles, así que puedes actualizar cada servidor uno por uno (no se necesita un big bang).
+
+- luego, actualiza los clientes
+
+Este paso puede tomar algo de tiempo, ya que algunos clientes pueden todavía tener un cliente v2 en caché.
+
+Puedes verificar la versión de la conexión con:
+
+```js
+io.on("connection", (socket) => {
+ const version = socket.conn.protocol; // ya sea 3 o 4
+});
+```
+
+Esto coincide con el valor del parámetro de consulta `EIO` en las solicitudes HTTP.
+
+- y finalmente, una vez que cada cliente haya sido actualizado, establece `allowEIO3` en `false` (que es el valor por defecto)
+
+```js
+const io = require("socket.io")({
+ allowEIO3: false
+});
+```
+
+Con `allowEIO3` establecido en `false`, los clientes v2 ahora recibirán un error HTTP 400 (`Unsupported protocol version`) al conectarse.
+
+
+### Problemas conocidos de migración
+
+- `stream_1.pipeline is not a function`
+
+```
+TypeError: stream_1.pipeline is not a function
+ at Function.sendFile (.../node_modules/socket.io/dist/index.js:249:26)
+ at Server.serve (.../node_modules/socket.io/dist/index.js:225:16)
+ at Server.srv.on (.../node_modules/socket.io/dist/index.js:186:22)
+ at emitTwo (events.js:126:13)
+ at Server.emit (events.js:214:7)
+ at parserOnIncoming (_http_server.js:602:12)
+ at HTTPParser.parserOnHeadersComplete (_http_common.js:116:23)
+```
+
+Este error probablemente se debe a tu versión de Node.js. El método [pipeline](https://nodejs.org/api/stream.html#stream_stream_pipeline_source_transforms_destination_callback) fue introducido en Node.js 10.0.0.
+
+
+- `error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'.`
+
+```
+node_modules/socket.io/dist/namespace.d.ts(89,5): error TS2416: Property 'emit' in type 'Namespace' is not assignable to the same property in base type 'EventEmitter'.
+ Type '(ev: string, ...args: any[]) => Namespace' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
+ Type 'Namespace' is not assignable to type 'boolean'.
+node_modules/socket.io/dist/socket.d.ts(84,5): error TS2416: Property 'emit' in type 'Socket' is not assignable to the same property in base type 'EventEmitter'.
+ Type '(ev: string, ...args: any[]) => this' is not assignable to type '(event: string | symbol, ...args: any[]) => boolean'.
+ Type 'this' is not assignable to type 'boolean'.
+ Type 'Socket' is not assignable to type 'boolean'.
+```
+
+La firma del método `emit()` fue corregida en la versión `3.0.1` ([commit](https://github.com/socketio/socket.io/commit/50671d984a81535a6a15c704546ca7465e2ea295)).
+
+
+- el cliente se desconecta al enviar una carga útil grande (> 1MB)
+
+Esto probablemente se debe al hecho de que el valor predeterminado de `maxHttpBufferSize` ahora es `1MB`. Al recibir un paquete más grande que esto, el servidor desconecta al cliente, para evitar que clientes maliciosos sobrecarguen el servidor.
+
+Puedes ajustar el valor al crear el servidor:
+
+```js
+const io = require("socket.io")(httpServer, {
+ maxHttpBufferSize: 1e8
+});
+```
+
+- `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at xxx/socket.io/?EIO=4&transport=polling&t=NMnp2WI. (Reason: CORS header 'Access-Control-Allow-Origin' missing).`
+
+Desde Socket.IO v3, necesitas habilitar explícitamente [Cross-Origin Resource Sharing](https://developer.mozilla.org/es/docs/Web/HTTP/CORS) (CORS). La documentación se puede encontrar [aquí](../02-Server/handling-cors.md).
+
+- `Uncaught TypeError: packet.data is undefined`
+
+Parece que estás usando un cliente v3 para conectarte a un servidor v2, lo cual no es posible. Por favor consulta la [siguiente sección](#cómo-actualizar-un-despliegue-en-producción-existente).
+
+- `Object literal may only specify known properties, and 'extraHeaders' does not exist in type 'ConnectOpts'`
+
+Dado que el código fuente ha sido reescrito en TypeScript (más información [aquí](#el-código-fuente-de-socketio-ha-sido-reescrito-en-typescript)), `@types/socket.io-client` ya no es necesario y de hecho entrará en conflicto con los tipos que vienen del paquete `socket.io-client`.
+
+- cookie faltante en un contexto cross-origin
+
+Ahora necesitas habilitar explícitamente las cookies si el frontend no es servido desde el mismo dominio que el backend:
+
+*Servidor*
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({
+ cors: {
+ origin: ["https://front.domain.com"],
+ credentials: true
+ }
+});
+```
+
+*Cliente*
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io("https://backend.domain.com", {
+ withCredentials: true
+});
+```
+
+Referencia:
+
+- [Manejo de CORS](../02-Server/handling-cors.md)
+- [Opción `cors`](../../server-api.md#cors)
+- [Opción `withCredentials`](../../client-api.md#withcredentials)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-3-to-4.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-3-to-4.md
new file mode 100644
index 00000000..746240c9
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/07-Migrations/migrating-from-3-to-4.md
@@ -0,0 +1,324 @@
+---
+title: Migrando de 3.x a 4.0
+sidebar_position: 2
+slug: /migrating-from-3-x-to-4-0/
+toc_max_heading_level: 4
+---
+
+La versión 4.0.0 añade muchas características nuevas, que se detallan [abajo](#nuevas-características), pero también contiene algunos cambios incompatibles en la API (de ahí el salto de versión mayor).
+
+Por favor ten en cuenta que estos cambios incompatibles solo impactan la API en el lado del servidor. El protocolo Socket.IO en sí no fue actualizado, así que un cliente v3 **podrá** conectarse a un servidor v4 y viceversa. Además, el modo de compatibilidad ([`allowEIO3: true`](../../server-options.md#alloweio3)) todavía está disponible entre un cliente Socket.IO v2 y un servidor Socket.IO v4.
+
+Aquí está la lista completa de cambios:
+
+- [Cambios incompatibles](#cambios-incompatibles)
+ - [`io.to()` ahora es inmutable](#ioto-ahora-es-inmutable)
+ - [Opción `wsEngine`](#opción-wsengine)
+- [Configuración](#configuración)
+ - [Asegurar compatibilidad con clientes Swift v15](#asegurar-compatibilidad-con-clientes-swift-v15)
+ - [El valor por defecto de `pingTimeout` fue aumentado](#el-valor-por-defecto-de-pingtimeout-fue-aumentado)
+- [Nuevas características](#nuevas-características)
+ - [Permitir excluir salas específicas al hacer broadcast](#permitir-excluir-salas-específicas-al-hacer-broadcast)
+ - [Permitir pasar un array a `io.to()`](#permitir-pasar-un-array-a-ioto)
+ - [Métodos utilitarios adicionales](#métodos-utilitarios-adicionales)
+ - [Eventos tipados](#eventos-tipados)
+ - [Opción `autoUnref`](#opción-autounref)
+- [Problemas conocidos de migración](#problemas-conocidos-de-migración)
+
+### Cambios incompatibles
+
+#### `io.to()` ahora es inmutable
+
+Anteriormente, hacer broadcast a una sala dada (llamando `io.to()`) mutaba la instancia io, lo cual podía llevar a comportamientos sorprendentes, como:
+
+```js
+io.to("room1");
+io.to("room2").emit(/* ... */); // también enviado a room1
+
+// o con async/await
+io.to("room3").emit("details", await fetchDetails()); // comportamiento aleatorio: quizás en room3, quizás a todos los clientes
+```
+
+Llamar `io.to()` (o cualquier otro modificador de broadcast) ahora devolverá una instancia inmutable.
+
+Ejemplos:
+
+```js
+const operator1 = io.to("room1");
+const operator2 = operator1.to("room2");
+const operator3 = socket.broadcast;
+const operator4 = socket.to("room3").to("room4");
+
+operator1.emit(/* ... */); // solo a clientes en "room1"
+operator2.emit(/* ... */); // a clientes en "room1" o en "room2"
+operator3.emit(/* ... */); // a todos los clientes excepto el emisor
+operator4.emit(/* ... */); // a clientes en "room3" o en "room4" excepto el emisor
+```
+
+#### Opción `wsEngine`
+
+El formato de la opción [`wsEngine`](../../server-options.md#wsengine) fue actualizado para deshacerse del siguiente error:
+
+`Critical dependency: the request of a dependency is an expression`
+
+al empaquetar el servidor con webpack.
+
+Antes:
+
+```js
+const io = require("socket.io")(httpServer, {
+ wsEngine: "eiows"
+});
+```
+
+Después:
+
+```js
+const io = require("socket.io")(httpServer, {
+ wsEngine: require("eiows").Server
+});
+```
+
+### Configuración
+
+#### Asegurar compatibilidad con clientes Swift v15
+
+Antes de la versión 16.0.0, el cliente Swift no incluía el parámetro de consulta `EIO` en las solicitudes HTTP, y el servidor Socket.IO v3 infería `EIO=4` por defecto.
+
+Por eso un cliente Swift v15 no podía conectarse al servidor, incluso cuando el modo de compatibilidad estaba habilitado ([`allowEIO3: true`](../../server-options.md#alloweio3)), a menos que especificaras explícitamente el parámetro de consulta:
+
+```swift
+let manager = SocketManager(socketURL: URL(string: "http://localhost:8080")!, config: [
+ .log(true),
+ .connectParams(["EIO": "3"])
+])
+let socket = manager.defaultSocket
+```
+
+El servidor Socket.IO v4 ahora inferirá `EIO=3` si el parámetro de consulta `EIO` no está incluido.
+
+#### El valor por defecto de `pingTimeout` fue aumentado
+
+El valor por defecto de [`pingTimeout`](../../server-options.md#pingtimeout) (usado en el [mecanismo de heartbeat](../01-Documentation/how-it-works.md#disconnection-detection)) fue actualizado de 60000 a 5000 en `socket.io@2.1.0` (marzo 2018).
+
+El razonamiento en ese momento:
+
+Algunos usuarios experimentaban largos retrasos entre la desconexión en el lado del servidor y en el lado del cliente. El evento "disconnect" tardaba mucho en dispararse en el navegador, probablemente debido a un temporizador retrasado. De ahí el cambio.
+
+Dicho esto, el valor actual (5s) causaba desconexiones inesperadas cuando se enviaba una carga útil grande sobre una red lenta, porque impedía que los paquetes ping-pong se intercambiaran entre el cliente y el servidor. Esto también puede suceder cuando una tarea síncrona bloquea el servidor por más de 5 segundos.
+
+El nuevo valor (20s) parece ser un buen equilibrio entre detección rápida de desconexión y tolerancia a varios retrasos.
+
+### Nuevas características
+
+#### Permitir excluir salas específicas al hacer broadcast
+
+Gracias al increíble trabajo de [Sebastiaan Marynissen](https://github.com/sebamarynissen), ahora puedes excluir una sala específica al hacer broadcast:
+
+```js
+io.except("room1").emit(/* ... */); // a todos los clientes excepto los de "room1"
+io.to("room2").except("room3").emit(/* ... */); // a todos los clientes en "room2" excepto los de "room3"
+
+socket.broadcast.except("room1").emit(/* ... */); // a todos los clientes excepto los de "room1" y el emisor
+socket.except("room1").emit(/* ... */); // igual que arriba
+socket.to("room4").except("room5").emit(/* ... */); // a todos los clientes en "room4" excepto los de "room5" y el emisor
+```
+
+#### Permitir pasar un array a `io.to()`
+
+El método `to()` ahora acepta un array de salas.
+
+Antes:
+
+```js
+const rooms = ["room1", "room2", "room3"];
+for (const room of rooms) {
+ io.to(room);
+}
+// broadcast a clientes en "room1", "room2" o "room3"
+// ¡¡¡ADVERTENCIA!!! esto ya no funciona en v4, ver el cambio incompatible arriba
+io.emit(/* ... */);
+```
+
+Después:
+
+```js
+io.to(["room1", "room2", "room3"]).emit(/* ... */);
+
+socket.to(["room1", "room2", "room3"]).emit(/* ... */);
+```
+
+#### Métodos utilitarios adicionales
+
+Se añadieron algunos métodos (muy esperados):
+
+- `socketsJoin`: hace que las instancias de socket coincidentes se unan a las salas especificadas
+
+```js
+// hacer que todas las instancias de Socket se unan a la sala "room1"
+io.socketsJoin("room1");
+
+// hacer que todas las instancias de Socket del namespace "admin" en la sala "room1" se unan a la sala "room2"
+io.of("/admin").in("room1").socketsJoin("room2");
+```
+
+- `socketsLeave`: hace que las instancias de socket coincidentes abandonen las salas especificadas
+
+```js
+// hacer que todas las instancias de Socket abandonen la sala "room1"
+io.socketsLeave("room1");
+
+// hacer que todas las instancias de Socket del namespace "admin" en la sala "room1" abandonen la sala "room2"
+io.of("/admin").in("room1").socketsLeave("room2");
+```
+
+- `disconnectSockets`: hace que las instancias de socket coincidentes se desconecten
+
+```js
+// hacer que todas las instancias de Socket se desconecten
+io.disconnectSockets();
+
+// hacer que todas las instancias de Socket del namespace "admin" en la sala "room1" se desconecten
+io.of("/admin").in("room1").disconnectSockets();
+
+// esto también funciona con un solo ID de socket
+io.of("/admin").in(theSocketId).disconnectSockets();
+```
+
+- `fetchSockets`: devuelve las instancias de socket coincidentes
+
+```js
+// devolver todas las instancias de Socket del namespace principal
+const sockets = await io.fetchSockets();
+
+// devolver todas las instancias de Socket del namespace "admin" en la sala "room1"
+const sockets = await io.of("/admin").in("room1").fetchSockets();
+
+// esto también funciona con un solo ID de socket
+const sockets = await io.in(theSocketId).fetchSockets();
+```
+
+La variable `sockets` en el ejemplo anterior es un array de objetos que exponen un subconjunto de la clase Socket usual:
+
+```js
+for (const socket of sockets) {
+ console.log(socket.id);
+ console.log(socket.handshake);
+ console.log(socket.rooms);
+ socket.emit(/* ... */);
+ socket.join(/* ... */);
+ socket.leave(/* ... */);
+ socket.disconnect(/* ... */);
+}
+```
+
+Esos métodos comparten la misma semántica que el broadcasting, y se aplican los mismos filtros:
+
+```js
+io.of("/admin").in("room1").except("room2").local.disconnectSockets();
+```
+
+Lo cual hace que todas las instancias de Socket del namespace "admin"
+
+- en la sala "room1" (`in("room1")` o `to("room1")`)
+- excepto las de "room2" (`except("room2")`)
+- y solo en el servidor Socket.IO actual (`local`)
+
+se desconecten.
+
+#### Eventos tipados
+
+Gracias al increíble trabajo de [Maxime Kjaer](https://github.com/MaximeKjaer), los usuarios de TypeScript ahora pueden tipar los eventos enviados entre el cliente y el servidor.
+
+Primero, declaras la firma de cada evento:
+
+```ts
+interface ClientToServerEvents {
+ noArg: () => void;
+ basicEmit: (a: number, b: string, c: number[]) => void;
+}
+
+interface ServerToClientEvents {
+ withAck: (d: string, cb: (e: number) => void) => void;
+}
+```
+
+Y ahora puedes usarlos en el lado del cliente:
+
+```ts
+import { io, Socket } from "socket.io-client";
+
+const socket: Socket = io();
+
+socket.emit("noArg");
+
+socket.emit("basicEmit", 1, "2", [3]);
+
+socket.on("withAck", (d, cb) => {
+ cb(4);
+});
+```
+
+Tu IDE ahora debería inferir correctamente el tipo de cada argumento:
+
+De manera similar en el lado del servidor (los `ServerToClientEvents` y `ClientToServerEvents` están invertidos):
+
+```ts
+import { Server } from "socket.io";
+
+const io = new Server(3000);
+
+io.on("connection", (socket) => {
+ socket.on("noArg", () => {
+ // ...
+ });
+
+ socket.on("basicEmit", (a, b, c) => {
+ // ...
+ });
+
+ socket.emit("withAck", "42", (e) => {
+ console.log(e);
+ });
+});
+```
+
+Por defecto, los eventos no están tipados y los argumentos se inferirán como `any`.
+
+#### Opción `autoUnref`
+
+Y finalmente, gracias al increíble trabajo de [KC Erb](https://github.com/KCErb), se añadió la opción `autoUnref`.
+
+Con `autoUnref` establecido en true (por defecto: false), el cliente Socket.IO permitirá que el programa termine si no hay otro temporizador/socket TCP activo en el sistema de eventos (incluso si el cliente está conectado):
+
+```js
+const socket = io({
+ autoUnref: true
+});
+```
+
+Nota: esta opción solo aplica a clientes Node.js.
+
+### Problemas conocidos de migración
+
+- `cannot get emit of undefined`
+
+La siguiente expresión:
+
+```js
+socket.to("room1").broadcast.emit(/* ... */);
+```
+
+funcionaba en Socket.IO v3 pero ahora se considera inválida, ya que la bandera `broadcast` es inútil porque el método `to("room1")` ya pone la instancia de Socket en modo broadcasting.
+
+```js
+// VÁLIDO
+socket.broadcast.emit(/* ... */); // a todos los clientes excepto el emisor
+socket.to("room1").emit(/* ... */); // a clientes en "room1" excepto el emisor
+
+// VÁLIDO (pero bandera 'broadcast' inútil)
+socket.broadcast.to("room1").emit(/* ... */);
+
+// INVÁLIDO
+socket.to("room1").broadcast.emit(/* ... */);
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/eio-protocol.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/eio-protocol.md
new file mode 100644
index 00000000..da08c6b4
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/eio-protocol.md
@@ -0,0 +1,423 @@
+---
+title: El protocolo Engine.IO
+sidebar_position: 3
+slug: /engine-io-protocol/
+---
+
+Este documento describe la 4ta versión del protocolo Engine.IO.
+
+La fuente de este documento se puede encontrar [aquí](https://github.com/socketio/engine.io-protocol).
+
+**Tabla de contenido**
+
+- [Introducción](#introducción)
+- [Transportes](#transportes)
+ - [HTTP long-polling](#http-long-polling)
+ - [Ruta de la solicitud](#ruta-de-la-solicitud)
+ - [Parámetros de consulta](#parámetros-de-consulta)
+ - [Cabeceras](#cabeceras)
+ - [Envío y recepción de datos](#envío-y-recepción-de-datos)
+ - [Envío de datos](#envío-de-datos)
+ - [Recepción de datos](#recepción-de-datos)
+ - [WebSocket](#websocket)
+- [Protocolo](#protocolo)
+ - [Handshake](#handshake)
+ - [Heartbeat](#heartbeat)
+ - [Upgrade](#upgrade)
+ - [Message](#message)
+- [Codificación de paquetes](#codificación-de-paquetes)
+ - [HTTP long-polling](#http-long-polling-1)
+ - [WebSocket](#websocket-1)
+- [Historial](#historial)
+ - [De v2 a v3](#de-v2-a-v3)
+ - [De v3 a v4](#de-v3-a-v4)
+- [Suite de pruebas](#suite-de-pruebas)
+
+
+
+## Introducción
+
+El protocolo Engine.IO permite comunicación [full-duplex](https://es.wikipedia.org/wiki/D%C3%BAplex_(telecomunicaciones)#Full-d%C3%BAplex) y de bajo overhead entre un cliente y un servidor.
+
+Está basado en el [protocolo WebSocket](https://es.wikipedia.org/wiki/WebSocket) y usa [HTTP long-polling](https://es.wikipedia.org/wiki/Tecnolog%C3%ADa_Push#Long_polling) como fallback si la conexión WebSocket no puede establecerse.
+
+La implementación de referencia está escrita en [TypeScript](https://www.typescriptlang.org/):
+
+- servidor: https://github.com/socketio/engine.io
+- cliente: https://github.com/socketio/engine.io-client
+
+El [protocolo Socket.IO](https://github.com/socketio/socket.io-protocol) está construido sobre estos fundamentos, añadiendo características adicionales sobre el canal de comunicación proporcionado por el protocolo Engine.IO.
+
+## Transportes
+
+La conexión entre un cliente Engine.IO y un servidor Engine.IO puede establecerse con:
+
+- [HTTP long-polling](#http-long-polling)
+- [WebSocket](#websocket)
+
+### HTTP long-polling
+
+El transporte HTTP long-polling (también referido simplemente como "polling") consiste en solicitudes HTTP sucesivas:
+
+- solicitudes `GET` de larga duración, para recibir datos del servidor
+- solicitudes `POST` de corta duración, para enviar datos al servidor
+
+#### Ruta de la solicitud
+
+La ruta de las solicitudes HTTP es `/engine.io/` por defecto.
+
+Puede ser actualizada por bibliotecas construidas sobre el protocolo (por ejemplo, el protocolo Socket.IO usa `/socket.io/`).
+
+#### Parámetros de consulta
+
+Se utilizan los siguientes parámetros de consulta:
+
+| Nombre | Valor | Descripción |
+|-------------|-----------|-------------------------------------------------------------------------|
+| `EIO` | `4` | Obligatorio, la versión del protocolo. |
+| `transport` | `polling` | Obligatorio, el nombre del transporte. |
+| `sid` | `` | Obligatorio una vez establecida la sesión, el identificador de sesión. |
+
+Si falta un parámetro de consulta obligatorio, el servidor DEBE responder con un código de estado HTTP 400.
+
+#### Cabeceras
+
+Al enviar datos binarios, el emisor (cliente o servidor) DEBE incluir una cabecera `Content-Type: application/octet-stream`.
+
+Sin una cabecera `Content-Type` explícita, el receptor DEBERÍA inferir que los datos son texto plano.
+
+Referencia: https://developer.mozilla.org/es/docs/Web/HTTP/Headers/Content-Type
+
+#### Envío y recepción de datos
+
+##### Envío de datos
+
+Para enviar algunos paquetes, un cliente DEBE crear una solicitud HTTP `POST` con los paquetes codificados en el cuerpo de la solicitud:
+
+```
+CLIENT SERVER
+
+ │ │
+ │ POST /engine.io/?EIO=4&transport=polling&sid=... │
+ │ ───────────────────────────────────────────────────► │
+ │ ◄──────────────────────────────────────────────────┘ │
+ │ HTTP 200 │
+ │ │
+```
+
+El servidor DEBE devolver una respuesta HTTP 400 si el ID de sesión (del parámetro de consulta `sid`) no es conocido.
+
+Para indicar éxito, el servidor DEBE devolver una respuesta HTTP 200, con la cadena `ok` en el cuerpo de la respuesta.
+
+Para asegurar el orden de los paquetes, un cliente NO DEBE tener más de una solicitud `POST` activa. Si esto sucede, el servidor DEBE devolver un código de estado HTTP 400 y cerrar la sesión.
+
+##### Recepción de datos
+
+Para recibir algunos paquetes, un cliente DEBE crear una solicitud HTTP `GET`:
+
+```
+CLIENT SERVER
+
+ │ GET /engine.io/?EIO=4&transport=polling&sid=... │
+ │ ──────────────────────────────────────────────────► │
+ │ . │
+ │ . │
+ │ . │
+ │ . │
+ │ ◄─────────────────────────────────────────────────┘ │
+ │ HTTP 200 │
+```
+
+El servidor DEBE devolver una respuesta HTTP 400 si el ID de sesión (del parámetro de consulta `sid`) no es conocido.
+
+El servidor PUEDE no responder inmediatamente si no hay paquetes en búfer para la sesión dada. Una vez que hay algunos paquetes para enviar, el servidor DEBERÍA codificarlos (ver [Codificación de paquetes](#codificación-de-paquetes)) y enviarlos en el cuerpo de la respuesta de la solicitud HTTP.
+
+Para asegurar el orden de los paquetes, un cliente NO DEBE tener más de una solicitud `GET` activa. Si esto sucede, el servidor DEBE devolver un código de estado HTTP 400 y cerrar la sesión.
+
+### WebSocket
+
+El transporte WebSocket consiste en una [conexión WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSockets_API), que proporciona un canal de comunicación bidireccional y de baja latencia entre el servidor y el cliente.
+
+Se utilizan los siguientes parámetros de consulta:
+
+| Nombre | Valor | Descripción |
+|-------------|-------------|------------------------------------------------------------------------------------------|
+| `EIO` | `4` | Obligatorio, la versión del protocolo. |
+| `transport` | `websocket` | Obligatorio, el nombre del transporte. |
+| `sid` | `` | Opcional, dependiendo de si es un upgrade desde HTTP long-polling o no. |
+
+Si falta un parámetro de consulta obligatorio, el servidor DEBE cerrar la conexión WebSocket.
+
+Cada paquete (lectura o escritura) se envía en su propio [frame WebSocket](https://datatracker.ietf.org/doc/html/rfc6455#section-5).
+
+Un cliente NO DEBE abrir más de una conexión WebSocket por sesión. Si esto sucede, el servidor DEBE cerrar la conexión WebSocket.
+
+## Protocolo
+
+Un paquete Engine.IO consiste en:
+
+- un tipo de paquete
+- una carga útil de paquete opcional
+
+Aquí está la lista de tipos de paquetes disponibles:
+
+| Tipo | ID | Uso |
+|---------|-----|---------------------------------------------------------|
+| open | 0 | Usado durante el [handshake](#handshake). |
+| close | 1 | Usado para indicar que un transporte puede cerrarse. |
+| ping | 2 | Usado en el [mecanismo de heartbeat](#heartbeat). |
+| pong | 3 | Usado en el [mecanismo de heartbeat](#heartbeat). |
+| message | 4 | Usado para enviar una carga útil al otro lado. |
+| upgrade | 5 | Usado durante el [proceso de upgrade](#upgrade). |
+| noop | 6 | Usado durante el [proceso de upgrade](#upgrade). |
+
+### Handshake
+
+Para establecer una conexión, el cliente DEBE enviar una solicitud HTTP `GET` al servidor:
+
+- HTTP long-polling primero (por defecto)
+
+```
+CLIENT SERVER
+
+ │ │
+ │ GET /engine.io/?EIO=4&transport=polling │
+ │ ───────────────────────────────────────────────────────► │
+ │ ◄──────────────────────────────────────────────────────┘ │
+ │ HTTP 200 │
+ │ │
+```
+
+- Sesión solo WebSocket
+
+```
+CLIENT SERVER
+
+ │ │
+ │ GET /engine.io/?EIO=4&transport=websocket │
+ │ ───────────────────────────────────────────────────────► │
+ │ ◄──────────────────────────────────────────────────────┘ │
+ │ HTTP 101 │
+ │ │
+```
+
+Si el servidor acepta la conexión, DEBE responder con un paquete `open` con la siguiente carga útil codificada en JSON:
+
+| Clave | Tipo | Descripción |
+|----------------|------------|--------------------------------------------------------------------------------------------------------------------------|
+| `sid` | `string` | El ID de sesión. |
+| `upgrades` | `string[]` | La lista de [upgrades de transporte](#upgrade) disponibles. |
+| `pingInterval` | `number` | El intervalo de ping, usado en el [mecanismo de heartbeat](#heartbeat) (en milisegundos). |
+| `pingTimeout` | `number` | El timeout de ping, usado en el [mecanismo de heartbeat](#heartbeat) (en milisegundos). |
+| `maxPayload` | `number` | El número máximo de bytes por fragmento, usado por el cliente para agregar paquetes en [cargas útiles](#codificación-de-paquetes). |
+
+Ejemplo:
+
+```json
+{
+ "sid": "lv_VI97HAXpY6yYWAAAC",
+ "upgrades": ["websocket"],
+ "pingInterval": 25000,
+ "pingTimeout": 20000,
+ "maxPayload": 1000000
+}
+```
+
+El cliente DEBE enviar el valor `sid` en los parámetros de consulta de todas las solicitudes subsiguientes.
+
+### Heartbeat
+
+Una vez que el [handshake](#handshake) se completa, se inicia un mecanismo de heartbeat para verificar la vivacidad de la conexión:
+
+```
+CLIENT SERVER
+
+ │ *** Handshake *** │
+ │ │
+ │ ◄───────────────────────────────────────────────── │
+ │ 2 │ (paquete ping)
+ │ ─────────────────────────────────────────────────► │
+ │ 3 │ (paquete pong)
+```
+
+A un intervalo dado (el valor `pingInterval` enviado en el handshake) el servidor envía un paquete `ping` y el cliente tiene unos segundos (el valor `pingTimeout`) para enviar un paquete `pong` de vuelta.
+
+Si el servidor no recibe un paquete `pong` de vuelta, DEBERÍA considerar que la conexión está cerrada.
+
+Inversamente, si el cliente no recibe un paquete `ping` dentro de `pingInterval + pingTimeout`, DEBERÍA considerar que la conexión está cerrada.
+
+### Upgrade
+
+Por defecto, el cliente DEBERÍA crear una conexión HTTP long-polling, y luego hacer upgrade a mejores transportes si están disponibles.
+
+Para hacer upgrade a WebSocket, el cliente DEBE:
+
+- pausar el transporte HTTP long-polling (no se envían más solicitudes HTTP), para asegurar que ningún paquete se pierda
+- abrir una conexión WebSocket con el mismo ID de sesión
+- enviar un paquete `ping` con la cadena `probe` en la carga útil
+
+El servidor DEBE:
+
+- enviar un paquete `noop` a cualquier solicitud `GET` pendiente (si aplica) para cerrar limpiamente el transporte HTTP long-polling
+- responder con un paquete `pong` con la cadena `probe` en la carga útil
+
+Finalmente, el cliente DEBE enviar un paquete `upgrade` para completar el upgrade:
+
+```
+CLIENT SERVER
+
+ │ │
+ │ GET /engine.io/?EIO=4&transport=websocket&sid=... │
+ │ ───────────────────────────────────────────────────► │
+ │ ◄─────────────────────────────────────────────────┘ │
+ │ HTTP 101 (WebSocket handshake) │
+ │ │
+ │ ----- WebSocket frames ----- │
+ │ ─────────────────────────────────────────────────► │
+ │ 2probe │ (paquete ping)
+ │ ◄───────────────────────────────────────────────── │
+ │ 3probe │ (paquete pong)
+ │ ─────────────────────────────────────────────────► │
+ │ 5 │ (paquete upgrade)
+ │ │
+```
+
+### Message
+
+Una vez que el [handshake](#handshake) se completa, el cliente y el servidor pueden intercambiar datos incluyéndolos en un paquete `message`.
+
+
+## Codificación de paquetes
+
+La serialización de un paquete Engine.IO depende del tipo de carga útil (texto plano o binario) y del transporte.
+
+### HTTP long-polling
+
+Debido a la naturaleza del transporte HTTP long-polling, múltiples paquetes pueden concatenarse en una sola carga útil para aumentar el throughput.
+
+Formato:
+
+```
+[][][][...]
+```
+
+Ejemplo:
+
+```
+4hello\x1e2\x1e4world
+
+con:
+
+4 => tipo de paquete message
+hello => carga útil del mensaje
+\x1e => separador
+2 => tipo de paquete ping
+\x1e => separador
+4 => tipo de paquete message
+world => carga útil del mensaje
+```
+
+Los paquetes están separados por el [carácter separador de registro](https://en.wikipedia.org/wiki/C0_and_C1_control_codes#Field_separators): `\x1e`
+
+Las cargas útiles binarias DEBEN estar codificadas en base64 y prefijadas con un carácter `b`:
+
+Ejemplo:
+
+```
+4hello\x1ebAQIDBA==
+
+con:
+
+4 => tipo de paquete message
+hello => carga útil del mensaje
+\x1e => separador
+b => prefijo binario
+AQIDBA== => buffer <01 02 03 04> codificado como base64
+```
+
+El cliente DEBERÍA usar el valor `maxPayload` enviado durante el [handshake](#handshake) para decidir cuántos paquetes deben concatenarse.
+
+### WebSocket
+
+Cada paquete Engine.IO se envía en su propio [frame WebSocket](https://datatracker.ietf.org/doc/html/rfc6455#section-5).
+
+Formato:
+
+```
+[]
+```
+
+Ejemplo:
+
+```
+4hello
+
+con:
+
+4 => tipo de paquete message
+hello => carga útil del mensaje (codificada en UTF-8)
+```
+
+Las cargas útiles binarias se envían tal cual, sin modificación.
+
+## Historial
+
+### De v2 a v3
+
+- añadir soporte para datos binarios
+
+La [2da versión](https://github.com/socketio/engine.io-protocol/tree/v2) del protocolo se usa en Socket.IO `v0.9` y anteriores.
+
+La [3ra versión](https://github.com/socketio/engine.io-protocol/tree/v3) del protocolo se usa en Socket.IO `v1` y `v2`.
+
+### De v3 a v4
+
+- invertir mecanismo ping/pong
+
+Los paquetes ping ahora son enviados por el servidor, porque los temporizadores configurados en los navegadores no son lo suficientemente confiables. Sospechamos que muchos problemas de timeout vinieron de temporizadores retrasados en el lado del cliente.
+
+- siempre usar base64 al codificar una carga útil con datos binarios
+
+Este cambio permite tratar todas las cargas útiles (con o sin binarios) de la misma manera, sin tener que tomar en cuenta si el cliente o el transporte actual soporta datos binarios o no.
+
+Por favor nota que esto solo aplica a HTTP long-polling. Los datos binarios se envían en frames WebSocket sin transformación adicional.
+
+- usar un separador de registro (`\x1e`) en lugar de contar caracteres
+
+Contar caracteres impedía (o al menos hacía más difícil) implementar el protocolo en otros lenguajes, que pueden no usar la codificación UTF-16.
+
+Por ejemplo, `€` se codificaba como `2:4€`, aunque `Buffer.byteLength('€') === 3`.
+
+Nota: esto asume que el separador de registro no se usa en los datos.
+
+La 4ta versión (actual) está incluida en Socket.IO `v3` y superiores.
+
+## Suite de pruebas
+
+La suite de pruebas en el directorio [`test-suite/`](https://github.com/socketio/engine.io-protocol/tree/main/test-suite) te permite verificar la conformidad de una implementación de servidor.
+
+Uso:
+
+- en Node.js: `npm ci && npm test`
+- en un navegador: simplemente abre el archivo `index.html` en tu navegador
+
+Para referencia, aquí está la configuración esperada para que el servidor JavaScript pase todas las pruebas:
+
+```js
+import { listen } from "engine.io";
+
+const server = listen(3000, {
+ pingInterval: 300,
+ pingTimeout: 200,
+ maxPayload: 1e6,
+ cors: {
+ origin: "*"
+ }
+});
+
+server.on("connection", socket => {
+ socket.on("data", (...args) => {
+ socket.send(...args);
+ });
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/faq.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/faq.md
new file mode 100644
index 00000000..df735e5b
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/faq.md
@@ -0,0 +1,101 @@
+---
+title: FAQ
+sidebar_position: 1
+slug: /faq/
+---
+
+import TOCInline from '@theme/TOCInline';
+
+Aquí hay una lista de preguntas comunes sobre Socket.IO:
+
+
+
+## ¿Algo no funciona correctamente, pueden ayudarme?
+
+Por favor revisa la [guía de solución de problemas](../01-Documentation/troubleshooting.md).
+
+## ¿Cómo funciona internamente?
+
+La conexión de Socket.IO puede establecerse con diferentes transportes de bajo nivel:
+
+- HTTP long-polling
+- [WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSockets_API)
+- [WebTransport](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API)
+
+Socket.IO elegirá automáticamente la mejor opción disponible, dependiendo de:
+
+- las capacidades del navegador (ver [aquí](https://caniuse.com/websockets) y [aquí](https://caniuse.com/webtransport))
+- la red (algunas redes bloquean conexiones WebSocket y/o WebTransport)
+
+Puedes encontrar más detalles sobre esto en la [sección "Cómo funciona"](../01-Documentation/how-it-works.md).
+
+## ¿Cuáles son las características proporcionadas por Socket.IO sobre WebSocket puro?
+
+¡Los WebSockets son increíbles! No, en serio. Proporcionan una forma eficiente de transferir datos entre un cliente y un servidor. Entre las ventajas:
+
+- no necesitas depender de polling periódico para obtener datos del servidor
+- no necesitas enviar repetidamente todos los encabezados HTTP al enviar datos al servidor
+
+Lo que los hace perfectos para aplicaciones de baja latencia e intensivas en datos como juegos, chats, soluciones colaborativas...
+
+Dicho esto, los WebSockets también son bastante de bajo nivel y desarrollar aplicaciones en tiempo real con WebSockets a menudo requiere una capa adicional sobre ellos:
+
+- fallback a HTTP long-polling, en caso de que la conexión WebSocket no pueda establecerse
+- reconexión automática, en caso de que la conexión WebSocket se cierre
+- acknowledgements, para enviar algunos datos y esperar una respuesta del otro lado
+- transmisión a todos o a un subconjunto de clientes conectados
+- escalar a múltiples instancias del servidor
+- recuperación de conexión, para períodos cortos de desconexión
+
+Como habrás adivinado, esta capa adicional es implementada por la biblioteca Socket.IO.
+
+## ¿Qué es WebTransport?
+
+En resumen, WebTransport es una alternativa a WebSocket que corrige varios problemas de rendimiento que afectan a los WebSockets como el [bloqueo de cabeza de línea](https://es.wikipedia.org/wiki/Bloqueo_de_cabeza_de_l%C3%ADnea).
+
+Si quieres más información sobre esta nueva API web (que se incluyó en Chrome en enero de 2022 y en Firefox en junio de 2023), por favor revisa estos enlaces:
+
+- https://w3c.github.io/webtransport/
+- https://developer.mozilla.org/en-US/docs/Web/API/WebTransport
+- https://developer.chrome.com/articles/webtransport/
+
+:::note
+
+El soporte para WebTransport no está habilitado por defecto en Socket.IO, ya que requiere un contexto seguro (HTTPS). Por favor revisa el [tutorial dedicado](/get-started/webtransport) si quieres experimentar con WebTransport.
+
+:::
+
+## ¿Socket.IO almacena los mensajes?
+
+El servidor Socket.IO no almacena ningún mensaje.
+
+Es deber de tu aplicación persistir esos mensajes *en algún lugar* para los clientes que no están actualmente conectados.
+
+:::tip
+
+Dicho esto, Socket.IO almacenará los mensajes por un breve período de tiempo si habilitas la [característica de recuperación del estado de conexión](../01-Documentation/connection-state-recovery.md).
+
+:::
+
+## ¿Cuáles son las garantías de entrega de Socket.IO?
+
+Socket.IO **garantiza el orden de los mensajes**, sin importar qué transporte de bajo nivel se use (incluso al cambiar entre dos transportes).
+
+Además, por defecto Socket.IO proporciona una garantía de entrega **como máximo una vez** (también conocida como "disparar y olvidar"), lo que significa que bajo ciertas circunstancias un mensaje podría perderse y no se intentará ningún reintento.
+
+Más información sobre esto [aquí](../01-Documentation/delivery-guarantees.md).
+
+## ¿Cómo identificar a un usuario dado?
+
+No hay concepto de usuario en Socket.IO.
+
+Es deber de tu aplicación vincular una conexión Socket.IO dada a una cuenta de usuario.
+
+Para aplicaciones Node.js, puedes por ejemplo:
+
+- reutilizar el contexto de usuario proporcionado por [Passport](https://www.passportjs.org/) (revisa [este tutorial](/how-to/use-with-express-session))
+- o usar la opción [`auth`](../../client-options.md#auth) en el lado del cliente para enviar las credenciales del usuario y validarlas en un [middleware](../02-Server/middlewares.md)
+
+## ¿Dónde puedo encontrar el changelog?
+
+Por favor consulta [aquí](../../changelog/index.md).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/glossary.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/glossary.md
new file mode 100644
index 00000000..0be9e3d6
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/glossary.md
@@ -0,0 +1,62 @@
+---
+title: Glosario
+sidebar_position: 2
+slug: /glossary/
+---
+
+Aquí listamos los términos que están relacionados con el ecosistema Socket.IO:
+
+- [Adaptador](#adaptador)
+- [Engine.IO](#engineio)
+- [Namespace](#namespace)
+- [Sala](#sala)
+- [Transporte](#transporte)
+
+## Adaptador
+
+Un Adaptador es un componente del lado del servidor que es responsable de:
+
+- almacenar las relaciones entre las instancias de Socket y las [salas](../04-Events/rooms.md)
+- transmitir eventos a [todos](../04-Events/broadcasting-events.md) (o un subconjunto de) clientes
+
+Además del [adaptador en memoria](https://github.com/socketio/socket.io-adapter/) que está incluido por defecto con el servidor Socket.IO, actualmente hay 5 adaptadores oficiales:
+
+- el [adaptador Redis](../05-Adapters/adapter-redis.md)
+- el [adaptador Redis Streams](../05-Adapters/adapter-redis-streams.md)
+- el [adaptador MongoDB](../05-Adapters/adapter-mongo.md)
+- el [adaptador Postgres](../05-Adapters/adapter-postgres.md)
+- el [adaptador Cluster](../05-Adapters/adapter-cluster.md)
+
+El adaptador en memoria puede ser extendido para agregar soporte para otros sistemas de mensajería, como RabbitMQ o Google Pub/Sub por ejemplo.
+
+Por favor consulta la documentación [aquí](../05-Adapters/adapter.md).
+
+## Engine.IO
+
+Engine.IO es un componente interno de Socket.IO, que es responsable de establecer la conexión de bajo nivel entre el servidor y el cliente.
+
+Encontrarás más información [aquí](../01-Documentation/how-it-works.md).
+
+## Namespace
+
+Un Namespace es un concepto que permite dividir la lógica de la aplicación en el lado del servidor.
+
+Por favor consulta la documentación [aquí](../06-Advanced/namespaces.md).
+
+## Sala
+
+Una Sala es un concepto del lado del servidor que permite transmitir datos a un subconjunto de clientes.
+
+Por favor consulta la documentación [aquí](../04-Events/rooms.md).
+
+## Transporte
+
+Un Transporte representa la forma de bajo nivel de establecer una conexión entre el servidor y el cliente.
+
+Actualmente hay tres transportes implementados:
+
+- HTTP long-polling
+- [WebSocket](https://developer.mozilla.org/es/docs/Web/API/WebSockets_API)
+- [WebTransport](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport_API)
+
+Por favor consulta la documentación [aquí](../01-Documentation/how-it-works.md#transports).
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/sio-protocol.md b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/sio-protocol.md
new file mode 100644
index 00000000..46c90a64
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/categories/08-Miscellaneous/sio-protocol.md
@@ -0,0 +1,725 @@
+---
+title: El protocolo Socket.IO
+sidebar_position: 4
+slug: /socket-io-protocol/
+---
+
+Este documento describe la 5ta versión del protocolo Socket.IO.
+
+La fuente de este documento se puede encontrar [aquí](https://github.com/socketio/socket.io-protocol).
+
+**Tabla de contenido**
+
+- [Introducción](#introducción)
+- [Protocolo de intercambio](#protocolo-de-intercambio)
+ - [Conexión a un namespace](#conexión-a-un-namespace)
+ - [Envío y recepción de datos](#envío-y-recepción-de-datos)
+ - [Acknowledgement](#acknowledgement)
+ - [Desconexión de un namespace](#desconexión-de-un-namespace)
+- [Codificación de paquetes](#codificación-de-paquetes)
+ - [Formato](#formato)
+ - [Ejemplos](#ejemplos)
+ - [Conexión a un namespace](#conexión-a-un-namespace-1)
+ - [Envío y recepción de datos](#envío-y-recepción-de-datos-1)
+ - [Acknowledgement](#acknowledgement-1)
+ - [Desconexión de un namespace](#desconexión-de-un-namespace-1)
+- [Sesión de ejemplo](#sesión-de-ejemplo)
+- [Historial](#historial)
+ - [Diferencia entre v5 y v4](#diferencia-entre-v5-y-v4)
+ - [Diferencia entre v4 y v3](#diferencia-entre-v4-y-v3)
+ - [Diferencia entre v3 y v2](#diferencia-entre-v3-y-v2)
+ - [Diferencia entre v2 y v1](#diferencia-entre-v2-y-v1)
+ - [Revisión inicial](#revisión-inicial)
+- [Suite de pruebas](#suite-de-pruebas)
+
+
+## Introducción
+
+El protocolo Socket.IO permite comunicación [full-duplex](https://es.wikipedia.org/wiki/D%C3%BAplex_(telecomunicaciones)#Full-d%C3%BAplex) y de bajo overhead entre un cliente y un servidor.
+
+Está construido sobre [el protocolo Engine.IO](https://github.com/socketio/engine.io-protocol), que maneja la plomería de bajo nivel con WebSocket y HTTP long-polling.
+
+El protocolo Socket.IO añade las siguientes características:
+
+- multiplexación (referida como ["namespace"](https://socket.io/docs/v4/namespaces) en la jerga de Socket.IO)
+
+Ejemplo con la API de JavaScript:
+
+*Servidor*
+
+```js
+// declarar el namespace
+const namespace = io.of("/admin");
+// manejar la conexión al namespace
+namespace.on("connection", (socket) => {
+ // ...
+});
+```
+
+*Cliente*
+
+```js
+// alcanzar el namespace principal
+const socket1 = io();
+// alcanzar el namespace "/admin" (con la misma conexión WebSocket subyacente)
+const socket2 = io("/admin");
+// manejar la conexión al namespace
+socket2.on("connect", () => {
+ // ...
+});
+```
+
+- acknowledgement de paquetes
+
+Ejemplo con la API de JavaScript:
+
+```js
+// en un lado
+socket.emit("hello", "foo", (arg) => {
+ console.log("recibido", arg);
+});
+
+// en el otro lado
+socket.on("hello", (arg, ack) => {
+ ack("bar");
+});
+```
+
+La implementación de referencia está escrita en [TypeScript](https://www.typescriptlang.org/):
+
+- servidor: https://github.com/socketio/socket.io
+- cliente: https://github.com/socketio/socket.io-client
+
+
+## Protocolo de intercambio
+
+Un paquete Socket.IO contiene los siguientes campos:
+
+- un tipo de paquete (entero)
+- un namespace (cadena)
+- opcionalmente, una carga útil (Object | Array)
+- opcionalmente, un id de acknowledgment (entero)
+
+Aquí está la lista de tipos de paquetes disponibles:
+
+| Tipo | ID | Uso |
+|---------------|-----|-----------------------------------------------------------------------------------------|
+| CONNECT | 0 | Usado durante la [conexión a un namespace](#conexión-a-un-namespace). |
+| DISCONNECT | 1 | Usado al [desconectarse de un namespace](#desconexión-de-un-namespace). |
+| EVENT | 2 | Usado para [enviar datos](#envío-y-recepción-de-datos) al otro lado. |
+| ACK | 3 | Usado para [acknowledger](#acknowledgement) un evento. |
+| CONNECT_ERROR | 4 | Usado durante la [conexión a un namespace](#conexión-a-un-namespace). |
+| BINARY_EVENT | 5 | Usado para [enviar datos binarios](#envío-y-recepción-de-datos) al otro lado. |
+| BINARY_ACK | 6 | Usado para [acknowledger](#acknowledgement) un evento (la respuesta incluye datos binarios). |
+
+
+### Conexión a un namespace
+
+Al comienzo de una sesión Socket.IO, el cliente DEBE enviar un paquete `CONNECT`:
+
+El servidor DEBE responder con:
+
+- un paquete `CONNECT` si la conexión es exitosa, con el ID de sesión en la carga útil
+- o un paquete `CONNECT_ERROR` si la conexión no está permitida
+
+```
+CLIENT SERVER
+
+ │ ───────────────────────────────────────────────────────► │
+ │ { type: CONNECT, namespace: "/" } │
+ │ ◄─────────────────────────────────────────────────────── │
+ │ { type: CONNECT, namespace: "/", data: { sid: "..." } } │
+```
+
+Si el servidor no recibe un paquete `CONNECT` primero, DEBE cerrar la conexión inmediatamente.
+
+Un cliente PUEDE estar conectado a múltiples namespaces al mismo tiempo, con la misma conexión WebSocket subyacente.
+
+Ejemplos:
+
+- con el namespace principal (llamado `"/"`)
+
+```
+Client > { type: CONNECT, namespace: "/" }
+Server > { type: CONNECT, namespace: "/", data: { sid: "wZX3oN0bSVIhsaknAAAI" } }
+```
+
+- con un namespace personalizado
+
+```
+Client > { type: CONNECT, namespace: "/admin" }
+Server > { type: CONNECT, namespace: "/admin", data: { sid: "oSO0OpakMV_3jnilAAAA" } }
+```
+
+- con una carga útil adicional
+
+```
+Client > { type: CONNECT, namespace: "/admin", data: { "token": "123" } }
+Server > { type: CONNECT, namespace: "/admin", data: { sid: "iLnRaVGHY4B75TeVAAAB" } }
+```
+
+- en caso de que la conexión sea rechazada
+
+```
+Client > { type: CONNECT, namespace: "/" }
+Server > { type: CONNECT_ERROR, namespace: "/", data: { message: "No autorizado" } }
+```
+
+### Envío y recepción de datos
+
+Una vez que la [conexión a un namespace](#conexión-a-un-namespace) está establecida, el cliente y el servidor pueden comenzar a intercambiar datos:
+
+```
+CLIENT SERVER
+
+ │ ───────────────────────────────────────────────────────► │
+ │ { type: EVENT, namespace: "/", data: ["foo"] } │
+ │ │
+ │ ◄─────────────────────────────────────────────────────── │
+ │ { type: EVENT, namespace: "/", data: ["bar"] } │
+```
+
+La carga útil es obligatoria y DEBE ser un array no vacío. Si ese no es el caso, el receptor DEBE cerrar la conexión.
+
+Ejemplos:
+
+- con el namespace principal
+
+```
+Client > { type: EVENT, namespace: "/", data: ["foo"] }
+```
+
+- con un namespace personalizado
+
+```
+Server > { type: EVENT, namespace: "/admin", data: ["bar"] }
+```
+
+- con datos binarios
+
+```
+Client > { type: BINARY_EVENT, namespace: "/", data: ["baz", > ] }
+```
+
+### Acknowledgement
+
+El emisor PUEDE incluir un ID de evento para solicitar un acknowledgement del receptor:
+
+```
+CLIENT SERVER
+
+ │ ───────────────────────────────────────────────────────► │
+ │ { type: EVENT, namespace: "/", data: ["foo"], id: 12 } │
+ │ ◄─────────────────────────────────────────────────────── │
+ │ { type: ACK, namespace: "/", data: ["bar"], id: 12 } │
+```
+
+El receptor DEBE responder con un paquete `ACK` con el mismo ID de evento.
+
+La carga útil es obligatoria y DEBE ser un array (posiblemente vacío).
+
+Ejemplos:
+
+- con el namespace principal
+
+```
+Client > { type: EVENT, namespace: "/", data: ["foo"], id: 12 }
+Server > { type: ACK, namespace: "/", data: [], id: 12 }
+```
+
+- con un namespace personalizado
+
+```
+Server > { type: EVENT, namespace: "/admin", data: ["foo"], id: 13 }
+Client > { type: ACK, namespace: "/admin", data: ["bar"], id: 13 }
+```
+
+- con datos binarios
+
+```
+Client > { type: BINARY_EVENT, namespace: "/", data: ["foo", ], id: 14 }
+Server > { type: ACK, namespace: "/", data: ["bar"], id: 14 }
+
+o
+
+Server > { type: EVENT, namespace: "/", data: ["foo" ], id: 15 }
+Client > { type: BINARY_ACK, namespace: "/", data: ["bar", ], id: 15 }
+```
+
+### Desconexión de un namespace
+
+En cualquier momento, un lado puede terminar la conexión a un namespace enviando un paquete `DISCONNECT`:
+
+```
+CLIENT SERVER
+
+ │ ───────────────────────────────────────────────────────► │
+ │ { type: DISCONNECT, namespace: "/" } │
+```
+
+No se espera respuesta del otro lado. La conexión de bajo nivel PUEDE mantenerse activa si el cliente está conectado a otro namespace.
+
+
+## Codificación de paquetes
+
+Esta sección detalla la codificación usada por el parser por defecto que está incluido en el servidor y cliente Socket.IO, y cuya fuente se puede encontrar [aquí](https://github.com/socketio/socket.io-parser).
+
+Las implementaciones de servidor y cliente en JavaScript también soportan parsers personalizados, que tienen diferentes compromisos y pueden beneficiar a ciertos tipos de aplicaciones. Por favor consulta [socket.io-json-parser](https://github.com/socketio/socket.io-json-parser) o [socket.io-msgpack-parser](https://github.com/socketio/socket.io-msgpack-parser) como ejemplo.
+
+Por favor también nota que cada paquete Socket.IO se envía como un paquete `message` de Engine.IO (más información [aquí](https://github.com/socketio/engine.io-protocol)), así que el resultado codificado será prefijado por el carácter `"4"` cuando se envíe por el cable (en el cuerpo de solicitud/respuesta con HTTP long-polling, o en el frame WebSocket).
+
+### Formato
+
+```
+[<# de adjuntos binarios>-][,][][carga útil JSON-stringified sin binarios]
+
++ adjuntos binarios extraídos
+```
+
+Nota: el namespace solo se incluye si es diferente del namespace principal (`/`)
+
+### Ejemplos
+
+#### Conexión a un namespace
+
+- con el namespace principal
+
+*Paquete*
+
+```
+{ type: CONNECT, namespace: "/" }
+```
+
+*Codificado*
+
+```
+0
+```
+
+- con un namespace personalizado
+
+*Paquete*
+
+```
+{ type: CONNECT, namespace: "/admin", data: { sid: "oSO0OpakMV_3jnilAAAA" } }
+```
+
+*Codificado*
+
+```
+0/admin,{"sid":"oSO0OpakMV_3jnilAAAA"}
+```
+
+- en caso de que la conexión sea rechazada
+
+*Paquete*
+
+```
+{ type: CONNECT_ERROR, namespace: "/", data: { message: "No autorizado" } }
+```
+
+*Codificado*
+
+```
+4{"message":"No autorizado"}
+```
+
+#### Envío y recepción de datos
+
+- con el namespace principal
+
+*Paquete*
+
+```
+{ type: EVENT, namespace: "/", data: ["foo"] }
+```
+
+*Codificado*
+
+```
+2["foo"]
+```
+
+- con un namespace personalizado
+
+*Paquete*
+
+```
+{ type: EVENT, namespace: "/admin", data: ["bar"] }
+```
+
+*Codificado*
+
+```
+2/admin,["bar"]
+```
+
+- con datos binarios
+
+*Paquete*
+
+```
+{ type: BINARY_EVENT, namespace: "/", data: ["baz", > ] }
+```
+
+*Codificado*
+
+```
+51-["baz",{"_placeholder":true,"num":0}]
+
++ >
+```
+
+- con múltiples adjuntos
+
+*Paquete*
+
+```
+{ type: BINARY_EVENT, namespace: "/admin", data: ["baz", >, > ] }
+```
+
+*Codificado*
+
+```
+52-/admin,["baz",{"_placeholder":true,"num":0},{"_placeholder":true,"num":1}]
+
++ >
++ >
+```
+
+Por favor recuerda que cada paquete Socket.IO está envuelto en un paquete `message` de Engine.IO, así que serán prefijados por el carácter `"4"` cuando se envíen por el cable.
+
+Ejemplo: `{ type: EVENT, namespace: "/", data: ["foo"] }` se enviará como `42["foo"]`
+
+#### Acknowledgement
+
+- con el namespace principal
+
+*Paquete*
+
+```
+{ type: EVENT, namespace: "/", data: ["foo"], id: 12 }
+```
+
+*Codificado*
+
+```
+212["foo"]
+```
+
+- con un namespace personalizado
+
+*Paquete*
+
+```
+{ type: ACK, namespace: "/admin", data: ["bar"], id: 13 }
+```
+
+*Codificado*
+
+```
+3/admin,13["bar"]`
+```
+
+- con datos binarios
+
+*Paquete*
+
+```
+{ type: BINARY_ACK, namespace: "/", data: ["bar", >], id: 15 }
+```
+
+*Codificado*
+
+```
+61-15["bar",{"_placeholder":true,"num":0}]
+
++ >
+```
+
+#### Desconexión de un namespace
+
+- con el namespace principal
+
+*Paquete*
+
+```
+{ type: DISCONNECT, namespace: "/" }
+```
+
+*Codificado*
+
+```
+1
+```
+
+- con un namespace personalizado
+
+```
+{ type: DISCONNECT, namespace: "/admin" }
+```
+
+*Codificado*
+
+```
+1/admin,
+```
+
+
+## Sesión de ejemplo
+
+Aquí hay un ejemplo de lo que se envía por el cable al combinar los protocolos Engine.IO y Socket.IO.
+
+- Solicitud n°1 (paquete open)
+
+```
+GET /socket.io/?EIO=4&transport=polling&t=N8hyd6w
+< HTTP/1.1 200 OK
+< Content-Type: text/plain; charset=UTF-8
+0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000}
+```
+
+Detalles:
+
+```
+0 => tipo de paquete "open" de Engine.IO
+{"sid":... => los datos de handshake de Engine.IO
+```
+
+Nota: el parámetro de consulta `t` se usa para asegurar que la solicitud no sea cacheada por el navegador.
+
+- Solicitud n°2 (solicitud de conexión al namespace):
+
+```
+POST /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
+< HTTP/1.1 200 OK
+< Content-Type: text/plain; charset=UTF-8
+40
+```
+
+Detalles:
+
+```
+4 => tipo de paquete "message" de Engine.IO
+0 => tipo de paquete "CONNECT" de Socket.IO
+```
+
+- Solicitud n°3 (aprobación de conexión al namespace)
+
+```
+GET /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
+< HTTP/1.1 200 OK
+< Content-Type: text/plain; charset=UTF-8
+40{"sid":"wZX3oN0bSVIhsaknAAAI"}
+```
+
+- Solicitud n°4
+
+`socket.emit('hey', 'Jude')` se ejecuta en el servidor:
+
+```
+GET /socket.io/?EIO=4&transport=polling&t=N8hyd7H&sid=lv_VI97HAXpY6yYWAAAC
+< HTTP/1.1 200 OK
+< Content-Type: text/plain; charset=UTF-8
+42["hey","Jude"]
+```
+
+Detalles:
+
+```
+4 => tipo de paquete "message" de Engine.IO
+2 => tipo de paquete "EVENT" de Socket.IO
+[...] => contenido
+```
+
+- Solicitud n°5 (mensaje saliente)
+
+`socket.emit('hello'); socket.emit('world');` se ejecuta en el cliente:
+
+```
+POST /socket.io/?EIO=4&transport=polling&t=N8hzxke&sid=lv_VI97HAXpY6yYWAAAC
+> Content-Type: text/plain; charset=UTF-8
+42["hello"]\x1e42["world"]
+< HTTP/1.1 200 OK
+< Content-Type: text/plain; charset=UTF-8
+ok
+```
+
+Detalles:
+
+```
+4 => tipo de paquete "message" de Engine.IO
+2 => tipo de paquete "EVENT" de Socket.IO
+["hello"] => el 1er contenido
+\x1e => separador
+4 => tipo de paquete "message" de Engine.IO
+2 => tipo de paquete "EVENT" de Socket.IO
+["world"] => el 2do contenido
+```
+
+- Solicitud n°6 (upgrade a WebSocket)
+
+```
+GET /socket.io/?EIO=4&transport=websocket&sid=lv_VI97HAXpY6yYWAAAC
+< HTTP/1.1 101 Switching Protocols
+```
+
+Frames WebSocket:
+
+```
+< 2probe => solicitud probe de Engine.IO
+> 3probe => respuesta probe de Engine.IO
+> 5 => tipo de paquete "upgrade" de Engine.IO
+> 42["hello"]
+> 42["world"]
+> 40/admin, => solicitar acceso al namespace admin (paquete "CONNECT" de Socket.IO)
+< 40/admin,{"sid":"-G5j-67EZFp-q59rADQM"} => conceder acceso al namespace admin
+> 42/admin,1["tellme"] => paquete "EVENT" de Socket.IO con acknowledgement
+< 461-/admin,1[{"_placeholder":true,"num":0}] => paquete "BINARY_ACK" de Socket.IO con un placeholder
+< => el adjunto binario (enviado en el siguiente frame)
+... después de un rato sin mensaje
+> 2 => tipo de paquete "ping" de Engine.IO
+< 3 => tipo de paquete "pong" de Engine.IO
+> 1 => tipo de paquete "close" de Engine.IO
+```
+
+## Historial
+
+### Diferencia entre v5 y v4
+
+La 5ta revisión (actual) del protocolo Socket.IO se usa en Socket.IO v3 y superiores (`v3.0.0` fue lanzado en noviembre de 2020).
+
+Está construido sobre la 4ta revisión de [el protocolo Engine.IO](https://github.com/socketio/engine.io-protocol) (de ahí el parámetro de consulta `EIO=4`).
+
+Lista de cambios:
+
+- eliminar la conexión implícita al namespace por defecto
+
+En versiones anteriores, un cliente siempre estaba conectado al namespace por defecto, incluso si solicitaba acceso a otro namespace.
+
+Este ya no es el caso, el cliente debe enviar un paquete `CONNECT` en cualquier caso.
+
+Commits: [09b6f23](https://github.com/socketio/socket.io/commit/09b6f2333950b8afc8c1400b504b01ad757876bd) (servidor) y [249e0be](https://github.com/socketio/socket.io-client/commit/249e0bef9071e7afd785485961c4eef0094254e8) (cliente)
+
+
+- renombrar `ERROR` a `CONNECT_ERROR`
+
+El significado y el número de código (4) no se modifican: este tipo de paquete todavía es usado por el servidor cuando la conexión a un namespace es rechazada. Pero sentimos que el nombre es más autodescriptivo.
+
+Commits: [d16c035](https://github.com/socketio/socket.io/commit/d16c035d258b8deb138f71801cb5aeedcdb3f002) (servidor) y [13e1db7c](https://github.com/socketio/socket.io-client/commit/13e1db7c94291c583d843beaa9e06ee041ae4f26) (cliente).
+
+
+- el paquete `CONNECT` ahora puede contener una carga útil
+
+El cliente puede enviar una carga útil para propósitos de autenticación/autorización. Ejemplo:
+
+```json
+{
+ "type": 0,
+ "nsp": "/admin",
+ "data": {
+ "token": "123"
+ }
+}
+```
+
+En caso de éxito, el servidor responde con una carga útil que contiene el ID del Socket. Ejemplo:
+
+```json
+{
+ "type": 0,
+ "nsp": "/admin",
+ "data": {
+ "sid": "CjdVH4TQvovi1VvgAC5Z"
+ }
+}
+```
+
+Este cambio significa que el ID de la conexión Socket.IO ahora será diferente del ID de la conexión Engine.IO subyacente (el que se encuentra en los parámetros de consulta de las solicitudes HTTP).
+
+Commits: [2875d2c](https://github.com/socketio/socket.io/commit/2875d2cfdfa463e64cb520099749f543bbc4eb15) (servidor) y [bbe94ad](https://github.com/socketio/socket.io-client/commit/bbe94adb822a306c6272e977d394e3e203cae25d) (cliente)
+
+
+- la carga útil del paquete `CONNECT_ERROR` ahora es un objeto en lugar de una cadena simple
+
+Commits: [54bf4a4](https://github.com/socketio/socket.io/commit/54bf4a44e9e896dfb64764ee7bd4e8823eb7dc7b) (servidor) y [0939395](https://github.com/socketio/socket.io-client/commit/09393952e3397a0c71f239ea983f8ec1623b7c21) (cliente)
+
+
+### Diferencia entre v4 y v3
+
+La 4ta revisión del protocolo Socket.IO se usa en Socket.IO v1 (`v1.0.3` fue lanzado en junio de 2014) y v2 (`v2.0.0` fue lanzado en mayo de 2017).
+
+Los detalles de la revisión se pueden encontrar aquí: https://github.com/socketio/socket.io-protocol/tree/v4
+
+Está construido sobre la 3ra revisión de [el protocolo Engine.IO](https://github.com/socketio/engine.io-protocol) (de ahí el parámetro de consulta `EIO=3`).
+
+Lista de cambios:
+
+- añadir un tipo de paquete `BINARY_ACK`
+
+Anteriormente, un paquete `ACK` siempre se trataba como si pudiera contener objetos binarios, con búsqueda recursiva de tales objetos, lo cual podía perjudicar el rendimiento.
+
+Referencia: https://github.com/socketio/socket.io-parser/commit/ca4f42a922ba7078e840b1bc09fe3ad618acc065
+
+### Diferencia entre v3 y v2
+
+La 3ra revisión del protocolo Socket.IO se usa en las primeras versiones de Socket.IO v1 (`socket.io@1.0.0...1.0.2`) (lanzado en mayo de 2014).
+
+Los detalles de la revisión se pueden encontrar aquí: https://github.com/socketio/socket.io-protocol/tree/v3
+
+Lista de cambios:
+
+- eliminar el uso de msgpack para codificar paquetes que contienen objetos binarios (ver también [299849b](https://github.com/socketio/socket.io-parser/commit/299849b00294c3bc95817572441f3aca8ffb1f65))
+
+### Diferencia entre v2 y v1
+
+Lista de cambios:
+
+- añadir un tipo de paquete `BINARY_EVENT`
+
+Esto fue añadido durante el trabajo hacia Socket.IO 1.0, para añadir soporte para objetos binarios. Los paquetes `BINARY_EVENT` fueron codificados con [msgpack](https://msgpack.org/).
+
+### Revisión inicial
+
+Esta primera revisión fue el resultado de la separación entre el protocolo Engine.IO (plomería de bajo nivel con WebSocket / HTTP long-polling, heartbeat) y el protocolo Socket.IO. Nunca fue incluida en un lanzamiento de Socket.IO, pero allanó el camino para las siguientes iteraciones.
+
+## Suite de pruebas
+
+La suite de pruebas en el directorio [`test-suite/`](https://github.com/socketio/socket.io-protocol/tree/main/test-suite) te permite verificar la conformidad de una implementación de servidor.
+
+Uso:
+
+- en Node.js: `npm ci && npm test`
+- en un navegador: simplemente abre el archivo `index.html` en tu navegador
+
+Para referencia, aquí está la configuración esperada para que el servidor JavaScript pase todas las pruebas:
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server(3000, {
+ pingInterval: 300,
+ pingTimeout: 200,
+ maxPayload: 1000000,
+ cors: {
+ origin: "*"
+ }
+});
+
+io.on("connection", (socket) => {
+ socket.emit("auth", socket.handshake.auth);
+
+ socket.on("message", (...args) => {
+ socket.emit.apply(socket, ["message-back", ...args]);
+ });
+
+ socket.on("message-with-ack", (...args) => {
+ const ack = args.pop();
+ ack(...args);
+ })
+});
+
+io.of("/custom").on("connection", (socket) => {
+ socket.emit("auth", socket.handshake.auth);
+});
+```
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/2.5.0.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/2.5.0.md
new file mode 100644
index 00000000..5eb94b2d
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/2.5.0.md
@@ -0,0 +1,42 @@
+---
+title: Version 2.5.0
+sidebar_label: 2.5.0 (June 26, 2022)
+sidebar_position: 105
+toc_max_heading_level: 4
+---
+
+*June 26, 2022*
+
+## Server
+
+⚠️ WARNING ⚠️
+
+The default value of the `maxHttpBufferSize` option has been decreased from 100 MB to 1 MB, in order to prevent attacks by denial of service.
+
+Security advisory: [GHSA-j4f2-536g-r55m](https://github.com/advisories/GHSA-j4f2-536g-r55m)
+
+
+### Bug Fixes
+
+* fix race condition in dynamic namespaces ([05e1278](https://github.com/socketio/socket.io/commit/05e1278cfa99f3ecf3f8f0531ffe57d850e9a05b))
+* ignore packet received after disconnection ([22d4bdf](https://github.com/socketio/socket.io/commit/22d4bdf00d1a03885dc0171125faddfaef730066))
+* only set 'connected' to true after middleware execution ([226cc16](https://github.com/socketio/socket.io/commit/226cc16165f9fe60f16ff4d295fb91c8971cde35))
+* prevent the socket from joining a room after disconnection ([f223178](https://github.com/socketio/socket.io/commit/f223178eb655a7713303b21a78f9ef9e161d6458))
+
+### Dependencies
+
+- [`engine.io@~3.6.0`](https://github.com/socketio/engine.io/releases/tag/3.6.0) (https://github.com/socketio/engine.io/compare/3.5.0...3.6.0)
+- [`ws@~7.4.2`](https://github.com/websockets/ws/releases/tag/7.4.2) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* ensure buffered events are sent in order ([991eb0b](https://github.com/Automattic/socket.io-client/commit/991eb0b0289bbbf680099e6d42b302beee7568b8))
+
+### Dependencies
+
+- [`engine.io-client@~3.5.0`](https://github.com/socketio/engine.io-client/releases/tag/3.5.0) (no change)
+- [`ws@~7.4.2`](https://github.com/websockets/ws/releases/tag/7.4.2) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.0.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.0.md
new file mode 100644
index 00000000..07ec97c4
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.0.md
@@ -0,0 +1,113 @@
+---
+title: Version 4.5.0
+sidebar_label: 4.5.0 (April 23, 2022)
+sidebar_position: 107
+toc_max_heading_level: 4
+---
+
+*April 23, 2022*
+
+## Server
+
+### Bug Fixes
+
+* **typings:** ensure compatibility with TypeScript 3.x ([#4259](https://github.com/socketio/socket.io/issues/4259)) ([02c87a8](https://github.com/socketio/socket.io/commit/02c87a85614e217b8e7b93753f315790ae9d99f6))
+
+
+### Features
+
+#### Catch-all listeners for outgoing packets
+
+This is similar to `onAny()`, but for outgoing packets.
+
+Syntax:
+
+```js
+socket.onAnyOutgoing((event, ...args) => {
+ console.log(event);
+});
+```
+
+Added in [531104d](https://github.com/socketio/socket.io/commit/531104d332690138b7aab84d5583d6204132c8b4).
+
+#### Broadcast and expect multiple acknowledgements
+
+Syntax:
+
+```js
+io.timeout(1000).emit("some-event", (err, responses) => {
+ // ...
+});
+```
+
+Added in [8b20457](https://github.com/socketio/socket.io/commit/8b204570a94979bbec307f23ca078f30f5cf07b0).
+
+#### `maxHttpBufferSize` value negotiation
+
+A "maxPayload" field is now included in the Engine.IO handshake, so that clients in HTTP long-polling can decide how many packets they have to send to stay under the `maxHttpBufferSize` value.
+
+This is a backward compatible change which should not mandate a new major revision of the protocol (we stay in v4), as we only add a field in the JSON-encoded handshake data:
+
+```
+0{"sid":"lv_VI97HAXpY6yYWAAAC","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":5000,"maxPayload":1000000}
+```
+
+Added in [088dcb4](https://github.com/socketio/engine.io/commit/088dcb4dff60df39785df13d0a33d3ceaa1dff38).
+
+### Dependencies
+
+- [`engine.io@~6.2.0`](https://github.com/socketio/engine.io/releases/tag/6.2.0) (https://github.com/socketio/engine.io/compare/6.1.0...6.2.0)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
+
+
+
+## Client
+
+### Features
+
+#### Additional details for the disconnect event
+
+The "disconnect" event will now include additional details to help debugging if anything has gone wrong.
+
+Example when a payload is over the maxHttpBufferSize value in HTTP long-polling mode:
+
+```js
+socket.on("disconnect", (reason, details) => {
+ console.log(reason); // "transport error"
+
+ // in that case, details is an error object
+ console.log(details.message); "xhr post error"
+ console.log(details.description); // 413 (the HTTP status of the response)
+
+ // details.context refers to the XMLHttpRequest object
+ console.log(details.context.status); // 413
+ console.log(details.context.responseText); // ""
+});
+```
+
+Added in [b862924](https://github.com/socketio/socket.io-client/commit/b862924b7f1720979e5db2f0154906b305d420e3).
+
+#### Catch-all listeners for outgoing packets
+
+This is similar to `onAny()`, but for outgoing packets.
+
+Syntax:
+
+```js
+socket.onAnyOutgoing((event, ...args) => {
+ console.log(event);
+});
+```
+
+Added in [74e3e60](https://github.com/socketio/socket.io-client/commit/74e3e601a43133b2c0ea43c3de2764cc55b57b5a).
+
+#### Slice write buffer according to the maxPayload value
+
+The server will now include a "maxPayload" field in the handshake details, allowing the clients to decide how many packets they have to send to stay under the `maxHttpBufferSize` value.
+
+Added in [46fdc2f](https://github.com/socketio/engine.io-client/commit/46fdc2f0ed352b454614247406689edc9d908927).
+
+### Dependencies
+
+- [`engine.io-client@~6.2.1`](https://github.com/socketio/engine.io-client/releases/tag/6.2.1) (https://github.com/socketio/engine.io-client/compare/6.1.1...6.2.1)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.1.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.1.md
new file mode 100644
index 00000000..59ecd304
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.1.md
@@ -0,0 +1,31 @@
+---
+title: Version 4.5.1
+sidebar_label: 4.5.1 (May 17, 2022)
+sidebar_position: 106
+toc_max_heading_level: 4
+---
+
+*May 17, 2022*
+
+## Server
+
+### Bug Fixes
+
+* forward the local flag to the adapter when using fetchSockets() ([30430f0](https://github.com/socketio/socket.io/commit/30430f0985f8e7c49394543d4c84913b6a15df60))
+* **typings:** add HTTPS server to accepted types ([#4351](https://github.com/socketio/socket.io/issues/4351)) ([9b43c91](https://github.com/socketio/socket.io/commit/9b43c9167cff817c60fa29dbda2ef7cd938aff51))
+
+### Dependencies
+
+- [`engine.io@~6.2.0`](https://github.com/socketio/engine.io/releases/tag/6.2.0) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
+
+
+
+## Client
+
+There were some minor bug fixes on the server side, which mandate a client bump.
+
+### Dependencies
+
+- [`engine.io-client@~6.2.1`](https://github.com/socketio/engine.io-client/releases/tag/6.2.1) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.2.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.2.md
new file mode 100644
index 00000000..7bbca8f2
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.2.md
@@ -0,0 +1,33 @@
+---
+title: Version 4.5.2
+sidebar_label: 4.5.2 (September 2, 2022)
+sidebar_position: 104
+toc_max_heading_level: 4
+---
+
+*September 2, 2022*
+
+## Server
+
+### Bug Fixes
+
+* prevent the socket from joining a room after disconnection ([18f3fda](https://github.com/socketio/socket.io/commit/18f3fdab12947a9fee3e9c37cfc1da97027d1473))
+* **uws:** prevent the server from crashing after upgrade ([ba497ee](https://github.com/socketio/socket.io/commit/ba497ee3eb52c4abf1464380d015d8c788714364))
+
+### Dependencies
+
+- [`engine.io@~6.2.0`](https://github.com/socketio/engine.io/releases/tag/6.2.0) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* handle ill-formatted packet from server ([c597023](https://github.com/socketio/socket.io-client/commit/c5970231699aa47b00c4a617af4239d0fa90fa53))
+
+### Dependencies
+
+- [`engine.io-client@~6.2.1`](https://github.com/socketio/engine.io-client/releases/tag/6.2.1) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.3.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.3.md
new file mode 100644
index 00000000..0d99850f
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.3.md
@@ -0,0 +1,33 @@
+---
+title: Version 4.5.3
+sidebar_label: 4.5.3 (October 15, 2022)
+sidebar_position: 103
+toc_max_heading_level: 4
+---
+
+*October 15, 2022*
+
+## Server
+
+### Bug Fixes
+
+* **typings:** accept an HTTP2 server in the constructor ([d3d0a2d](https://github.com/socketio/socket.io/commit/d3d0a2d5beaff51fd145f810bcaf6914213f8a06))
+* **typings:** apply types to "io.timeout(...).emit()" calls ([e357daf](https://github.com/socketio/socket.io/commit/e357daf5858560bc84e7e50cd36f0278d6721ea1))
+
+### Dependencies
+
+- [`engine.io@~6.2.0`](https://github.com/socketio/engine.io/releases/tag/6.2.1) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* do not swallow user exceptions ([2403b88](https://github.com/socketio/socket.io-client/commit/2403b88057bf3fd32eb2047c82be26c455c13a2f))
+
+### Dependencies
+
+- [`engine.io-client@~6.2.3`](https://github.com/socketio/engine.io-client/tree/6.2.3) (https://github.com/socketio/engine.io-client/compare/6.2.1...6.2.3)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.4.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.4.md
new file mode 100644
index 00000000..c8a1920f
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.5.4.md
@@ -0,0 +1,31 @@
+---
+title: Version 4.5.4
+sidebar_label: 4.5.4 (November 22, 2022)
+sidebar_position: 102
+toc_max_heading_level: 4
+---
+
+*November 22, 2022*
+
+## Server
+
+This release contains a bump of:
+
+- `engine.io` in order to fix [CVE-2022-41940](https://github.com/socketio/engine.io/security/advisories/GHSA-r7qp-cfhv-p84w)
+- `socket.io-parser` in order to fix [CVE-2022-2421](https://github.com/advisories/GHSA-qm95-pgcg-qqfq).
+
+### Dependencies
+
+- [`engine.io@~6.2.1`](https://github.com/socketio/engine.io/releases/tag/6.2.1) (https://github.com/socketio/engine.io/compare/6.2.0...6.2.1)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
+
+
+
+## Client
+
+This release contains a bump of the `socket.io-parser` dependency, in order to fix [CVE-2022-2421](https://github.com/advisories/GHSA-qm95-pgcg-qqfq).
+
+### Dependencies
+
+- [`engine.io-client@~6.2.3`](https://github.com/socketio/engine.io-client/tree/6.2.3) (no change)
+- [`ws@~8.2.3`](https://github.com/websockets/ws/releases/tag/8.2.3) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.0.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.0.md
new file mode 100644
index 00000000..1dcf54be
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.0.md
@@ -0,0 +1,285 @@
+---
+title: Version 4.6.0
+sidebar_label: 4.6.0 (February 7, 2023)
+sidebar_position: 101
+toc_max_heading_level: 4
+---
+
+*February 7, 2023*
+
+## Server
+
+### Bug Fixes
+
+* add timeout method to remote socket ([#4558](https://github.com/socketio/socket.io/issues/4558)) ([0c0eb00](https://github.com/socketio/socket.io/commit/0c0eb0016317218c2be3641e706cfaa9bea39a2d))
+* **typings:** properly type emits with timeout ([f3ada7d](https://github.com/socketio/socket.io/commit/f3ada7d8ccc02eeced2b9b9ac8e4bc921eb630d2))
+
+
+### Features
+
+#### Promise-based acknowledgements
+
+This commit adds some syntactic sugar around acknowledgements:
+
+- `emitWithAck()`
+
+```js
+try {
+ const responses = await io.timeout(1000).emitWithAck("some-event");
+ console.log(responses); // one response per client
+} catch (e) {
+ // some clients did not acknowledge the event in the given delay
+}
+
+io.on("connection", async (socket) => {
+ // without timeout
+ const response = await socket.emitWithAck("hello", "world");
+
+ // with a specific timeout
+ try {
+ const response = await socket.timeout(1000).emitWithAck("hello", "world");
+ } catch (err) {
+ // the client did not acknowledge the event in the given delay
+ }
+});
+```
+
+- `serverSideEmitWithAck()`
+
+```js
+try {
+ const responses = await io.timeout(1000).serverSideEmitWithAck("some-event");
+ console.log(responses); // one response per server (except itself)
+} catch (e) {
+ // some servers did not acknowledge the event in the given delay
+}
+```
+
+Added in [184f3cf](https://github.com/socketio/socket.io/commit/184f3cf7af57acc4b0948eee307f25f8536eb6c8).
+
+#### Connection state recovery
+
+This feature allows a client to reconnect after a temporary disconnection and restore its state:
+
+- id
+- rooms
+- data
+- missed packets
+
+Usage:
+
+```js
+import { Server } from "socket.io";
+
+const io = new Server({
+ connectionStateRecovery: {
+ // default values
+ maxDisconnectionDuration: 2 * 60 * 1000,
+ skipMiddlewares: true,
+ },
+});
+
+io.on("connection", (socket) => {
+ console.log(socket.recovered); // whether the state was recovered or not
+});
+```
+
+Here's how it works:
+
+- the server sends a session ID during the handshake (which is different from the current `id` attribute, which is public and can be freely shared)
+- the server also includes an offset in each packet (added at the end of the data array, for backward compatibility)
+- upon temporary disconnection, the server stores the client state for a given delay (implemented at the adapter level)
+- upon reconnection, the client sends both the session ID and the last offset it has processed, and the server tries to restore the state
+
+The in-memory adapter already supports this feature, and we will soon update the Postgres and MongoDB adapters. We will also create a new adapter based on [Redis Streams](https://redis.io/docs/data-types/streams/), which will support this feature.
+
+Added in [54d5ee0](https://github.com/socketio/socket.io/commit/54d5ee05a684371191e207b8089f09fc24eb5107).
+
+#### Compatibility (for real) with Express middlewares
+
+This feature implements middlewares at the Engine.IO level, because Socket.IO middlewares are meant for namespace authorization and are not executed during a classic HTTP request/response cycle.
+
+Syntax:
+
+```js
+io.engine.use((req, res, next) => {
+ // do something
+
+ next();
+});
+
+// with express-session
+import session from "express-session";
+
+io.engine.use(session({
+ secret: "keyboard cat",
+ resave: false,
+ saveUninitialized: true,
+ cookie: { secure: true }
+}));
+
+// with helmet
+import helmet from "helmet";
+
+io.engine.use(helmet());
+```
+
+A workaround was possible by using the allowRequest option and the "headers" event, but this feels way cleaner and works with upgrade requests too.
+
+Added in [24786e7](https://github.com/socketio/engine.io/commit/24786e77c5403b1c4b5a2bc84e2af06f9187f74a).
+
+#### Error details in the disconnecting and disconnect events
+
+The `disconnect` event will now contain additional details about the disconnection reason.
+
+```js
+io.on("connection", (socket) => {
+ socket.on("disconnect", (reason, description) => {
+ console.log(description);
+ });
+});
+```
+
+Added in [8aa9499](https://github.com/socketio/socket.io/commit/8aa94991cee5518567d6254eec04b23f81510257).
+
+#### Automatic removal of empty child namespaces
+
+This commit adds a new option, "cleanupEmptyChildNamespaces". With this option enabled (disabled by default), when a socket disconnects from a dynamic namespace and if there are no other sockets connected to it then the namespace will be cleaned up and its adapter will be closed.
+
+```js
+import { createServer } from "node:http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ cleanupEmptyChildNamespaces: true
+});
+```
+
+Added in [5d9220b](https://github.com/socketio/socket.io/commit/5d9220b69adf73e086c27bbb63a4976b348f7c4c).
+
+#### A new "addTrailingSlash" option
+
+The trailing slash which was added by default can now be disabled:
+
+```js
+import { createServer } from "node:http";
+import { Server } from "socket.io";
+
+const httpServer = createServer();
+const io = new Server(httpServer, {
+ addTrailingSlash: false
+});
+```
+
+In the example above, the clients can omit the trailing slash and use `/socket.io` instead of `/socket.io/`.
+
+Added in [d0fd474](https://github.com/socketio/engine.io/commit/d0fd4746afa396297f07bb62e539b0c1c4018d7c).
+
+### Performance Improvements
+
+* precompute the WebSocket frames when broadcasting ([da2b542](https://github.com/socketio/socket.io/commit/da2b54279749adc5279c9ac4742b01b36c01cff0))
+
+
+### Dependencies
+
+- [`engine.io@~6.4.0`](https://github.com/socketio/engine.io/releases/tag/6.4.0) (https://github.com/socketio/engine.io/compare/6.2.1...6.4.0)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (https://github.com/websockets/ws/compare/8.2.3...8.11.0)
+
+
+
+## Client
+
+### Bug Fixes
+
+* **typings:** do not expose browser-specific types ([4d6d95e](https://github.com/socketio/socket.io-client/commit/4d6d95e0792efd43b78c760b055764fef02ebc9e))
+* ensure manager.socket() returns an active socket ([b7dd891](https://github.com/socketio/socket.io-client/commit/b7dd891e890461d33a104ca9187d5cd30d6f76af))
+* **typings:** properly type emits with timeout ([#1570](https://github.com/socketio/socket.io-client/issues/1570)) ([33e4172](https://github.com/socketio/socket.io-client/commit/33e417258c9a5697e001163971ae87821e9c097f))
+
+
+### Features
+
+#### A new "addTrailingSlash" option
+
+The trailing slash which was added by default can now be disabled:
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io("https://example.com", {
+ addTrailingSlash: false
+});
+```
+
+In the example above, the request URL will be `https://example.com/socket.io` instead of `https://example.com/socket.io/`.
+
+Added in [21a6e12](https://github.com/socketio/engine.io-client/commit/21a6e1219add92157c5442537d24fbe1129a50f5).
+
+#### Promise-based acknowledgements
+
+This commit adds some syntactic sugar around acknowledgements:
+
+```js
+// without timeout
+const response = await socket.emitWithAck("hello", "world");
+
+// with a specific timeout
+try {
+ const response = await socket.timeout(1000).emitWithAck("hello", "world");
+} catch (err) {
+ // the server did not acknowledge the event in the given delay
+}
+```
+
+Note: environments that [do not support Promises](https://caniuse.com/promises) will need to add a polyfill in order to use this feature.
+
+Added in [47b979d](https://github.com/socketio/socket.io-client/commit/47b979d57388e9b5e9a332f3f4a9873211f0d844).
+
+#### Connection state recovery
+
+This feature allows a client to reconnect after a temporary disconnection and restore its ID and receive any packets that was missed during the disconnection gap. It must be enabled on the server side.
+
+A new boolean attribute named `recovered` is added on the `socket` object:
+
+```js
+socket.on("connect", () => {
+ console.log(socket.recovered); // whether the recovery was successful
+});
+```
+
+Added in [54d5ee0](https://github.com/socketio/socket.io/commit/54d5ee05a684371191e207b8089f09fc24eb5107) (server) and [b4e20c5](https://github.com/socketio/socket.io-client/commit/b4e20c5c709b5e9cc03ee9b6bd1d576f4810a817) (client).
+
+#### Retry mechanism
+
+Two new options are available:
+
+- `retries`: the maximum number of retries. Above the limit, the packet will be discarded.
+- `ackTimeout`: the default timeout in milliseconds used when waiting for an acknowledgement (not to be mixed up with the already existing `timeout` option, which is used by the Manager during the connection)
+
+```js
+const socket = io({
+ retries: 3,
+ ackTimeout: 10000
+});
+
+// implicit ack
+socket.emit("my-event");
+
+// explicit ack
+socket.emit("my-event", (err, val) => { /* ... */ });
+
+// custom timeout (in that case the ackTimeout is optional)
+socket.timeout(5000).emit("my-event", (err, val) => { /* ... */ });
+```
+
+In all examples above, "my-event" will be sent up to 4 times (1 + 3), until the server sends an acknowledgement.
+
+Assigning a unique ID to each packet is the duty of the user, in order to allow deduplication on the server side.
+
+Added in [655dce9](https://github.com/socketio/socket.io-client/commit/655dce97556a1ea44a60db6b694d0cfd85b5f70f).
+
+
+### Dependencies
+
+- [`engine.io-client@~6.4.0`](https://github.com/socketio/engine.io-client/releases/tag/6.4.0) ([diff](https://github.com/socketio/engine.io-client/compare/6.2.3...6.4.0))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) ([diff](https://github.com/websockets/ws/compare/8.2.3...8.11.0))
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.1.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.1.md
new file mode 100644
index 00000000..49b21aaf
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.1.md
@@ -0,0 +1,36 @@
+---
+title: Version 4.6.1
+sidebar_label: 4.6.1 (February 20, 2023)
+sidebar_position: 100
+toc_max_heading_level: 4
+---
+
+*February 20, 2023*
+
+## Server
+
+### Bug Fixes
+
+* properly handle manually created dynamic namespaces ([0d0a7a2](https://github.com/socketio/socket.io/commit/0d0a7a22b5ff95f864216c529114b7dd41738d1e))
+* **types:** fix nodenext module resolution compatibility ([#4625](https://github.com/socketio/socket.io/issues/4625)) ([d0b22c6](https://github.com/socketio/socket.io/commit/d0b22c630208669aceb7ae013180c99ef90279b0))
+
+
+### Dependencies
+
+- [`engine.io@~6.4.0`](https://github.com/socketio/engine.io/releases/tag/6.4.0) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* do not drain the queue while the socket is offline ([4996f9e](https://github.com/socketio/socket.io-client/commit/4996f9ee71074e2d62a0f8fa95fcf7d43e99615d))
+* prevent duplicate connections when multiplexing ([46213a6](https://github.com/socketio/socket.io-client/commit/46213a647ea0d4453b00bca09268f69ffd259509))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.4.0`](https://github.com/socketio/engine.io-client/releases/tag/6.4.0) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.2.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.2.md
new file mode 100644
index 00000000..f87a7f77
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.6.2.md
@@ -0,0 +1,34 @@
+---
+title: Version 4.6.2
+sidebar_label: 4.6.2 (May 31, 2023)
+sidebar_position: 99
+toc_max_heading_level: 4
+---
+
+*May 31, 2023*
+
+## Server
+
+### Bug Fixes
+
+* **exports:** move `types` condition to the top ([#4698](https://github.com/socketio/socket.io/issues/4698)) ([3d44aae](https://github.com/socketio/socket.io/commit/3d44aae381af38349fdb808d510d9f47a0c2507e))
+
+
+### Dependencies
+
+- [`engine.io@~6.4.2`](https://github.com/socketio/engine.io/releases/tag/6.4.0) ([diff](https://github.com/socketio/engine.io/compare/6.4.1...6.4.2))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* **exports:** move `types` condition to the top ([#1580](https://github.com/socketio/socket.io-client/issues/1580)) ([7ead241](https://github.com/socketio/socket.io-client/commit/7ead241ecfd9f122db6789b5f2d11c04e9427953))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.4.0`](https://github.com/socketio/engine.io-client/releases/tag/6.4.0) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.0.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.0.md
new file mode 100644
index 00000000..f092abc8
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.0.md
@@ -0,0 +1,172 @@
+---
+title: Version 4.7.0
+sidebar_label: 4.7.0 (June 22, 2023)
+sidebar_position: 98
+toc_max_heading_level: 4
+---
+
+*June 22, 2023*
+
+## Server
+
+### Bug Fixes
+
+* remove the Partial modifier from the socket.data type ([#4740](https://github.com/socketio/socket.io/issues/4740)) ([e5c62ca](https://github.com/socketio/socket.io/commit/e5c62cad60fc7d16fbb024fd9be1d1880f4e6f5f))
+
+
+### Features
+
+#### Support for WebTransport
+
+The Socket.IO server can now use WebTransport as the underlying transport.
+
+WebTransport is a web API that uses the HTTP/3 protocol as a bidirectional transport. It's intended for two-way communications between a web client and an HTTP/3 server.
+
+References:
+
+- https://w3c.github.io/webtransport/
+- https://developer.mozilla.org/en-US/docs/Web/API/WebTransport
+- https://developer.chrome.com/articles/webtransport/
+
+Until WebTransport support lands [in Node.js](https://github.com/nodejs/node/issues/38478), you can use the `@fails-components/webtransport` package:
+
+```js
+import { readFileSync } from "fs";
+import { createServer } from "https";
+import { Server } from "socket.io";
+import { Http3Server } from "@fails-components/webtransport";
+
+// WARNING: the total length of the validity period MUST NOT exceed two weeks (https://w3c.github.io/webtransport/#custom-certificate-requirements)
+const cert = readFileSync("/path/to/my/cert.pem");
+const key = readFileSync("/path/to/my/key.pem");
+
+const httpsServer = createServer({
+ key,
+ cert
+});
+
+httpsServer.listen(3000);
+
+const io = new Server(httpsServer, {
+ transports: ["polling", "websocket", "webtransport"] // WebTransport is not enabled by default
+});
+
+const h3Server = new Http3Server({
+ port: 3000,
+ host: "0.0.0.0",
+ secret: "changeit",
+ cert,
+ privKey: key,
+});
+
+(async () => {
+ const stream = await h3Server.sessionStream("/engine.io/");
+ const sessionReader = stream.getReader();
+
+ while (true) {
+ const { done, value } = await sessionReader.read();
+ if (done) {
+ break;
+ }
+ io.engine.onWebTransportSession(value);
+ }
+})();
+
+h3Server.startServer();
+```
+
+Added in [123b68c](https://github.com/socketio/engine.io/commit/123b68c04f9e971f59b526e0f967a488ee6b0116).
+
+
+#### Client bundles with CORS headers
+
+The bundles will now have the right `Access-Control-Allow-xxx` headers.
+
+Added in [63f181c](https://github.com/socketio/socket.io/commit/63f181cc12cbbbf94ed40eef52d60f36a1214fbe).
+
+
+### Dependencies
+
+- [`engine.io@~6.5.0`](https://github.com/socketio/engine.io/releases/tag/6.5.0) ([diff](https://github.com/socketio/engine.io/compare/6.4.2...6.5.0))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* properly report timeout error when connecting ([5bc94b5](https://github.com/socketio/socket.io-client/commit/5bc94b56bc1788bab16d9d514d2c8abf3b1d8f87))
+* use same scope for setTimeout and clearTimeout calls ([#1568](https://github.com/socketio/socket.io-client/issues/1568)) ([f2892ab](https://github.com/socketio/socket.io-client/commit/f2892aba0beeae7c9be930221655d7da6094c5f1))
+
+
+### Features
+
+#### Support for WebTransport
+
+The Socket.IO client can now use WebTransport as the underlying transport.
+
+WebTransport is a web API that uses the HTTP/3 protocol as a bidirectional transport. It's intended for two-way communications between a web client and an HTTP/3 server.
+
+References:
+
+- https://w3c.github.io/webtransport/
+- https://developer.mozilla.org/en-US/docs/Web/API/WebTransport
+- https://developer.chrome.com/articles/webtransport/
+
+**For Node.js clients**: until WebTransport support lands [in Node.js](https://github.com/nodejs/node/issues/38478), you can use the `@fails-components/webtransport` package:
+
+```js
+import { WebTransport } from "@fails-components/webtransport";
+
+global.WebTransport = WebTransport;
+```
+
+Added in [7195c0f](https://github.com/socketio/engine.io-client/commit/7195c0f305b482f7b1ca2ed812030caaf72c0906).
+
+#### Cookie management for the Node.js client
+
+When setting the `withCredentials` option to `true`, the Node.js client will now include the cookies in the HTTP requests, making it easier to use it with cookie-based sticky sessions.
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io("https://example.com", {
+ withCredentials: true
+});
+```
+
+Added in [5fc88a6](https://github.com/socketio/engine.io-client/commit/5fc88a62d4017cdc144fa39b9755deadfff2db34).
+
+#### Conditional import of the ESM build with debug logs
+
+By default, the ESM build does not include the `debug` package in the browser environments, because it increases the bundle size (see [16b6569](https://github.com/socketio/socket.io-client/commit/16b65698aed766e1e645c78847f2e91bfc5b6f56)).
+
+Which means that, unfortunately, debug logs are not available in the devtools console, even when setting the `localStorage.debug = ...` attribute.
+
+You can now import the build which includes the `debug` packages with a [conditional import](https://nodejs.org/api/packages.html#conditional-exports). Example with vite:
+
+```js
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+ server: {
+ port: 4000
+ },
+ resolve: {
+ conditions: ["development"]
+ }
+})
+```
+
+Reference: https://v2.vitejs.dev/config/#resolve-conditions
+
+Added in [781d753](https://github.com/socketio/socket.io-client/commit/781d753a626d01e675056a2ff4e27f5dd599564f).
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.0`](https://github.com/socketio/engine.io-client/releases/tag/6.5.0) ([diff](https://github.com/socketio/engine.io-client/compare/6.4.0...6.5.0))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.1.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.1.md
new file mode 100644
index 00000000..4231f153
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.1.md
@@ -0,0 +1,33 @@
+---
+title: Version 4.7.1
+sidebar_label: 4.7.1 (June 28, 2023)
+sidebar_position: 97
+toc_max_heading_level: 4
+---
+
+*June 28, 2023*
+
+## Server
+
+The client bundle contains a few fixes regarding the WebTransport support.
+
+
+### Dependencies
+
+- [`engine.io@~6.5.0`](https://github.com/socketio/engine.io/releases/tag/6.5.0) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+Some bug fixes are included from the `engine.io-client` package:
+
+* make closeOnBeforeunload default to false ([a63066b](https://github.com/socketio/engine.io-client/commit/a63066bdc8ae9e6746c3113d06c2ead78f4a4851))
+* **webtransport:** properly handle abruptly closed connections ([cf6aa1f](https://github.com/socketio/engine.io-client/commit/cf6aa1f43c27a56c076bf26fddfce74bfeb65040))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.1) ([diff](https://github.com/socketio/engine.io-client/compare/6.5.0...6.5.1))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.2.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.2.md
new file mode 100644
index 00000000..53a2d2f9
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.2.md
@@ -0,0 +1,39 @@
+---
+title: Version 4.7.2
+sidebar_label: 4.7.2 (August 2, 2023)
+sidebar_position: 96
+toc_max_heading_level: 4
+---
+
+*August 2, 2023*
+
+## Server
+
+### Bug Fixes
+
+* clean up child namespace when client is rejected in middleware ([#4773](https://github.com/socketio/socket.io/issues/4773)) ([0731c0d](https://github.com/socketio/socket.io/commit/0731c0d2f497d5cce596ea1ec32a67c08bcccbcd))
+* **webtransport:** properly handle WebTransport-only connections ([3468a19](https://github.com/socketio/socket.io/commit/3468a197afe87e65eb0d779fabd347fe683013ab))
+* **webtransport:** add proper framing ([a306db0](https://github.com/socketio/engine.io/commit/a306db09e8ddb367c7d62f45fec920f979580b7c))
+
+
+### Dependencies
+
+- [`engine.io@~6.5.2`](https://github.com/socketio/engine.io/releases/tag/6.5.2) ([diff](https://github.com/socketio/engine.io/compare/6.5.0...6.5.2))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+Some bug fixes are included from the `engine.io-client` package:
+
+* **webtransport:** add proper framing ([d55c39e](https://github.com/socketio/engine.io-client/commit/d55c39e0ed5cb7b3a34875a398efc111c91184f6))
+* **webtransport:** honor the binaryType attribute ([8270e00](https://github.com/socketio/engine.io-client/commit/8270e00d5b865278d136a4d349b344cbc2b38dc5))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.2`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) ([diff](https://github.com/socketio/engine.io-client/compare/6.5.1...6.5.2))
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.3.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.3.md
new file mode 100644
index 00000000..3efcbec0
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.3.md
@@ -0,0 +1,37 @@
+---
+title: Version 4.7.3
+sidebar_label: 4.7.3 (January 3, 2024)
+sidebar_position: 95
+toc_max_heading_level: 4
+---
+
+*January 3, 2024*
+
+## Server
+
+### Bug Fixes
+
+* return the first response when broadcasting to a single socket ([#4878](https://github.com/socketio/socket.io/issues/4878)) ([df8e70f](https://github.com/socketio/socket.io/commit/df8e70f79822e3887b4f21ca718af8a53bbda2c4))
+* **typings:** allow to bind to a non-secure Http2Server ([#4853](https://github.com/socketio/socket.io/issues/4853)) ([8c9ebc3](https://github.com/socketio/socket.io/commit/8c9ebc30e5452ff9381af5d79f547394fa55633c))
+
+
+### Dependencies
+
+- [`engine.io@~6.5.2`](https://github.com/socketio/engine.io/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* improve compatibility with node16 module resolution ([#1595](https://github.com/socketio/socket.io-client/issues/1595)) ([605de78](https://github.com/socketio/socket.io-client/commit/605de78d2cd7303bf25d9e2146e2b707dbf63d4f))
+* **typings:** accept string | undefined as init argument ([5a3eafe](https://github.com/socketio/socket.io-client/commit/5a3eafed1c4118ac3a06ec81a24491eec7d0655f))
+* **typings:** fix the type of the socket#id attribute ([f9c16f2](https://github.com/socketio/socket.io-client/commit/f9c16f226512fc8a8df461e3a07e392720462165))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.2`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.4.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.4.md
new file mode 100644
index 00000000..8d7f4a51
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.4.md
@@ -0,0 +1,32 @@
+---
+title: Version 4.7.4
+sidebar_label: 4.7.4 (January 12, 2024)
+sidebar_position: 94
+toc_max_heading_level: 4
+---
+
+*January 12, 2024*
+
+## Server
+
+### Bug Fixes
+
+* **typings:** calling io.emit with no arguments incorrectly errored ([cb6d2e0](https://github.com/socketio/socket.io/commit/cb6d2e02aa7ec03c2de1817d35cffa1128b107ef)), closes [#4914](https://github.com/socketio/socket.io/issues/4914)
+
+
+### Dependencies
+
+- [`engine.io@~6.5.2`](https://github.com/socketio/engine.io/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+There were some minor bug fixes on the server side, which mandate a client bump.
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.2`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.5.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.5.md
new file mode 100644
index 00000000..dcb844d3
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.7.5.md
@@ -0,0 +1,35 @@
+---
+title: Version 4.7.5
+sidebar_label: 4.7.5 (March 14, 2024)
+sidebar_position: 93
+toc_max_heading_level: 4
+---
+
+*March 14, 2024*
+
+## Server
+
+### Bug Fixes
+
+* close the adapters when the server is closed ([bf64870](https://github.com/socketio/socket.io/commit/bf64870957e626a73e0544716a1a41a4ba5093bb))
+* remove duplicate pipeline when serving bundle ([e426f3e](https://github.com/socketio/socket.io/commit/e426f3e8e1bfea5720c32d30a3663303200ee6ad))
+
+
+### Dependencies
+
+- [`engine.io@~6.5.2`](https://github.com/socketio/engine.io/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* discard acknowledgements upon disconnection ([34cbfbb](https://github.com/socketio/socket.io-client/commit/34cbfbb532ae333f4dd034138e8f87cb80a8e382))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.5.2`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
+- [`ws@~8.11.0`](https://github.com/websockets/ws/releases/tag/8.11.0) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.0.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.0.md
new file mode 100644
index 00000000..d5062211
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.0.md
@@ -0,0 +1,104 @@
+---
+title: Version 4.8.0
+sidebar_label: 4.8.0 (September 21, 2024)
+sidebar_position: 92
+toc_max_heading_level: 4
+---
+
+*September 21, 2024*
+
+## Server
+
+### Bug Fixes
+
+* allow to join a room in a middleware (uws) ([b04fa64](https://github.com/socketio/socket.io/commit/b04fa64365729244a9c50a6b54b12e9bcc9e55d0))
+* correctly await async close on adapters ([#4971](https://github.com/socketio/socket.io/issues/4971)) ([e347a3c](https://github.com/socketio/socket.io/commit/e347a3c24e773cf59f589110989fd56703a9057c))
+* expose type of default engine ([132d05f](https://github.com/socketio/socket.io/commit/132d05fc0b319df7eb1b3010a91adc7d5ae58ef2))
+
+
+### Dependencies
+
+- [`engine.io@~6.6.0`](https://github.com/socketio/engine.io/releases/tag/6.5.2) ([diff](https://github.com/socketio/engine.io/compare/6.5.2...6.6.0) and [diff](https://github.com/socketio/socket.io/compare/engine.io@6.6.0...engine.io@6.6.1))
+- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) ([diff](https://github.com/websockets/ws/compare/8.11.0...8.17.1))
+
+
+
+## Client
+
+### Features
+
+#### Custom transport implementations
+
+The `transports` option now accepts an array of transport implementations:
+
+```js
+import { io } from "socket.io-client";
+import { Fetch, WebSocket } from "engine.io-client";
+
+const socket = io({
+ transports: [Fetch, WebSocket]
+});
+```
+
+Here is the list of provided implementations:
+
+| Transport | Description |
+|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `Fetch` | HTTP long-polling based on the built-in [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) method. |
+| `NodeXHR` | HTTP long-polling based on the `XMLHttpRequest` object provided by the [`xmlhttprequest-ssl`](https://www.npmjs.com/package/xmlhttprequest-ssl) package. |
+| `XHR` | HTTP long-polling based on the built-in [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) object. |
+| `NodeWebSocket` | WebSocket transport based on the `WebSocket` object provided by the [`ws`](https://www.npmjs.com/package/ws) package. |
+| `WebSocket` | WebSocket transport based on the built-in [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) object. |
+| `WebTransport` | WebTransport transport based on the built-in [`WebTransport`](https://developer.mozilla.org/en-US/docs/Web/API/WebTransport) object. |
+
+Usage:
+
+| Transport | browser | Node.js | Deno | Bun |
+|-----------------|--------------------|------------------------|--------------------|--------------------|
+| `Fetch` | :white_check_mark: | :white_check_mark: (1) | :white_check_mark: | :white_check_mark: |
+| `NodeXHR` | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| `XHR` | :white_check_mark: | | | |
+| `NodeWebSocket` | | :white_check_mark: | :white_check_mark: | :white_check_mark: |
+| `WebSocket` | :white_check_mark: | :white_check_mark: (2) | :white_check_mark: | :white_check_mark: |
+| `WebTransport` | :white_check_mark: | :white_check_mark: | | |
+
+(1) since [v18.0.0](https://nodejs.org/api/globals.html#fetch)
+(2) since [v21.0.0](https://nodejs.org/api/globals.html#websocket)
+
+Added in [f4d898e](https://github.com/socketio/engine.io-client/commit/f4d898ee9652939a4550a41ac0e8143056154c0a) and [b11763b](https://github.com/socketio/engine.io-client/commit/b11763beecfe4622867b4dec9d1db77460733ffb).
+
+
+#### Test each low-level transports
+
+When setting the `tryAllTransports` option to `true`, if the first transport (usually, HTTP long-polling) fails, then the other transports will be tested too:
+
+```js
+import { io } from "socket.io-client";
+
+const socket = io({
+ tryAllTransports: true
+});
+```
+
+This feature is useful in two cases:
+
+- when HTTP long-polling is disabled on the server, or if CORS fails
+- when WebSocket is tested first (with `transports: ["websocket", "polling"]`)
+
+The only potential downside is that the connection attempt could take more time in case of failure, as there have been reports of WebSocket connection errors taking several seconds before being detected (that's one reason for using HTTP long-polling first). That's why the option defaults to `false` for now.
+
+Added in [579b243](https://github.com/socketio/engine.io-client/commit/579b243e89ac7dc58233f9844ef70817364ecf52).
+
+
+### Bug Fixes
+
+* accept string | undefined as init argument (bis) ([60c757f](https://github.com/socketio/socket.io/commit/60c757f718d400e052c3160ee377bbe4973277c9))
+* allow to manually stop the reconnection loop ([13c6d2e](https://github.com/socketio/socket.io/commit/13c6d2e89deb1e6c6c8c7245118f9b37d66537cb))
+* close the engine upon decoding exception ([04c8dd9](https://github.com/socketio/socket.io/commit/04c8dd979ce40acaceec1f4507c1ae69325d6158))
+* do not send a packet on an expired connection ([#5134](https://github.com/socketio/socket.io/issues/5134)) ([8adcfbf](https://github.com/socketio/socket.io/commit/8adcfbfde50679095ec2abe376650cf2b6814325))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.6.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) ([diff](https://github.com/socketio/engine.io-client/compare/6.5.3...6.6.0) and [diff](https://github.com/socketio/socket.io/compare/engine.io-client@6.6.0...engine.io-client@6.6.1))
+- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) ([diff](https://github.com/websockets/ws/compare/8.11.0...8.17.1))
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.1.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.1.md
new file mode 100644
index 00000000..6188a33e
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/4.8.1.md
@@ -0,0 +1,31 @@
+---
+title: Version 4.8.1
+sidebar_label: 4.8.1 (October 25, 2024)
+sidebar_position: 91
+toc_max_heading_level: 4
+---
+
+*October 25, 2024*
+
+## Server
+
+Due to a change in the bundler configuration, the production bundle (`socket.io.min.js`) did not support sending and receiving binary data in version `4.8.0`. This is now fixed.
+
+### Dependencies
+
+- [`engine.io@~6.6.0`](https://github.com/socketio/engine.io/releases/tag/6.5.2) (no change)
+- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
+
+
+
+## Client
+
+### Bug Fixes
+
+* **bundle:** do not mangle the "_placeholder" attribute ([ca9e994](https://github.com/socketio/socket.io/commit/ca9e994815aa2e31e0342e37ccdc2e9e8c5fd13c))
+
+
+### Dependencies
+
+- [`engine.io-client@~6.6.1`](https://github.com/socketio/engine.io-client/releases/tag/6.5.2) (no change)
+- [`ws@~8.17.1`](https://github.com/websockets/ws/releases/tag/8.17.1) (no change)
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/changelog/index.md b/i18n/es/docusaurus-plugin-content-docs/current/changelog/index.md
new file mode 100644
index 00000000..6238e8da
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/changelog/index.md
@@ -0,0 +1,71 @@
+---
+title: Changelog
+sidebar_position: 1
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+## Política de versionado
+
+Los lanzamientos de Socket.IO siguen de cerca el [Versionado Semántico](https://semver.org/).
+
+Esto significa que con un número de versión `x.y.z`:
+
+- cuando lanzamos correcciones de errores críticos, hacemos un lanzamiento de parche incrementando el número `z` (ej: `1.2.3` a `1.2.4`).
+- cuando lanzamos nuevas características o correcciones no críticas, hacemos un lanzamiento menor incrementando el número `y` (ej: `1.2.3` a `1.3.0`).
+- cuando lanzamos cambios incompatibles, hacemos un lanzamiento mayor incrementando el número `x` (ej: `1.2.3` a `2.0.0`).
+
+## Cambios incompatibles
+
+Los cambios incompatibles son inconvenientes para todos, así que intentamos minimizar el número de lanzamientos mayores.
+
+Hemos tenido dos cambios incompatibles importantes que impactaron el protocolo Socket.IO a lo largo de los años:
+
+- Socket.IO v2 fue lanzado en **mayo 2017**
+- Socket.IO v3 fue lanzado en **noviembre 2020**
+
+:::info
+
+Socket.IO v4 (lanzado en marzo 2021) no incluyó ninguna actualización al protocolo Socket.IO (solo un par de cambios incompatibles en la API del servidor Node.js), así que no se cuenta aquí.
+
+Referencia: [Migrando de 3.x a 4.0](../categories/07-Migrations/migrating-from-3-to-4.md)
+
+:::
+
+## Hitos importantes
+
+Además de los cambios incompatibles listados arriba, aquí están los últimos cambios importantes en Socket.IO:
+
+| Versión | Fecha | Descripción |
+|---------------------|----------------|----------------------------------------------------------------------------------------------------------|
+| [`4.7.0`](4.7.0.md) | Junio 2023 | Soporte para WebTransport |
+| [`4.6.0`](4.6.0.md) | Febrero 2023 | Introducción de [Recuperación del estado de conexión](../categories/01-Documentation/connection-state-recovery.md) |
+| `4.4.0` | Noviembre 2021 | Soporte para [uWebSockets.js](../categories/02-Server/server-installation.md#usage-with-uwebsockets) |
+| `4.1.0` | Mayo 2021 | Introducción de [`serverSideEmit()`](../categories/02-Server/server-instance.md#serversideemit) |
+| `4.0.0` | Marzo 2021 | Reescritura a [TypeScript](https://www.typescriptlang.org/) |
+
+## Uso por versión
+
+A partir de junio 2024:
+
+Paquete `socket.io`
+
+
+
+
+Paquete `socket.io-client`
+
+
diff --git a/i18n/es/docusaurus-plugin-content-docs/current/client-api.md b/i18n/es/docusaurus-plugin-content-docs/current/client-api.md
new file mode 100644
index 00000000..acc733af
--- /dev/null
+++ b/i18n/es/docusaurus-plugin-content-docs/current/client-api.md
@@ -0,0 +1,1020 @@
+---
+title: Client API
+sidebar_label: API
+sidebar_position: 1
+slug: /client-api/
+toc_max_heading_level: 4
+---
+
+import ThemedImage from '@theme/ThemedImage';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+## IO
+
+The `io` method is bound to the global scope in the standalone build:
+
+```html
+
+
+```
+
+An ESM bundle is also available since version [4.3.0](/blog/socket-io-4-3-0/):
+
+```html
+
+```
+
+With an [import map](https://caniuse.com/import-maps):
+
+```html
+
+
+
+```
+
+Else, in all other cases (with some build tools, in Node.js or React Native), it can be imported from the `socket.io-client` package:
+
+```js
+// ES modules
+import { io } from "socket.io-client";
+
+// CommonJS
+const { io } = require("socket.io-client");
+```
+
+### io.protocol
+
+ * [``](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type)
+
+The protocol revision number (currently: 5).
+
+The protocol defines the format of the packets exchanged between the client and the server. Both the client and the server must use the same revision in order to understand each other.
+
+You can find more information [here](https://github.com/socketio/socket.io-protocol).
+
+### io([url][, options])
+
+ - `url` [``](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#string_type) (defaults to `window.location.host`)
+ - `options` [`