# Referencia de hooks, plugins y timeline

Extiende el lifecycle, instala plugins con grants y controla proyecciones de tiempo sin confundir sus autoridades.

Un hook modifica el recorrido de lifecycle; un plugin agrupa setup, permisos y cleanup; timeline conserva un journal de proyecciones. Cada mecanismo tiene un dueño y una forma de terminar su actividad.

<span class="section-anchor" id="section-1"></span>

## Interceptar una fase

```js
import { wu, createGuardHook } from 'wu-framework';

const removeGuard = wu.hooks.use('beforeMount', createGuardHook(context => {
  return context.appName !== 'disabled-app';
}), { name: 'availability' });
// Al retirar esta extensión:
removeGuard();
```

Las fases disponibles son before/after Init, Load, Mount, Update, Unmount y Destroy. Una prioridad mayor se ejecuta antes. Omitir `next()` cancela la cadena; `failOpen: true` permite continuar tras un fallo del middleware. Un guard de UI no sustituye el control de roles ni los grants del core.

<span class="section-anchor" id="section-2"></span>

## Conceder autoridad a un plugin

Los permisos de plugin son `mount`, `events`, `store`, `apps`, `config` y `unsafe`. Un plugin solicita esos nombres y el host concede `options.grants` al instalar. No reutilices como grants de plugin los nombres `store.read` o `rpc.call`: pertenecen al contrato de aplicaciones.

Espera `pluginSystem.use` o `pluginSystem.ready` antes de depender del setup. Instalar desde una factory asíncrona también participa en ese arranque. El plugin ejecuta código host; su API reducida no elimina privilegios capturados en closures.

<span class="section-anchor" id="section-3"></span>

## Grabar antes de escribir

```js
await wu.timelineReady();
wu.timeline.record({ maxEntries: 500 });
wu.store.set('demo.count', 1);
wu.store.set('demo.count', 2);
await wu.timeline.seek(0, { apps: false, props: false });
await wu.timeline.live();
wu.timeline.stop();
```

La facade `wu.timeline` conserva métodos chainable para controles síncronos. El objeto real devuelto por `timelineReady` tiene retornos distintos; utiliza la facade en los ejemplos de esta referencia. Seek rematerializa store, props y composición según sus opciones; no rebobina red, timers ni infraestructura.

<span class="section-anchor" id="section-4"></span>

## wu.hooks.use

Instala middleware de lifecycle con prioridad y limpieza individual.

`wu.hooks.use(phase, middleware, options?): () => void`

- **Parámetros:** Fase válida, middleware(context,next); options: name, priority, failOpen.
- **Retorno:** Función de baja; la declaración TypeScript indica void.
- **Errores:** Fase desconocida o callback inválido lanza; omitir next cancela la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** stop() retira solo ese middleware.

```js
const stop = wu.hooks.use('beforeMount', async (context, next) => {
  console.log(context.appName);
  await next();
}, { name: 'mount-log', priority: 10 });
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-5"></span>

## wu.hooks.useMultiple

Instala un mismo middleware en varias fases.

`wu.hooks.useMultiple(phases, middleware, options?): () => void`

- **Parámetros:** Array de fases; usa un name estable para identificar cada registro derivado.
- **Retorno:** Cleanup conjunto.
- **Errores:** Cada fase debe existir; no es un registro atómico si una fase posterior falla.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** stop() retira las instalaciones creadas.

```js
const stop = wu.hooks.useMultiple(['beforeMount', 'beforeUnmount'], async (ctx, next) => {
  await next();
}, { name: 'trace' });
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-6"></span>

## wu.hooks.remove

Retira el middleware identificado por fase y nombre.

`wu.hooks.remove(phase, name): void`

- **Parámetros:** phase y el name de registro.
- **Retorno:** void; ausencia no lanza.
- **Errores:** Nombres duplicados vuelven ambigua la baja; prefiere el cleanup individual.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos nuevos.

```js
wu.hooks.remove('beforeMount', 'mount-log');
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-7"></span>

## wu.hooks.getHooks

Consulta metadatos de middleware instalado.

`wu.hooks.getHooks(phase?): object | object[]`

- **Parámetros:** Fase opcional.
- **Retorno:** Sin fase: mapa de metadatos; con fase: array de registros.
- **Errores:** Con fase devuelve registros internos; no los mutas como API de configuración.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos nuevos.

```js
console.log(wu.hooks.getHooks());
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-8"></span>

## wu.hooks.getStats

Consulta distribución de hooks y registros de ejecución.

