npx afterpack@latest ofusca un directorio de build. Úsalo cuando no exista un plugin para tu bundler, o en CI. Si existe un plugin para tu framework, instala @afterpack/<fw> en su lugar: tu build habitual emitirá entonces salida ofuscada sin ningún paso adicional. Consulta Frameworks.
Esta página cubre el argumento, los ejemplos, qué hace una ejecución, verify, restore, audit, la salida legible por máquina y los códigos de salida. Cada opción se define una sola vez, en Configuración.
Uso
afterpack [path] [--<key>=<value> ...] [-h | --help] [-v | --version]
afterpack verify [dir]
afterpack restore [dir]
afterpack audit <url>$ npx afterpack@latestLa ruta acepta una de dos cosas:
- un directorio, recorrido de forma recursiva en busca de cada
.js,.mjsy.cjs, o - un único archivo
.js,.mjso.cjs, ofuscado por sí solo.
Si lo ejecutas sin ruta, afterpack detecta la salida de tu build: el directorio en el que escribe por convención el bundler declarado en tu package.json (Next.js → .next/, Nuxt → .output/, SvelteKit → build/, Electron → out/, cualquier otro bundler compatible → dist/) cuando ese directorio existe; si no, el más reciente entre dist/, build/, out/, .output/ y .next/. Nunca lee el archivo de configuración de tu bundler, así que un directorio de salida personalizado no se detecta: pasa la ruta tú mismo cuando tengas uno. Imprime qué encontró y qué va a hacer, y después se ejecuta; en CI nunca pide confirmación. Cuando no encuentra nada, imprime la ayuda de inicio rápido y sale con 1. El comando también lee tu bundler desde package.json e imprime al final de la ejecución una línea que sugiere el plugin correspondiente.
Apúntalo a la salida de tu bundler, no a tu código fuente. La mayoría de los bundlers (Vite, Rollup, esbuild, webpack, Astro, Parcel) escriben en dist/. Next.js escribe en .next/ y Nuxt en .output/.
Ejemplos
npx afterpack@latest # detects the build output, light default
npx afterpack@latest dist/ # a directory you choose
npx afterpack@latest dist/ --preset=hard # heavier posture
npx afterpack@latest .next/ --seed=git # reproducible output, pinned to the commit
npx afterpack@latest build/widget.js # one file
npx afterpack@latest dist/ --diagnostics.level=all # every diagnostic, not the rolled-up summary
AFTERPACK_complexity=40 npx afterpack@latest dist/ # any flag also works as an env varQué hace una ejecución
- Recopila el JavaScript en
<path>. Recorre de forma recursiva cuando la ruta es un directorio, omite sus propias copias.backup.<hash>y omitenode_modules/a menos que lo incluyas con--paths.include='**/node_modules/**'. Si no recopila nada, el build falla. - Mantiene
.afterpack/fuera de git con un.gitignoredentro de él, que se escribe la primera vez que hace falta. Tu propio.gitignorenunca se edita. - Ofusca en el sitio cada archivo recopilado, con el preset solicitado, o con
--complexitycuando lo fijas de forma explícita. En un build de Next.js, Angular, Vite o webpack también renombra cada archivo con hash de contenido para que coincida con sus bytes ofuscados y reescribe cada referencia a él; en cualquier otra salida los nombres quedan tal como los escribió tu bundler. Consulta Nombres de archivo con hash de contenido. - Escribe los artefactos que los flags permiten y, siempre, el recibo de protección. Cuando está activado (por defecto, si se encontró un source map y el build no se ejecuta en CI), el Protection Map combinado se guarda en
.afterpack/protectionMap.html, ignorado por git y nunca servido, porque incrusta tu código fuente original. Un source map o una copia de seguridad contiene tu código fuente completo. Nunca despliegues ninguno de los dos. Si alguno queda bajo un segmento de ruta servido, la CLI avisa una vez. - Imprime los diagnósticos y, después, la sugerencia de bundler si encontró uno.
El comando sobrescribe la salida de tu build en el sitio y no conserva ninguna copia desde la que volver a ofuscar. Compila y luego ejecútalo una sola vez. En CI ese orden es automático.
No lo ejecutes dos veces sobre la misma salida
AfterPack se niega a ofuscar un archivo que ya contiene su propia salida ofuscada de una ejecución anterior: compara los archivos recopilados con el recibo de protección por hash de contenido y hace fallar el build con DIAG_ALREADY_OBFUSCATED, sea cual sea el directorio desde el que lo ejecutes, en lugar de ofuscar código ya ofuscado. Una copia que un framework hizo de una salida protegida, como el build/ de SvelteKit, se rechaza del mismo modo. Primero vuelve a compilar desde el código fuente: borra el directorio de salida o ejecuta el paso de limpieza de tu bundler.
Nombres de archivo con hash de contenido
Los bundlers nombran cada chunk según un hash de su contenido, como index-4f9a2c1e.js, para que un servidor pueda guardarlo en caché durante un año con Cache-Control: immutable. La ofuscación cambia los bytes, así que en los builds de abajo la ejecución da a cada archivo con hash de contenido un hash nuevo en su nombre y reescribe cada referencia a él en el HTML, los manifiestos y los demás chunks. Un nombre nunca apunta a bytes distintos, así que un navegador, una CDN o un service worker nunca sirve una copia obsoleta bajo un nombre reutilizado. El hash nuevo se obtiene del original más la semilla, la versión del motor y tu configuración, igual que un plugin nombra sus archivos; Nombres de archivo y caché explica qué significa la semilla para tu caché.
| Build | Apunta la CLI a | Qué renombra la ejecución |
|---|---|---|
| Next.js, con webpack o Turbopack | .next/, .next/static/ o .next/static/chunks/; out/ para una exportación estática | Los chunks bajo static/chunks/ |
| Application builder de Angular | dist/<app>/browser/ o dist/<app>/ | Cada archivo con hash. La ejecución también recalcula los atributos integrity y la tabla de hashes ngsw.json del service worker |
| Vite | Un directorio con un index.html o un .vite/manifest.json | Los scripts y hojas de estilo name-<hash> de assets/, o de las carpetas que nombra el manifiesto |
| webpack | Un directorio cuyo runtime de webpack carga los chunks | Los archivos con un hash de contenido hexadecimal en su nombre |
Los nombres con hash de contenido quedan tal como los escribió tu bundler, y la ejecución imprime un único aviso, cuando:
- la ruta es un solo archivo, o la salida viene de cualquier otro bundler;
- no se puede identificar el bundler de un build de Next.js;
- en un build de Vite o webpack, un archivo con hash no está referenciado desde dentro del directorio de build, dos hashes no se pueden distinguir, o una carpeta junto al directorio de build, como un bundle de servidor,
.svelte-kit/o.output/, nombra los archivos con hash o es demasiado grande para comprobarla; - un manifiesto de precaché, como el de Workbox, lista los archivos por revisión.
Para una caché de larga duración en un build así, usa el plugin de tu framework, o fija una --seed constante.
Un build de servidor de Angular cuyo angular-app-manifest.mjs incrusta hashes de assets se rechaza con salida 1, y la salida queda tal como la escribió el builder. Ofusca la salida de navegador de un build sin SSR.
Flags
Cada clave de configuración es un flag: --<key>=<value>, por ejemplo --complexity=40 o --identifiers.reserved=Hls,Foo para una lista. Un flag booleano se escribe sin valor para true (--build.backup) y =false la desactiva (--protectionMap.enabled=false). -h, --help y -v, --version imprimen y salen con 0.
Cada clave escalar o de lista de la referencia de opciones funciona como flag de esta forma. Los valores estructurados van en afterpack.json. Las variables de entorno siguen los mismos nombres con los puntos convertidos en guiones bajos, consulta Configuración; por ejemplo, AFTERPACK_key, AFTERPACK_preset y AFTERPACK_telemetry_enabled.
Archivos
| Ruta | Función |
|---|---|
afterpack.json | El archivo de configuración. AfterPack sube desde el directorio de trabajo hasta la coincidencia más cercana. Contiene toda la configuración del motor: opciones de build, key, todo. |
.afterpack/receipts/<build dir>.json | El recibo de protección de un directorio de build, por ejemplo .afterpack/receipts/dist.json o .afterpack/receipts/.next.json. Una ruta larga o poco habitual recibe un nombre abreviado con un hash. Registra cada archivo de salida con el hash de los bytes escritos, la versión del motor y la semilla. A partir de 1.0.1, varios builds de AfterPack que escriben en el mismo directorio de build comparten un único recibo. afterpack verify vuelve a calcular los hashes contra él, y gracias a él se rechaza una ejecución posterior sobre la misma salida sin recompilar. |
.afterpack/protectionMap.html | El Protection Map combinado. Ignorado por git automáticamente. Incrusta tu código fuente original. Nunca lo confirmes en git ni lo sirvas. |
.afterpack/backups/<build dir>/ | Tus originales de un directorio de build, byte a byte, por ejemplo .afterpack/backups/dist/, con un manifiesto de cada archivo que la ejecución creó, modificó, renombró o eliminó. Cada directorio de build tiene su propia copia de seguridad y su propio bloqueo, así que los builds de distintas apps de un mismo repositorio nunca comparten una. Se escribe en cada ejecución (--build.backup=false lo desactiva), y afterpack restore lo lee para deshacer la ejecución. La ejecución imprime su ruta al terminar. Es tu código fuente. Nunca lo confirmes en git ni lo despliegues. |
<file>.js.map | El source map emitido, cuando sourceMap.enabled está activado. Desactivado por defecto: una ejecución es de producción salvo que diga que es de desarrollo, y una ejecución de producción también elimina los source maps que escribió tu bundler, incluidos los de CSS. Un source map publicado lleva la salida directamente de vuelta a tu código fuente. |
.afterpack/ guarda los recibos, el Protection Map y las copias de seguridad. AfterPack lo encuentra a partir del directorio de build, sea cual sea el directorio desde el que ejecutes. Sube hasta la carpeta más cercana que contenga un lockfile (package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml, yarn.lock, bun.lock o bun.lockb), afterpack.json o .git. Si no la hay, usa la carpeta más cercana con un package.json, y si tampoco la hay, se coloca junto al directorio de build. Esa carpeta es la raíz de tu proyecto en estas páginas. .afterpack/ nunca está dentro de la salida del build.
La ubicación del recibo bajo .afterpack/receipts/ y los campos de su schema 1 conservan su nombre y su significado en toda la serie 1.x. Una versión menor puede añadir un campo, así que un script o un paso de CI que lea un recibo sigue funcionando.
Una ejecución falla, sin reescribir nada, cuando encuentra una carpeta .afterpack/ dentro de la salida del build (DIAG_AFTERPACK_DIR_IN_OUTPUT: borra esa carpeta y vuelve a compilar) o cuando no puede escribir el recibo (DIAG_RECEIPT_UNWRITABLE). Un .afterpack-protection.json que una versión anterior dejó en la salida se elimina en la siguiente ejecución.
afterpack verify [dir]
La puerta de despliegue. Cada build protegido escribe un recibo de protección en .afterpack/receipts/. verify encuentra el recibo de [dir] del mismo modo que la ejecución, así que funciona desde cualquier directorio. Vuelve a calcular el hash de cada archivo que el recibo nombra y falla cuando alguno ya no coincide, cuando el recibo falta o cuando procede de otro build. Si pasa, cada archivo que el recibo registra está presente y no ha cambiado desde que AfterPack lo escribió.
npx afterpack@latest verify . # a project root: looks in ./ then ./.next/
npx afterpack@latest verify dist # or the build output directory itself
npx afterpack@latest verify build # SvelteKit's adapter copy; also .output/public, out/Un directorio que un framework copió de su salida de build, como el build/ de SvelteKit, el .output/public/ de Nuxt o el out/ de Next, se comprueba contra el recibo del directorio que copió. verify asocia la copia a ese recibo por sus archivos y comprueba cada hash, así que un archivo modificado hace fallar la comprobación y se nombra. Apúntalo a la raíz del despliegue en sí. La salida de texto añade entonces · copy of <source dir>/.
No lee ninguna configuración del motor, solo --diagnostics.format y --diagnostics.level. Con --diagnostics.format=json, el campo receipt del resumen indica el recibo que comprobó, y copiedFrom indica el directorio del que se hizo la copia. Ejecútalo como último paso antes de desplegar; consulta Builds y CI.
También falla cuando una ejecución o un restore detenidos dejaron un archivo temporal .afterpack-tmp en el directorio de build. Consulta Una ejecución que se detuvo a medias.
Archivos que el recibo no lista
verify comprueba los archivos que escribió AfterPack, y solo esos. A partir de 1.0.1, su salida de texto también cuenta los demás archivos JavaScript del directorio que el recibo no lista. El recuento es solo informativo y nunca cambia el código de salida. Esos archivos son tuyos: recursos que tu pipeline copia, o la salida de un build que no ejecuta AfterPack. Si un archivo que querías proteger está entre ellos, el build que lo escribió no ejecutó AfterPack.
Varios builds en un mismo directorio
Dos o más builds de AfterPack pueden escribir en el mismo directorio de salida, como una app y una landing page que un proyecto compila en un mismo dist/. A partir de 1.0.1, comparten un único recibo: cada ejecución fusiona en él los archivos que escribió en lugar de reemplazarlo, así que verify dist comprueba los archivos de todos los builds. 1.0.0 reemplazaba el recibo, así que verify solo veía el último build.
afterpack restore [dir]
El deshacer. Antes de escribir nada, una ejecución copia tus originales en .afterpack/backups/<build dir>/ y registra cada archivo que va a crear, modificar, renombrar o eliminar. restore devuelve cada archivo a su nombre original con sus bytes originales, deshace los renombrados y las referencias reescritas, y borra la copia de seguridad y el recibo que escribió la ejecución.
npx afterpack@latest restore # every backup under the project root
npx afterpack@latest restore dist # only the run over dist/Si lo apuntas a un directorio de build, restaura solo las ejecuciones sobre ese directorio, sobre un directorio dentro de él o sobre el build que lo contiene. Si lo apuntas a la raíz del proyecto, restaura todas las copias de seguridad que hay allí. Cuando ninguna copia de seguridad cubre la ruta, falla y lista los directorios que tienen una. Encuentra .afterpack/ a partir de [dir] del mismo modo que una ejecución, y también lee una carpeta .afterpack/backup/ que escribió una versión anterior.
Falla de forma segura (fail closed): cada archivo registrado se comprueba antes de escribir nada, de modo que un archivo que hayas editado o recompilado desde la ejecución detiene la restauración, no se restaura nada, y el comando nombra el archivo y sale con 1. Sin copia de seguridad se niega directamente. Un restore que se detuvo a medias lo termina el siguiente restore. No lee ninguna configuración del motor más allá de --diagnostics.format.
Una ejecución que se detuvo a medias
Una ejecución que falla por un error deshace sus propias escrituras antes de salir. Cada archivo se escribe en un temporal <file>.afterpack-tmp y después se renombra a su sitio, así que una ejecución interrumpida nunca deja un archivo escrito a medias. La copia de seguridad de una ejecución interrumpida queda marcada como inacabada, y la siguiente ejecución de afterpack sobre el mismo directorio comprueba primero la salida:
| La salida del build contiene | La siguiente ejecución |
|---|---|
| Lo que escribió tu bundler y lo que escribió la ejecución detenida, nada más | Falla con salida 1. Ejecuta afterpack restore para dejar la salida como la escribió tu bundler, o recompila desde el código fuente y vuelve a ejecutar. |
| Un build nuevo, o exactamente lo que escribió tu bundler, sin nada de la ejecución detenida | Descarta la copia de seguridad obsoleta con un aviso y se ejecuta. |
| Una mezcla: algunos archivos recompilados y otros que aún contienen la salida de la ejecución detenida | Falla con salida 1 y nombra los archivos recompilados, y restore también se niega. Recompila toda la salida desde el código fuente y vuelve a ejecutar, o copia los originales de los demás archivos desde la carpeta de copia de seguridad que nombra el mensaje. |
Con --build.backup=false no se escribe ninguna copia de seguridad, así que una ejecución interrumpida no se puede deshacer: recompila desde el código fuente. AfterPack no fuerza el volcado de sus escrituras a disco, así que tras un corte de corriente durante una ejecución o un restore, recompila también desde el código fuente.
Ejecuciones en paralelo y bloqueos
Una ejecución y un restore mantienen cada uno el bloqueo de su directorio de build, .afterpack/backups/<build dir>/lock, hasta que terminan. Las ejecuciones sobre directorios de build distintos, como dos apps de un mismo repositorio, van en paralelo. Una segunda ejecución sobre el mismo build, incluido un directorio dentro de él como .next/static/chunks/ mientras se protege .next/, falla con salida 1 y nombra la ejecución que mantiene el bloqueo. Un bloqueo cuyo proceso en esta máquina ya terminó, o que tiene más de dos horas, se toma con un aviso. Si no hay ninguna ejecución en curso y el bloqueo sigue ahí, borra el archivo que nombra el mensaje.
afterpack audit <url>
Escanea un sitio desplegado en busca de secretos filtrados, código fuente expuesto y JavaScript sin proteger, y transmite los hallazgos a medida que avanza el escaneo. No lee ninguna configuración y no envía nada más que la URL. Consulta Auditoría para la referencia completa del comando.
npx afterpack@latest audit example.comSalida legible por máquina
--diagnostics.format=json escribe exactamente un documento JSON en stdout y nada más; cada línea legible por humanos pasa a stderr. Funciona desde el flag, desde AFTERPACK_diagnostics_format=json o desde diagnostics.format en afterpack.json, y verify y audit también lo respetan. Combínalo con diagnostics.level=none para una ejecución que no imprima nada más que el documento. Esta es la forma que los pasos de CI y los agentes de IA deberían leer en lugar de analizar la salida de texto; el paquete también incluye un SKILL.md que documenta la CLI para un agente que la invoque directamente.
npx afterpack@latest dist/ --diagnostics.format=json --diagnostics.level=noneCódigos de salida
| Código | Significado |
|---|---|
0 | Éxito. Cada archivo recopilado fue ofuscado y escrito. También lo devuelven --help y --version. |
1 | Fallo. No se produjo nada utilizable: una ruta que no existe, ningún JavaScript encontrado bajo ella, cualquier error del motor, un verify fallido, un escaneo audit fallido o un build con clave Pro cuya llamada a la nube falló. |
2 | Parcial. Reservado para un build con la cuota agotada; hoy ningún build lo devuelve. |
3 | Límite de tamaño. inflation.max no pudo alcanzar el objetivo de complejidad (DIAG_SIZE_CAP_REACHED). |
4 | Pro necesario. Una entrada de regions eleva la protección por encima de la configuración del programa y necesita una key (DIAG_PRO_DIRECTIVE_REQUIRES_KEY). La CLI no lee directivas del código fuente, así que solo regions llega a esta salida. |
5 | Reflexión. Se detectó un patrón de reflexión en tiempo de ejecución sin reflection.allow (DIAG_REFLECTION_NOT_ACKNOWLEDGED). |
6 | Actualización necesaria. La nube de AfterPack, o este afterpack, necesita un @afterpack/core más reciente. No se escribió nada. |
64 | Uso incorrecto. Un flag o un comando desconocidos, un valor mal formado o un argumento de ruta duplicado. |
Usa paths.exclude para excluir un archivo que debe publicarse intacto.
Un build Pro con una key resuelta falla de forma segura (fail closed). Si la llamada a la nube falla, el build se detiene en lugar de recurrir a la salida del nivel Free. 4 es la salida de un build Free con una entrada de regions que necesita una key; 5 es la salida de un patrón de reflexión sin reconocer en cualquier build, Free o Pro; 2 está reservado para un futuro build con la cuota agotada y hoy ningún build lo devuelve. Consulta Códigos de salida para el contrato completo.
Siguiente
- Configuración: cada opción y las cuatro formas que pueden establecerla.
- Presets: la escalera de cinco peldaños y lo que cuesta cada peldaño.
- Auditoría: la referencia completa de
afterpack audit. - Diagnósticos: el contrato de salida, la regla fail closed y cada código
DIAG_*. - Frameworks: la vía del plugin, mejor cuando existe uno para tu stack.
- Inicio rápido: el camino más corto desde la instalación hasta un Protection Map.