# DOM, CSS y sandbox

Distingue ownership del DOM, aislamiento de estilos y realm JavaScript sin confundirlos con una frontera de seguridad.

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

## Tres decisiones distintas

El comportamiento de una app depende de **dónde se ejecuta JavaScript**, **quién posee el límite DOM** y **qué estilos recibe**. Configurar una de estas dimensiones no configura automáticamente las otras.

| Modo JavaScript | Ejecución | Uso y límites |
| --- | --- | --- |
| `module` | `import()` en la ventana principal | Módulos modernos de confianza; no aísla globals del host |
| `strict` | Import real dentro de iframe oculto | Realm separado y cleanup; requiere que el módulo pueda cargarse allí |
| `eval` | Camino de HTML/scripts clásicos dentro de iframe | Compatibilidad con UMD/IIFE; no soporta módulos ESM como strict |

El modo `eval` ejecuta scripts en el iframe por su camino de compatibilidad HTML; para módulos ESM utiliza `module` o `strict` según el realm que necesites.

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

## Strict y su fallback

```js
await wu.init({
  sandbox: 'strict',
  strictFallback: false,
  apps: [{ name: 'reports', url: '/apps/reports' }],
});

await wu.mount('reports', '#reports-slot');
console.table(wu.getSandboxInfo('reports'));
```

Un fallo de import dentro del iframe produce error por defecto. `strictFallback: true` autoriza explícitamente una degradación al modo eval; no arregla un bundle ESM incompatible con ese modo. La opción por aplicación prevalece sobre la global.

`getSandboxInfo` expone `requestedMode`, `actualMode`, `isolationLevel`, `mounted`, `nativeShadow` y `domOwnership`. Compara requestedMode y actualMode al diagnosticar un fallback: la configuración expresa la preferencia y el estado describe el modo que ejecuta esa app.

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

## Quién posee el DOM

| `domOwnership` | Resultado |
| --- | --- |
| `wu` | WU crea el ShadowRoot exterior y entrega una raíz interna al renderer |
| `app` | El renderer gestiona su propio límite, útil para LitElement |
| `light` | Montaje sin el ShadowRoot exterior de WU |

El valor explícito gana. En su ausencia, `nativeShadow: true` selecciona `app`, `false` selecciona `wu`; sin ambos, `framework: 'lit'` selecciona `app` y el resto `wu`.

El nombre `app` expresa una responsabilidad, no una garantía de que el componente realmente cree Shadow DOM. Si el renderer no crea su propio límite, su contenido permanece en el árbol DOM que recibió.

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

## Distribución de estilos

Configura el manifest de la aplicación:

```json
{
  "name": "reports",
  "entry": "assets/reports.js",
  "styleMode": "own-only"
}
```

| Valor | Significado |
| --- | --- |
| `shared` | El bridge permite distribución de estilos externos en el ShadowRoot |
| `isolated` / `none` | No inyecta estilos externos adicionales |
| `fully-isolated` / `own-only` | Inyecta los identificados como pertenecientes a esa app |

El bridge observa cambios y atribuye estilos a aplicaciones. Para una hoja creada por código host, existe `wu.tagStyleAsApp(element, appName)`. No presupongas que una regla CSS global del shell atravesará un ShadowRoot. Custom properties heredadas y propiedades CSS heredables pueden seguir participando en la apariencia del contenido.

El sandbox recibe `styleMode` desde el manifest. Coloca ahí la decisión de estilos; el campo del mismo nombre en la configuración superior del host no sustituye ese valor durante el montaje.

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

## La frontera de confianza real

El propio iframe documenta que es same-origin y redirige creación de nodos al documento del host. Un nodo puede exponer `ownerDocument.defaultView`, desde donde un script alcanza el host. Ocultar `parent` o `top` no cierra esa vía.

Utiliza estos modos para coordinar código de confianza, globals y limpieza. Para ejecutar código no confiable necesitas otra frontera, como un origen aislado y un protocolo restringido; los iframes same-origin del runtime mantienen acceso potencial al host.

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

## Contenedores y layout

Un slot debe ser territorio exclusivo del renderer. En React/Vue, pon el mensaje de loading como hermano del nodo de montaje: si el shell reconcilia sus hijos dentro del mismo nodo, puede borrar el árbol que WU insertó. El test Vue específico del repositorio reproduce ese fallo silencioso.

`fullBleed` en configuración host decide si el microfrontend ocupa el alto del slot y se hace responsable de su scroll. Si el contenido debe fluir con el documento, conserva el comportamiento por defecto. El alto del slot y su layout siguen siendo responsabilidad del shell.

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

## Referencia de implementación

Modos: `src/core/wu-core.js`, `src/core/wu-core.js`, `src/core/wu-core.js`, `src/core/wu-core.js`; ownership: `src/core/wu-sandbox.js`; límite same-origin: `src/core/wu-iframe-sandbox.js`. Pruebas: `tests/unit/sandbox-honesty.test.js`, `tests/unit/native-shadow-sandbox.test.js`, `tests/unit/sandbox-container-ownership.test.js`, `tests/unit/style-own-only.test.js`, `tests/unit/style-cross-app.test.js`, `tests/unit/vue-slot-mount-target.test.js`.

En `strict`, `parent.postMessage` respeta `targetOrigin` y entrega como `event.source` la ventana del sandbox. El acceso interno `__wuHostPostMessage` fue eliminado. Esta corrección de identidad no convierte el iframe same-origin en aislamiento frente a código hostil.