`wu.hooks.getStats(): object`

- **Parámetros:** Sin argumentos.
- **Retorno:** Objeto de métricas.
- **Errores:** El log de ejecución tiene capacidad limitada.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos nuevos.

```js
console.log(wu.hooks.getStats());
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-9"></span>

## wu.hooks.cleanup

Retira middleware de una fase o de todas.

`wu.hooks.cleanup(phase?): void`

- **Parámetros:** Fase opcional; sin ella también vacía el log de ejecución.
- **Retorno:** void.
- **Errores:** No cancela por sí solo un callback que ya está ejecutándose.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Libera registros; recursos creados por callbacks necesitan su propio cleanup.

```js
wu.hooks.cleanup('beforeMount');
```

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-10"></span>

## createSimpleHook

Convierte un callback en middleware que continúa la cadena.

`createSimpleHook(fn): Function`

- **Parámetros:** fn(context) puede ser asíncrono.
- **Retorno:** Middleware async(context,next).
- **Errores:** Los errores del callback siguen la política de la factory y la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Instálalo con hooks.use y conserva el cleanup de ese registro.

```js
import { createSimpleHook } from 'wu-framework';
const middleware = createSimpleHook(ctx => console.log(ctx.appName));
```

Espera el callback y después invoca next.

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-11"></span>

## createConditionalHook

Ejecuta un callback si la condición resulta verdadera y continúa.

`createConditionalHook(condition, fn): Function`

- **Parámetros:** condition(context) y fn(context).
- **Retorno:** Middleware async(context,next).
- **Errores:** Los errores del callback siguen la política de la factory y la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Instálalo con hooks.use y conserva el cleanup de ese registro.

```js
import { createConditionalHook } from 'wu-framework';
const middleware = createConditionalHook(ctx => ctx.appName === 'catalog', ctx => console.log(ctx));
```

La condición no es un guard de cancelación: una condición falsa continúa la cadena.

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-12"></span>

## createGuardHook

Continúa la cadena solamente cuando el guard lo permite.

`createGuardHook(shouldContinue): Function`

- **Parámetros:** shouldContinue(context), síncrono o promesa.
- **Retorno:** Middleware async(context,next).
- **Errores:** Los errores del callback siguen la política de la factory y la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Instálalo con hooks.use y conserva el cleanup de ese registro.

```js
import { createGuardHook } from 'wu-framework';
const middleware = createGuardHook(ctx => ctx.appName !== 'disabled-app');
```

Si devuelve false no llama a next; no sustituye grants ni autorización de servidor.

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-13"></span>

## createTransformHook

Transforma el contexto que recibirá el siguiente middleware.

`createTransformHook(transformer): Function`

- **Parámetros:** transformer(context) devuelve datos para el contexto.
- **Retorno:** Middleware async(context,next).
- **Errores:** Los errores del callback siguen la política de la factory y la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Instálalo con hooks.use y conserva el cleanup de ese registro.

```js
import { createTransformHook } from 'wu-framework';
const middleware = createTransformHook(ctx => ({ ...ctx, traceLabel: 'catalog' }));
```

No equivale a configurar el core fuera de los campos consumidos por ese lifecycle.

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-14"></span>

## createTimedHook

Limita cuánto espera el middleware a un callback.

`createTimedHook(fn, timeout?: number): Function`

- **Parámetros:** fn(context); timeout por defecto 5000 ms.
- **Retorno:** Middleware async(context,next).
- **Errores:** Los errores del callback siguen la política de la factory y la cadena.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Instálalo con hooks.use y conserva el cleanup de ese registro.

```js
import { createTimedHook } from 'wu-framework';
const middleware = createTimedHook(async ctx => console.log(ctx.appName), 1000);
```

Un fallo o timeout se registra y la cadena continúa; el trabajo original no se aborta automáticamente.

Referencia: `src/core/wu-hooks.js`.

<span class="section-anchor" id="section-15"></span>

## createPlugin

Construye un plugin con nombre, permisos solicitados y callbacks.

`createPlugin(config): WuPlugin`

- **Parámetros:** config con name; install/uninstall y hooks son opcionales.
- **Retorno:** Objeto de plugin; crear no instala.
- **Errores:** Nombre/config inválidos pueden lanzar.
- **Permisos:** El plugin solicita permisos; el host los concede al llamar use.
- **Limpieza:** Instala con pluginSystem.use y retira con uninstall.

```js
import { createPlugin } from 'wu-framework';
const plugin = createPlugin({ name: 'catalog-observer', permissions: ['events'], install(api) { console.log(api.version); } });
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-16"></span>

