diff --git a/NEXT_STEPS.md b/NEXT_STEPS.md index afac66d..94fd202 100644 --- a/NEXT_STEPS.md +++ b/NEXT_STEPS.md @@ -21,6 +21,7 @@ Estado al cierre: - `arts/sess/engine-session.ts` delega la resolucion de revoke local/global/degradado en `session-revoke.ts`. - `arts/conn/connection.ts` delega la programacion de reconnect y exhaustion en `connection-reconnect-runtime.ts`. - `arts/conn/connection.ts` delega decode/routing de frames entrantes en `connection-message-router.ts`. + - `arts/conn/connection.ts` delega el intento open/auth/flush/join en `connection-connect.ts`. - `arts/timr/engine-timers.ts` delega fan-out de listeners y diagnostico de listeners en `timer-events.ts`. - Integracion total ampliada: `Auth.signOut()` valida anonimizacion, invalidacion de `Permissions` y evento `Cache.invalidate`. - Tanda focalizada verde: `npx vitest run src/arts/conn src/libs/cach src/arts/cach src/svrs/cach src/arts/auth src/svrs/auth src/libs/auth src/arts/aapp/test/ecosystem.integration.test.ts` -> 19 archivos, 82 tests. diff --git a/src/arts/conn/connection-connect.ts b/src/arts/conn/connection-connect.ts new file mode 100644 index 0000000..8d081d2 --- /dev/null +++ b/src/arts/conn/connection-connect.ts @@ -0,0 +1,78 @@ +import { + CONNECTION_CLOSE_REASON_AUTH_FAILED, + CONNECTION_CONNECT_REASON_AUTH_FAILED, + CONNECTION_CONNECT_REASON_TRANSPORT_ERROR, + CONNECTION_DIAGNOSTIC_EVENTS, + CONNECTION_STATE_CONNECTING, + CONNECTION_STATE_FAILED, + CONNECTION_STATE_RECONNECTING, + TIMER_KEY_RECONNECT +} from './consts.ts'; +import { emitConnectionDiagnostic, type ConnectionDiagnostics } from './diagnostics.ts'; +import type { ConnectionRequestRuntime } from './connection-requests.ts'; +import type { ConnectionStateTracker } from './connection-state.ts'; +import type { ConnectionTimerControls } from './connection-timers.ts'; +import type { ConnectionSender } from './sender.ts'; +import type { ConnectionConnectResult, ConnectionTransport } from './types.ts'; + +export interface RunConnectionConnectInput { + readonly reconnect: boolean; + readonly timers: ConnectionTimerControls; + resolveTransport(): ConnectionTransport; + setTransport(transport: ConnectionTransport): void; + attachTransport(transport: ConnectionTransport): void; + detachTransport(): void; + markOpen(): void; + markClosedFailed(error: unknown): void; + joinConfiguredChannels(reconnect: boolean): Promise; + readonly state: ConnectionStateTracker; + readonly requests: ConnectionRequestRuntime; + readonly sender: ConnectionSender; + readonly diagnostics: ConnectionDiagnostics; +} + +export async function runConnectionConnect( + input: RunConnectionConnectInput +): Promise { + input.timers.cancel(TIMER_KEY_RECONNECT); + const nextTransport = input.resolveTransport(); + input.setTransport(nextTransport); + input.attachTransport(nextTransport); + input.state.transition(input.reconnect ? CONNECTION_STATE_RECONNECTING : CONNECTION_STATE_CONNECTING); + try { + await nextTransport.open(); + input.markOpen(); + if (input.requests.hasAuthProvider()) { + const auth = await input.requests.runAuth(); + if (!auth.ok) { + const authError = auth.error ?? auth.reason; + input.state.setError(authError); + emitConnectionDiagnostic(input.diagnostics, CONNECTION_DIAGNOSTIC_EVENTS.AUTH_FAILED, auth); + input.detachTransport(); + nextTransport.close(undefined, CONNECTION_CLOSE_REASON_AUTH_FAILED); + input.markClosedFailed(authError); + return { + ok: false, + state: input.state.state, + reason: CONNECTION_CONNECT_REASON_AUTH_FAILED, + error: authError + }; + } + } + await input.sender.flushBuffer(); + await input.joinConfiguredChannels(input.reconnect); + return { ok: true, state: input.state.state }; + } catch (err) { + input.state.setError(err); + emitConnectionDiagnostic(input.diagnostics, CONNECTION_DIAGNOSTIC_EVENTS.CONNECT_FAILED, { + error: err + }); + input.state.transition(CONNECTION_STATE_FAILED, err); + return { + ok: false, + state: input.state.state, + reason: CONNECTION_CONNECT_REASON_TRANSPORT_ERROR, + error: err + }; + } +} diff --git a/src/arts/conn/connection.ts b/src/arts/conn/connection.ts index e060886..553dc02 100644 --- a/src/arts/conn/connection.ts +++ b/src/arts/conn/connection.ts @@ -2,6 +2,7 @@ import type { TimerScheduler } from '$timr'; import { createConnectionAckRegistry } from './acks.ts'; import { wireBrowserReconnect } from './browser-reconnect.ts'; import { createConnectionChannelRegistry } from './channel-registry.ts'; +import { runConnectionConnect } from './connection-connect.ts'; import { createConnectionEventBus } from './connection-events.ts'; import { routeConnectionMessage } from './connection-message-router.ts'; import { scheduleConnectionReconnect } from './connection-reconnect-runtime.ts'; @@ -12,16 +13,12 @@ import { CONNECTION_ACK_REASON_CLOSED, CONNECTION_CLOSE_REASON_DISCONNECT, CONNECTION_CLOSE_REASON_DISPOSE, - CONNECTION_CLOSE_REASON_AUTH_FAILED, CONNECTION_CLOSE_REASON_RECONNECT, CONNECTION_CLOSE_REASON_SESSION_EXPIRED, - CONNECTION_CONNECT_REASON_AUTH_FAILED, CONNECTION_CONNECT_REASON_DISPOSED, - CONNECTION_CONNECT_REASON_TRANSPORT_ERROR, CONNECTION_DIAGNOSTIC_EVENTS, CONNECTION_STATE_CLOSED, CONNECTION_STATE_CLOSING, - CONNECTION_STATE_CONNECTING, CONNECTION_STATE_FAILED, CONNECTION_STATE_OPEN, CONNECTION_STATE_RECONNECTING, @@ -232,46 +229,26 @@ export function createConnection { intentionalClose = false; - timers.cancel(TIMER_KEY_RECONNECT); - const nextTransport = resolveTransport(); - transport = nextTransport; - attachTransport(nextTransport); - stateTracker.transition(reconnect ? CONNECTION_STATE_RECONNECTING : CONNECTION_STATE_CONNECTING); try { - await nextTransport.open(); - markOpen(); - if (requests.hasAuthProvider()) { - const auth = await requests.runAuth(); - if (!auth.ok) { - const authError = auth.error ?? auth.reason; - stateTracker.setError(authError); - emitConnectionDiagnostic(diagnostics, CONNECTION_DIAGNOSTIC_EVENTS.AUTH_FAILED, auth); - detachTransport(); - transport?.close(undefined, CONNECTION_CLOSE_REASON_AUTH_FAILED); - markClosed(CONNECTION_STATE_FAILED, authError); - return { - ok: false, - state: stateTracker.state, - reason: CONNECTION_CONNECT_REASON_AUTH_FAILED, - error: authError - }; - } - } - await sender.flushBuffer(); - await joinConfiguredChannels(reconnect); - return { ok: true, state: stateTracker.state }; - } catch (err) { - stateTracker.setError(err); - emitConnectionDiagnostic(diagnostics, CONNECTION_DIAGNOSTIC_EVENTS.CONNECT_FAILED, { - error: err + return await runConnectionConnect({ + reconnect, + timers, + resolveTransport, + setTransport: (nextTransport) => { + transport = nextTransport; + }, + attachTransport, + detachTransport, + markOpen, + markClosedFailed: (error) => { + markClosed(CONNECTION_STATE_FAILED, error); + }, + joinConfiguredChannels, + state: stateTracker, + requests, + sender, + diagnostics }); - stateTracker.transition(CONNECTION_STATE_FAILED, err); - return { - ok: false, - state: stateTracker.state, - reason: CONNECTION_CONNECT_REASON_TRANSPORT_ERROR, - error: err - }; } finally { connectPromise = null; }