# Qué mantiene igual el código ofuscado (contrato semántico)

Qué conserva la salida de AfterPack del comportamiento de tu programa, cómo se comprueba cada versión del motor con código real y la breve lista de diferencias deliberadas.

Source: https://www.afterpack.dev/es/docs/semantic-contract

AfterPack cambia cómo se lee tu código y mantiene lo que hace. Un build ofuscado devuelve los mismos valores, lanza los mismos tipos de error, escribe la misma salida y hace los mismos cambios en la página que el build que le diste. Esta página describe ese contrato para el motor 0.2.3 y posteriores: qué se mantiene igual, cómo se comprueba cada versión y los pocos casos en que la salida difiere a propósito.

## Qué se mantiene igual

En cada [preset](https://www.afterpack.dev/es/docs/presets) y con cada [semilla](https://www.afterpack.dev/es/docs/config#seed), el build ofuscado conserva:

- **Resultados y errores.** Las funciones devuelven los mismos valores. El código que lanzaba un error sigue lanzando un error del mismo tipo.
- **Efectos y su orden.** La salida de consola, los cambios en el DOM, las peticiones de red y las escrituras en el almacenamiento ocurren en el mismo orden y con los mismos valores.
- **Orden asíncrono.** Las funciones `async`, las promesas y los temporizadores se resuelven en el mismo orden.
- **La superficie global.** Un script clásico define las mismas variables globales que antes, así que otros scripts de la página pueden seguir llamando a sus funciones y leyendo sus variables. Un módulo ES no añade nada a `window` ni sobrescribe ninguna variable global. Un archivo con `import` o `export` se lee como módulo; para uno sin ellos, se lo indica al motor un plugin de framework, la CLI o [`sourceType`](https://www.afterpack.dev/es/docs/config#sourceType).
- **Las reglas del lenguaje de las que depende tu código.** Los errores del modo estricto, `this` y los receptores de las llamadas, los getters y setters, los campos de clase, la aritmética `BigInt` y los ámbitos de `eval` y `with` se comportan tal como están escritos.
- **Los nombres que lee tu código.** En un archivo que lee `this.constructor.name` o `new.target.name`, como hace una clase de error propia, las clases y los constructores conservan los nombres que escribiste, salvo las excepciones de [diferencias deliberadas](#deliberate-differences).

Dos builds con semillas distintas difieren en su forma, nunca en su comportamiento.

## Qué cambia por diseño

- **El texto del código.** Cambian lo que devuelve `fn.toString()`, los números de línea y columna, y los nombres de una traza de pila. Guarda un [source map](https://www.afterpack.dev/es/docs/config#sourceMap-enabled) en privado para leer las trazas de pila de producción.
- **Tamaño y velocidad.** La salida es más grande y, a partir de `medium`, hace más trabajo por operación. [Rendimiento](https://www.afterpack.dev/es/docs/performance) tiene las cifras medidas.
- **Código que se inspecciona a sí mismo.** Cuando el motor encuentra código que lee su propia estructura en tiempo de ejecución, como `fn.toString()`, detiene el build y nombra el patrón. Reconoce el patrón con [`reflection.allow`](https://www.afterpack.dev/es/docs/config#reflection-allow) o entrega el archivo sin tocar con [`paths.exclude`](https://www.afterpack.dev/es/docs/config#paths-exclude).

## Cómo se comprueba cada versión

A partir del motor 0.2.3, cada versión del motor pasa una comprobación semántica antes de publicarse. La comprobación ejecuta cada programa dos veces, tal como está escrito y ofuscado, y compara lo que hace cada ejecución: los valores devueltos, los errores lanzados, la salida de consola y el orden del trabajo asíncrono. Cualquier diferencia hace fallar la versión, salvo que sea una de las [diferencias deliberadas](#deliberate-differences) de abajo.

La comprobación ejecuta:

- **Programas pequeños y específicos**, cada uno dirigido a una regla del lenguaje que un transformador de código puede romper: clases y campos privados, closures y ámbitos, generadores, el orden de `async`, plantillas literales, `BigInt`, `eval` y `with`, el modo estricto, etiquetas y `switch`. Cada preset, muchas semillas, y tanto el motor nativo como el motor WebAssembly.
- **Bibliotecas reales** en versiones fijadas, cada una ejercitada por un pequeño script: dayjs, Moment.js y Luxon para fechas, viem, ethers y @noble/curves para criptografía con uso intensivo de `BigInt`, Comlink, `_.template` de lodash y get-intrinsic.
- **Páginas en Chromium** que cargan varios scripts clásicos en orden, módulos ES con y sin imports, manejadores de eventos en línea y elementos personalizados, además de una app de Vue en un único chunk. Cada página debe comportarse igual y dejar `window` exactamente como lo deja el original, sin variables globales nuevas ni sobrescritas.

## Diferencias deliberadas

Estas diferencias y los nombres de clase descritos tras la tabla son las únicas que admite la comprobación. Cada fila afecta a código que reemplaza objetos integrados, envuelve instancias en un `Proxy`, llama a una clase sin `new` o lee los mensajes de error del motor de JavaScript, así que la mayoría de las aplicaciones nunca se encuentra con ellas. Se aplican dentro de las clases que el motor reescribe, a partir de `light`; `minify` deja las clases tal como están escritas.

| Tu código | Comportamiento nativo | Salida ofuscada |
|---|---|---|
| Define campos públicos de clase en una instancia que es un `Proxy` (devuelto por un constructor base) o que tiene uno en su cadena de prototipos | Solo se ejecuta la trampa `defineProperty` del proxy | También se ejecuta la trampa `has` para cada campo y, para un campo que el objeto no tiene, la trampa `set` y todo aquello a lo que reenvíe |
| Llama a un constructor de clase sin `new` | Lanza un `TypeError` antes de que se evalúe ningún valor por defecto de parámetro ni ninguna desestructuración | Evalúa los valores por defecto y la desestructuración de los parámetros, y luego lanza el mismo `TypeError` |
| Llama a un método privado o a `super.method()` después de reemplazar `Function.prototype.call`, o sobre un método con su propia propiedad `call` | Llama al método directamente | Pasa por el `call` reemplazado |
| Lee el mensaje de un `TypeError` de una comprobación de campo privado, de una segunda inicialización de un campo privado o de una clase llamada sin `new` | El mensaje propio del motor de JavaScript | El mensaje propio de AfterPack, con el mismo tipo de error |
| Reemplaza `TypeError`, `Object`, `Object.defineProperty`, `Reflect.construct`, `Reflect.ownKeys`, `WeakMap` o `WeakSet` antes de que se cargue el archivo | Las clases siguen usando los objetos integrados originales | Las clases reescritas usan el reemplazo |

Los nombres de clases y constructores difieren en unos pocos casos, en todos los presets. Un archivo que los lee solo de otras formas, como `err.constructor.name` o `const { name } = new.target`, obtiene los nombres renombrados. En un archivo que sí lee `this.constructor.name` o `new.target.name`, el nombre renombrado sigue apareciendo en una clase construida como `new (class X {})()`, en una clase anónima que recibe su nombre de un parámetro por defecto, en una función construida desde un caso de `switch` anterior al caso que la declara y en una función construida solo a través de otra referencia, como `new ns.F()`, un alias o `Reflect.construct`.

## Qué se deja tal como lo escribiste

Estas construcciones se dejan tal como están escritas allí donde reescribirlas con fidelidad no es barato. El código que las rodea se sigue renombrando y codificando, salvo alrededor de `with` y de un `eval` directo.

- Las funciones `async` y `await`.
- `delete`, y `typeof` aplicado a un nombre simple.
- Una función cuyo bloque `catch (e)` declara `var e`.
- Un `let` o `const` declarado directamente en un caso de `switch`.
- Una llamada opcional o una etiqueta cuya función llamada va entre paréntesis o es un miembro de `super`: `(o?.m)?.()`, `` (o?.m)`x` ``, `super.m?.()`.
- Una clase que asigna a su propio nombre, que llama a un miembro de `super` como llamada opcional, etiqueta o función entre paréntesis, o que tiene un constructor derivado sin exactamente una llamada a `super()` en el nivel superior.
- Los bucles, en `minify` y `light`.
- Los ámbitos alrededor de una sentencia `with` o de un `eval` directo. Los nombres de cada ámbito que lo contiene, hasta el nivel superior del archivo, conservan su forma original, y las variables globales dentro del cuerpo de un `with` se dejan tal como están escritas. El build lo informa con [`DIAG_DYNAMIC_SCOPE_NATIVE`](https://www.afterpack.dev/es/docs/diagnostics#complete-reference). Si el código evaluado no necesita variables locales, llama a `eval` de forma indirecta, `(0, eval)(code)`, o mueve el código que necesita `eval` o `with` a un archivo propio.

## Antes del motor 0.2.3

El motor 0.2.2 y anteriores no cumplían este contrato en algunos casos que la comprobación cubre ahora: los nombres de nivel superior de scripts clásicos compartidos con otros scripts, los módulos ES sin imports, los objetos como las fechas dentro de plantillas literales, las funciones usadas como constructores, los campos de clase sobre accesores heredados, las funciones que se refieren a sí mismas, la aritmética `BigInt` en `medium` y superiores, `eval` y `with`, un bloque `catch` que vuelve a declarar su variable con `var`, el orden de `async`, las llamadas opcionales entre paréntesis, las expresiones constantes con `**` y los nombres de clase leídos en tiempo de ejecución. Vuelve a hacer el build con el motor 0.2.3 o posterior; el [registro de cambios](https://www.afterpack.dev/es/changelog) enumera cada corrección.

## Informar de una diferencia

Una diferencia que no aparece en esta página es un bug. Abre un issue con una reproducción pequeña en [github.com/afterpack-dev/afterpack/issues](https://github.com/afterpack-dev/afterpack/issues). Hasta que se corrija, [`paths.exclude`](https://www.afterpack.dev/es/docs/config#paths-exclude) entrega el archivo afectado sin tocar.

## Siguiente

- [Diagnósticos](https://www.afterpack.dev/es/docs/diagnostics): cada código que puede informar un build y qué hacer con él.
- [Cómo funciona AfterPack](https://www.afterpack.dev/es/docs/concepts): qué hacen las transformaciones con tu código.
- [Presets](https://www.afterpack.dev/es/docs/presets): qué aplica cada nivel.
- [Configuración](https://www.afterpack.dev/es/docs/config): [`reflection.allow`](https://www.afterpack.dev/es/docs/config#reflection-allow), [`paths.exclude`](https://www.afterpack.dev/es/docs/config#paths-exclude) y las demás claves que enlaza esta página.