## wu.pluginSystem.use

Instala un plugin y espera su setup antes de considerarlo disponible.

`wu.pluginSystem.use(pluginOrFactory, options?): Promise<boolean>`

- **Parámetros:** Plugin o factory async; options.grants y compatibility configuran autoridad.
- **Retorno:** true si queda instalado; rechaza fallos de validación/setup.
- **Errores:** Permisos requeridos sin grants, duplicados y errores de install pueden rechazar.
- **Permisos:** Permisos plugin: mount, events, store, apps, config, unsafe; distintos de capabilities de app.
- **Limpieza:** await uninstall(name) revoca su API y ejecuta cleanup.

```js
await wu.pluginSystem.use({
  name: 'catalog-observer', permissions: ['events'],
  install(api) { console.log(api.version); },
}, { grants: ['events'] });
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-17"></span>

## wu.pluginSystem.ready

Espera instalaciones pendientes y expone sus fallos al bootstrap.

`wu.pluginSystem.ready(): Promise<boolean>`

- **Parámetros:** Sin argumentos.
- **Retorno:** true al terminar correctamente.
- **Errores:** Rechaza el primer error pendiente e incluye pendingPluginErrors.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No desinstala plugins exitosos.

```js
await wu.pluginSystem.ready();
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-18"></span>

## wu.pluginSystem.uninstall

Retira un plugin y su autoridad asociada.

`wu.pluginSystem.uninstall(name): Promise<boolean>`

- **Parámetros:** Nombre exacto del plugin.
- **Retorno:** Booleano de desinstalación.
- **Errores:** Uninstall puede fallar; consulta el error y los diagnósticos.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Invoca el hook uninstall y libera hooks/listeners propiedad del plugin.

```js
await wu.pluginSystem.uninstall('catalog-observer');
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-19"></span>

## wu.pluginSystem.getPlugin

Consulta la definición de un plugin instalado.

`wu.pluginSystem.getPlugin(name): WuPlugin | undefined`

- **Parámetros:** Nombre exacto.
- **Retorno:** Plugin o undefined.
- **Errores:** No existe hasta completar la instalación.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos nuevos.

```js
console.log(wu.pluginSystem.getPlugin('catalog-observer')?.name);
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-20"></span>

## wu.pluginSystem.getStats

Enumera plugins, permisos concedidos y hooks activos.

`wu.pluginSystem.getStats(): object`

- **Parámetros:** Sin argumentos.
- **Retorno:** Objeto con totalPlugins, plugins y hooks.
- **Errores:** No verifica efectos externos del plugin.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos nuevos.

```js
console.log(wu.pluginSystem.getStats());
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-21"></span>

## wu.pluginSystem.cleanup

Desinstala todos los plugins del sistema.

`wu.pluginSystem.cleanup(): Promise<void>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Promesa cuando termina el recorrido de desinstalación.
- **Errores:** Los fallos siguen el contrato de uninstall.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Afecta a todos los plugins; pertenece al teardown del host.

```js
await wu.pluginSystem.cleanup();
```

Referencia: `src/core/wu-plugin.js`.

<span class="section-anchor" id="section-22"></span>

## wu.timelineReady

Carga el recorder antes de las primeras escrituras que quieras capturar.

