docs(arts): A2 ES->EN — connection (full translation)

Full Spanish -> English translation of connection/README.md (faithful; code
blocks, event/error constants and state diagrams kept verbatim). Carries the A1
fixes (required `{ timers }`, the 13 ActiveConnections getters, `/active/docs/conn`
demo, the no-own-scheduler paragraph).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
menubar-v4-safe
dev 3 months ago
parent 313615cee3
commit dd1abac629

@ -1,31 +1,31 @@
# connection
`connection` es el artefacto de conexiones realtime. No es "un wrapper de
WebSocket": es un registro de conexiones con transporte intercambiable,
reconexión, heartbeat, request/reply, canales, integración opcional con sesión
y diagnósticos estructurados.
`connection` is the realtime connections artifact. It is not "a WebSocket
wrapper": it is a connection registry with a swappable transport, reconnection,
heartbeat, request/reply, channels, optional session integration and structured
diagnostics.
La regla de nombres es importante:
The naming rule matters:
| Concepto | Nombre |
| ----------------------------------- | ------------------------------------------------- |
| Raíz imperativa | `createEngineConnections()` / `EngineConnections` |
| Raíz reactiva | `createActiveConnections()` / `ActiveConnections` |
| Unidad individual | `Connection` |
| Topic lógico dentro de una conexión | `ConnectionChannel` |
| Concept | Name |
| --------------------------------- | ------------------------------------------------- |
| Imperative root | `createEngineConnections()` / `EngineConnections` |
| Reactive root | `createActiveConnections()` / `ActiveConnections` |
| Individual unit | `Connection` |
| Logical topic within a connection | `ConnectionChannel` |
No existe `EngineConnection` ni `ActiveConnection`. `Engine*` y `Active*`
quedan reservados para raíces de artefacto; una conexión individual no es una
raíz, es una entidad gestionada por `EngineConnections`.
There is no `EngineConnection` or `ActiveConnection`. `Engine*` and `Active*`
are reserved for artifact roots; an individual connection is not a root, it is
an entity managed by `EngineConnections`.
## Uso Mínimo
## Minimal Use
```ts
import { createEngineConnections, createWebSocketTransport } from '$connection';
import { createEngineTimers } from '$timer';
// `timers` es OBLIGATORIO: connection no construye su propio scheduler.
// En composición se pasa `App.timers`; standalone, `createEngineTimers()`.
// `timers` is REQUIRED: connection does not build its own scheduler.
// In composition you pass `App.timers`; standalone, `createEngineTimers()`.
const Connections = createEngineConnections({ timers: createEngineTimers() });
const Main = Connections.createConnection('main', {
@ -36,7 +36,7 @@ const Main = Connections.createConnection('main', {
await Main.connect();
```
La raíz mantiene el mapa de conexiones:
The root keeps the connection map:
```ts
Connections.names();
@ -47,22 +47,21 @@ await Connections.reconnectAll('network-restored');
Connections.dispose();
```
`dispose()` de la raíz cierra conexiones, cancela timers propios y limpia los
listeners. Después de `dispose()`, las operaciones públicas lanzan errores
tipados `Conn*`.
The root's `dispose()` closes connections, cancels its own timers and clears the
listeners. After `dispose()`, public operations throw typed `Conn*` errors.
## ActiveConnections
La capa activa añade estado derivado para UI:
The active layer adds derived state for the UI:
```ts
// `timers` también es obligatorio aquí (se reenvía al engine interno).
// `timers` is also required here (it is forwarded to the internal engine).
const Connections = createActiveConnections({ timers: createEngineTimers() });
Connections.size;
Connections.states; // Record<name, ConnectionState>
// Nombres agrupados por estado (reactivos):
// Names grouped by state (reactive):
Connections.activeNames;
Connections.connectedNames;
Connections.connectingNames;
@ -70,7 +69,7 @@ Connections.reconnectingNames;
Connections.failedNames;
Connections.closedNames;
// Booleanos derivados:
// Derived booleans:
Connections.allConnected;
Connections.anyConnected;
Connections.anyConnecting;
@ -78,13 +77,13 @@ Connections.anyReconnecting;
Connections.anyFailed;
```
`ActiveConnections` conserva la misma API de creación/consulta que el engine,
pero sus colecciones reflejan los cambios de estado de cada conexión.
`ActiveConnections` keeps the same creation/query API as the engine, but its
collections reflect each connection's state changes.
## Integración Con App
## App Integration
`active-app` expone el servicio `connections` cuando la app lo declara porque
las conexiones son app-scoped y suelen tener tipos específicos del proyecto:
`active-app` exposes the `connections` service when the app declares it, because
connections are app-scoped and usually have project-specific types:
```ts
const App = createActiveApp({
@ -96,16 +95,15 @@ const App = createActiveApp({
const Connections = App.connections;
```
App inyecta:
App injects:
- `App.logger`, como `Logger` común de `$libs/logger`.
- `App.timers`, para reconexión, heartbeat y timeouts de ack.
- `App.logger`, as the common `Logger` from `$libs/logger`.
- `App.timers`, for reconnection, heartbeat and ack timeouts.
### Reacción a cambios de identidad
### Reacting to identity changes
El patrón canónico para reaccionar a cambios de sesión vive en los
**presets de `arts/active-app`**, no dentro del registry de
conexiones:
The canonical pattern for reacting to session changes lives in the
**`arts/active-app` presets**, not inside the connection registry:
```ts
import { applyStandardOrca } from '$active-app/presets';
@ -120,22 +118,21 @@ const App = createActiveApp({
});
applyStandardOrca(App);
// → registra applyConnectionsReauthOnIdentityChange y
// applyConnectionsCloseOnRevoke entre otros, todos vía orca.
// → registers applyConnectionsReauthOnIdentityChange and
// applyConnectionsCloseOnRevoke among others, all via orca.
```
`applyConnectionsReauthOnIdentityChange` escucha
`SESSION_EVENT_IDENTITY_CHANGED` y llama
`App.connections.reauthenticateAll()`. `applyConnectionsCloseOnRevoke`
escucha `SESSION_EVENT_REVOKED` y llama `App.connections.closeAll()`.
Apps que prefieran granularidad pueden llamar a los presets
individuales en lugar del agregador.
`applyConnectionsReauthOnIdentityChange` listens to
`SESSION_EVENT_IDENTITY_CHANGED` and calls
`App.connections.reauthenticateAll()`. `applyConnectionsCloseOnRevoke` listens
to `SESSION_EVENT_REVOKED` and calls `App.connections.closeAll()`. Apps that
prefer granularity can call the individual presets instead of the aggregator.
### Sesión per-connection (modo manual)
### Per-connection session (manual mode)
Para casos donde una conexión concreta tiene un `ConnectionSessionSource`
propio (ajeno al `App.session` global), o para usos standalone sin
orca, cada conexión sigue aceptando `session` en sus opciones:
For cases where a specific connection has its own `ConnectionSessionSource`
(separate from the global `App.session`), or for standalone uses without orca,
each connection still accepts `session` in its options:
```ts
const Main = Connections.createConnection('main', {
@ -149,26 +146,26 @@ const Main = Connections.createConnection('main', {
});
```
Si la conexión declara `session.enabled`, su lógica interna
(`session-wiring.ts`) escucha el `onChange` del source y reacciona
con `reauthenticate()` / `disconnect()`. La reautenticación necesita
`auth`: define qué credencial nueva se envía cuando el source dice
"identidad cambió". Sin `auth` no se puede emitir un frame de reauth
y debe resolverse con reconnect/disconnect manual.
Las dos vías (preset orca a nivel App y `session` per-connection)
coexisten. La regla práctica: usa el preset cuando uses `arts/orca` y
`arts/session`; usa `session` per-connection para casos standalone o
cuando la conexión vive fuera del ciclo App.
`connection` **siempre** usa el `timers` inyectado — nunca construye su propio
scheduler (es un parámetro obligatorio). Los diagnósticos del scheduler salen bajo
la categoría `timer`; los de conexiones salen bajo `connection` o
If the connection declares `session.enabled`, its internal logic
(`session-wiring.ts`) listens to the source's `onChange` and reacts with
`reauthenticate()` / `disconnect()`. Reauthentication needs `auth`: it defines
which new credential is sent when the source says "identity changed". Without
`auth` a reauth frame cannot be emitted and it must be resolved with a manual
reconnect/disconnect.
The two routes (App-level orca preset and per-connection `session`) coexist. The
practical rule: use the preset when you use `arts/orca` and `arts/session`; use
per-connection `session` for standalone cases or when the connection lives
outside the App lifecycle.
`connection` **always** uses the injected `timers` — it never builds its own
scheduler (it is a required parameter). Scheduler diagnostics come out under the
`timer` category; connection diagnostics under `connection` or
`connection:<name>`.
## Estados
## States
Estados de conexión:
Connection states:
```txt
idle -> connecting -> open
@ -177,7 +174,7 @@ open -> closing -> closed
connecting/reconnecting -> failed
```
Campos útiles:
Useful fields:
```ts
Main.state;
@ -190,13 +187,13 @@ Main.lastMessageAt;
Main.reconnectAttempt;
```
`generation` cambia cuando se abre una conexión nueva. Los timeouts de ack,
heartbeat y reconexión usan esa generación para no resolver trabajo viejo sobre
una conexión nueva.
`generation` changes when a new connection opens. Ack, heartbeat and reconnect
timeouts use that generation so they never resolve stale work against a new
connection.
## Transports
El contrato mínimo es `ConnectionTransport`:
The minimal contract is `ConnectionTransport`:
```ts
interface ConnectionTransport {
@ -214,17 +211,17 @@ interface ConnectionTransport {
}
```
Incluidos:
Included:
- `createWebSocketTransport()` para navegador/runtime con WebSocket.
- `createMockTransport()` para tests, loopback y páginas de diagnóstico.
- `createWebSocketTransport()` for browser/runtime with WebSocket.
- `createMockTransport()` for tests, loopback and diagnostic pages.
El transporte no decide reconexión, auth, heartbeat ni canales. Solo abre,
envía, cierra y emite eventos.
The transport does not decide reconnection, auth, heartbeat or channels. It only
opens, sends, closes and emits events.
## Frames
El frame canónico:
The canonical frame:
```ts
interface ConnectionFrame<TType extends string = string, TPayload = unknown> {
@ -239,13 +236,13 @@ interface ConnectionFrame<TType extends string = string, TPayload = unknown> {
}
```
El serializer por defecto es JSON y valida la forma mínima del frame. Errores
de encode/decode no se lanzan como strings dispersos: devuelven resultados
tagged o errores `ConnectionInvalidFrameError` según el punto de entrada.
The default serializer is JSON and validates the frame's minimal shape.
Encode/decode errors are not thrown as scattered strings: they return tagged
results or `ConnectionInvalidFrameError` errors depending on the entry point.
## Send Y Request/Reply
## Send and Request/Reply
`send()` devuelve un resultado tagged:
`send()` returns a tagged result:
```ts
const result = await Main.send('project.updated', { id: 'p1' });
@ -255,8 +252,8 @@ if (!result.ok) {
}
```
`request()` usa `ack: true`, genera un `id` y espera un frame entrante con
`replyTo` igual a ese id:
`request()` uses `ack: true`, generates an `id` and waits for an incoming frame
with `replyTo` equal to that id:
```ts
const reply = await Main.request<{ id: string }, { ok: boolean }>('project.sync', {
@ -268,7 +265,7 @@ if (reply.ok) {
}
```
Razones de fallo principales:
Main failure reasons:
- `timeout`
- `closed`
@ -276,9 +273,9 @@ Razones de fallo principales:
- `transport_error`
- `invalid_reply`
## Canales
## Channels
Los canales son topics nombrados dentro de una conexión. Se cachean por nombre:
Channels are named topics within a connection. They are cached by name:
```ts
type ProjectEvents = {
@ -296,19 +293,19 @@ await Projects.send('project.updated', { id: 'p1', version: 2 });
await Projects.leave();
```
Estados de canal:
Channel states:
```txt
idle -> joining -> joined -> leaving -> left
joining -> failed
```
`dispose()` del canal limpia listeners y deja el canal en estado terminal
`left`.
The channel's `dispose()` clears listeners and leaves the channel in the
terminal `left` state.
## Reconexión
## Reconnection
La reconexión usa `timer` y backoff configurable:
Reconnection uses `timer` and a configurable backoff:
```ts
Connections.createConnection('main', {
@ -326,10 +323,10 @@ Connections.createConnection('main', {
});
```
Si `reconnectOnVisible` o `reconnectOnOnline` están activos, el módulo escucha
eventos del navegador y pide reconexión cuando la conexión está cerrada o
fallida. Esa capa no recibe funciones reducidas por severidad; recibe `Diagnostics`, que
incluye el `Logger` completo y emite eventos catalogados.
If `reconnectOnVisible` or `reconnectOnOnline` are active, the module listens to
browser events and requests reconnection when the connection is closed or
failed. That layer receives no severity-narrowed functions; it receives
`Diagnostics`, which includes the full `Logger` and emits catalogued events.
## Heartbeat
@ -346,13 +343,13 @@ Connections.createConnection('main', {
});
```
El heartbeat envía `pingType` periódicamente y espera `pongType`. Si vence el
timeout, cierra la conexión con `heartbeat_timeout` y deja que la política de
reconexión decida el siguiente paso.
The heartbeat sends `pingType` periodically and waits for `pongType`. If the
timeout elapses, it closes the connection with `heartbeat_timeout` and lets the
reconnection policy decide the next step.
## Auth Y Sesión
## Auth and Session
Auth de conexión:
Connection auth:
```ts
Connections.createConnection('main', {
@ -365,7 +362,7 @@ Connections.createConnection('main', {
});
```
El resultado de auth es tagged:
The auth result is tagged:
```ts
await Main.reauthenticate(); // { ok: true } | { ok: false, reason, error? }
@ -395,12 +392,12 @@ Both routes coexist: pick the orca preset when using `arts/orca` +
`arts/session`; pick the per-connection `session` option for manual
control.
`connection` no crea sesiones ni decide permisos. En servidor, los joins/sends
de un canal deben validarse con `auth/session/perm`.
`connection` does not create sessions or decide permissions. On the server, a
channel's joins/sends must be validated with `auth/session/perm`.
## Buffer
Cuando la conexión no está abierta, `send()` puede comportarse según policy:
When the connection is not open, `send()` can behave according to a policy:
```ts
buffer: {
@ -410,21 +407,21 @@ buffer: {
}
```
- `buffer`: encola y drena al abrir.
- `drop`: acepta la llamada pero descarta.
- `fail`: devuelve `{ ok: false, reason: 'closed' }`.
- `buffer`: queues and drains on open.
- `drop`: accepts the call but discards it.
- `fail`: returns `{ ok: false, reason: 'closed' }`.
## Diagnostics Y Logger
## Diagnostics and Logger
Las opciones públicas aceptan `logger?: Logger` desde `$libs/logger`. No existe
un contrato local de logger para `connection`.
The public options accept `logger?: Logger` from `$libs/logger`. There is no
local logger contract for `connection`.
```ts
import type { Logger } from '$libs/logger';
```
Internamente `connection` usa `createConnectionDiagnostics(logger)` y eventos
catalogados en `CONNECTION_DIAGNOSTIC_EVENTS`:
Internally `connection` uses `createConnectionDiagnostics(logger)` and catalogued
events in `CONNECTION_DIAGNOSTIC_EVENTS`:
- `connect_failed`
- `transport_error`
@ -441,13 +438,13 @@ catalogados en `CONNECTION_DIAGNOSTIC_EVENTS`:
- `session_revoked`
- `listener_threw`
Los mensajes y categorías viven en `consts.ts`. Si quieres enviar eventos a
Sentry, Loki o Datadog, inyecta un `EngineLogger` con el transporte adecuado;
`connection` solo emite al contrato común.
Messages and categories live in `consts.ts`. If you want to send events to
Sentry, Loki or Datadog, inject an `EngineLogger` with the right transport;
`connection` only emits to the common contract.
## Errores
## Errors
Programmer errors lanzan clases tipadas:
Programmer errors throw typed classes:
- `ConnectionDisposedError`
- `ConnectionAlreadyExistsError`
@ -458,18 +455,19 @@ Programmer errors lanzan clases tipadas:
- `ConnectionChannelNotFoundError`
- `ConnectionWebSocketUnavailableError`
Fallos runtime de transporte/envío/auth/request devuelven resultados tagged
para que el consumidor pueda decidir sin `try/catch` obligatorio.
Runtime transport/send/auth/request failures return tagged results so the
consumer can decide without a mandatory `try/catch`.
## Página De Prueba
## Test Page
La demo interactiva vive en `/active/docs/conn` (chat WebSocket + ciclo de vida de
conexión/canal). La demo de ecosistema usa `connection` con transporte mock loopback
para probar la integracion con `active-app`, `timer`, `logger`, `perm` y `cache`.
The interactive demo lives at `/active/docs/conn` (WebSocket chat + connection /
channel lifecycle). The ecosystem demo uses `connection` with a mock loopback
transport to test integration with `active-app`, `timer`, `logger`, `perm` and
`cache`.
## Testing
Tests principales:
Main tests:
```txt
src/arts/connection/test/engine-connections.test.ts
@ -479,14 +477,14 @@ src/arts/connection/test/connection-state.test.ts
src/arts/connection/test/websocket.test.ts
```
Casos que deben mantenerse cubiertos:
Cases that must stay covered:
- creación duplicada y lookup inexistente;
- `this` no requerido al desestructurar métodos del engine;
- reloj inyectado para timestamps deterministas;
- reconnect con fake timers;
- duplicate creation and nonexistent lookup;
- no `this` required when destructuring engine methods;
- injected clock for deterministic timestamps;
- reconnect with fake timers;
- heartbeat timeout;
- request/reply con timeout y rechazo;
- request/reply with timeout and rejection;
- channel join/leave/dispose;
- bridge de sesión refresh/expire/revoke;
- serializer inválido y transport errors.
- session bridge refresh/expire/revoke;
- invalid serializer and transport errors.

Loading…
Cancel
Save

Powered by TurnKey Linux.