`wu.timelineReady(): Promise<unknown>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Instancia real del recorder; no es la facade chainable.
- **Errores:** Rechaza errores de descarga/import del chunk.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** wu.timeline.stop() retira observadores; clear reinicia el journal.

```js
await wu.timelineReady();
wu.timeline.record();
```

Referencia: `src/index.js`.

<span class="section-anchor" id="section-23"></span>

## wu.timeline.record

Inicia el journal desde el estado y apps actuales.

`wu.timeline.record(options?: { snapshotEvery?: number; maxEntries?: number }): WuTimelineFacade`

- **Parámetros:** Frecuencia de snapshots y límite de entradas opcionales.
- **Retorno:** WuTimelineFacade
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** stop() retira los taps.

```js
await wu.timelineReady();
wu.timeline.record({ snapshotEvery: 25, maxEntries: 500 });
```

La facade devuelve ella misma; el recorder real devuelve status. Sin timelineReady, la carga diferida puede perder escrituras iniciales.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-24"></span>

## wu.timeline.stop

Detiene la captura manteniendo el journal para inspección.

`wu.timeline.stop(): WuTimelineFacade`

- **Parámetros:** Sin argumentos.
- **Retorno:** WuTimelineFacade
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Retira taps de store y bus.

```js
wu.timeline.stop();
```

No detiene timers, requests ni procesos creados por las aplicaciones.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-25"></span>

## wu.timeline.clear

Vacía el journal y toma la situación actual como nuevo baseline.

`wu.timeline.clear(): WuTimelineFacade`

- **Parámetros:** Sin argumentos.
- **Retorno:** WuTimelineFacade
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No sustituye stop si quieres dejar de grabar.

```js
wu.timeline.clear();
```

No borra estado de negocio ni revierte efectos externos.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-26"></span>

## wu.timeline.seek

Materializa una posición del journal en sus proyecciones.

`wu.timeline.seek(position: number, options?: { store?: boolean; apps?: boolean; props?: boolean }): Promise<WuTimelineStatus>`

- **Parámetros:** Índice 0..length; selecciona store/apps/props.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Vuelve a live para recuperar la posición final.

```js
const status = await wu.timeline.seek(0, { store: true, apps: false, props: false });
```

No revierte HTTP, timers ni infraestructura. Las escrituras hechas en el pasado no crean una rama nueva.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-27"></span>

## wu.timeline.live

Materializa el final del journal.

`wu.timeline.live(): Promise<WuTimelineStatus>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Conserva el recorder hasta stop.

```js
await wu.timeline.live();
```

Volver a live descarta cambios no registrados durante la posición histórica.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-28"></span>

## wu.timeline.stepBack

Retrocede una posición del journal.

`wu.timeline.stepBack(): Promise<WuTimelineStatus>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** stop libera la captura cuando termine la sesión.

```js
await wu.timeline.stepBack();
```

Se limita a las proyecciones reproducibles del recorder.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-29"></span>

## wu.timeline.stepForward

Avanza una posición del journal.

`wu.timeline.stepForward(): Promise<WuTimelineStatus>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** stop libera la captura cuando termine la sesión.

```js
await wu.timeline.stepForward();
```

No reproduce efectos externos de una acción.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-30"></span>

## wu.timeline.export

Serializa entradas, snapshots y baseline de apps.

`wu.timeline.export(): Promise<WuTimelineExport>`

- **Parámetros:** Sin argumentos.
- **Retorno:** Promise<WuTimelineExport>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** El host decide dónde guardar/exportar el objeto.

```js
const journal = await wu.timeline.export();
```

Puede contener estado de aplicación; no es una auditoría durable automática.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-31"></span>

## wu.timeline.import

Carga un journal serializado para inspeccionarlo.

`wu.timeline.import(journal: WuTimelineExport): Promise<WuTimelineStatus>`

- **Parámetros:** Objeto de export compatible.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** Afecta al recorder; elige explícitamente la proyección posterior.

```js
await wu.timeline.import(journal);
```

Valida procedencia del estado; importar no ejecuta un sandbox remoto.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-32"></span>

## wu.timeline.ingest

Combina entradas con el orden del journal y un baseline común vacío.

`wu.timeline.ingest(entries: WuTimelineEntry[]): Promise<WuTimelineStatus>`

- **Parámetros:** Entradas con reloj Lamport e identidad de sitio.
- **Retorno:** Promise<WuTimelineStatus>
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No abre ni autentica un transporte.

```js
await wu.timeline.ingest(remoteEntries);
```

El merge no materializa por sí mismo todo el estado en UI; requiere una base compartida y reglas de proyección.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-33"></span>

## wu.timeline.entries

Lee las entradas que conserva el recorder.

`wu.timeline.entries(): WuTimelineEntry[]`

- **Parámetros:** Sin argumentos.
- **Retorno:** WuTimelineEntry[]
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos.

```js
console.table(wu.timeline.entries());
```

El array se copia superficialmente; no lo uses para mutar el recorder.

Referencia: `src/core/wu-timeline.js`.

<span class="section-anchor" id="section-34"></span>

## wu.timeline.status

Consulta captura, posición, relojes y capacidad ocupada.

`wu.timeline.status(): WuTimelineStatus`

- **Parámetros:** Sin argumentos.
- **Retorno:** WuTimelineStatus
- **Errores:** La familia asíncrona puede rechazar errores de chunk, formato o materialización.
- **Permisos:** Host; la facade scoped no expone administración global.
- **Limpieza:** No instala recursos.

```js
console.log(wu.timeline.status());
```

loaded indica si el chunk ya está disponible; no garantiza que esté grabando.

Referencia: `src/core/wu-timeline.js`.