# Ofusca JavaScript dentro de un Cloudflare Worker

Ejecuta AfterPack en tu propio Cloudflare Worker con @afterpack/wasm, pasa tu clave Pro para hacer el build en la nube de AfterPack y guarda cada resultado en caché por ventana de rotación.

Source: https://www.afterpack.dev/es/docs/workers

AfterPack se ejecuta desde un Cloudflare Worker de dos maneras, con el mismo paquete y la misma llamada. Sin clave, el motor Free se ejecuta dentro de **tu** Worker, como un módulo WebAssembly que instalas desde npm. Con tu clave [Pro](https://www.afterpack.dev/es/docs/pro), el build se ejecuta en la nube **de AfterPack** con el motor Pro.

Esta página cubre las dos y después cómo guardar el resultado en caché para que cada recurso se construya una vez por ventana de rotación.

## AfterPack, dentro de tu propio Worker

[`@afterpack/wasm`](https://npmjs.com/package/@afterpack/wasm) es AfterPack compilado a WebAssembly. Es el mismo motor en Rust que ejecutan la [CLI](https://www.afterpack.dev/es/docs/cli) y los [plugins de framework](https://www.afterpack.dev/es/docs/frameworks), compilado para `wasm32` en lugar de para la arquitectura de tu máquina.

```bash
npm install @afterpack/wasm
```

```javascript
import { obfuscate } from "@afterpack/wasm";

export default {
  async fetch(request) {
    const source = await request.text();
    const result = await obfuscate({ path: "app.js", source }, { preset: "hard" });
    return new Response(result.bytes, {
      headers: { "content-type": "application/javascript" },
    });
  },
};
```

Esa es toda la integración. Sin llamada a `init`, sin flag `nodejs_compat`, sin regla de wrangler para el archivo `.wasm`. El módulo no tiene imports de WebAssembly, así que se instancia de forma síncrona la primera vez que llamas a `obfuscate`, y el mapa `exports` del paquete le entrega a tu bundler por sí solo una entrada adaptada a Workers. El mismo import funciona en Node, donde otra entrada lee el binario desde disco.

La superficie es idéntica a la de [`@afterpack/core`](https://npmjs.com/package/@afterpack/core): `obfuscate`, `obfuscateAll`, `version`, todas asíncronas y todas lanzan una excepción ante un build que no se puede publicar. La configuración es el mismo objeto documentado en [Configuración](https://www.afterpack.dev/es/docs/config), incluidos [`preset`](https://www.afterpack.dev/es/docs/config#preset) y [`seed`](https://www.afterpack.dev/es/docs/config#seed).

> **Usa `@afterpack/core` salvo que no puedas**
>
> `@afterpack/core` incluye un binario nativo y es más rápido. Recurre a `@afterpack/wasm` cuando el host no pueda cargarlo: un Worker, una imagen de CI aislada, una función edge.

### Lo que te cuesta

El binario del motor viene dentro del paquete, ya compilado.

| | Bytes | Tamaño |
|---|---:|---|
| `.wasm` sin comprimir | 4,573,321 | 4.36 MiB |
| Con gzip | 1,307,772 | 1.25 MiB |

El límite de tamaño de un Worker en Cloudflare es de 3 MB comprimidos en el plan gratuito y 10 MB en el de pago, así que el motor cabe en ambos. Lo que no cabe en el plan gratuito es el *trabajo*: limita la CPU a 10 ms por invocación, y ofuscar un bundle de 185 KB en un núcleo de portátil lleva menos de 0.3 segundos con el preset [`light`](https://www.afterpack.dev/es/docs/config) por defecto y alrededor de 1 segundo con `medium`. Cualquier cosa más allá de una demo necesita Workers Paid.

Un Worker que ofusca el mismo recurso en cada petición paga esa CPU cada vez. [Guardar el resultado en caché durante una ventana de rotación](#cache-the-result-for-a-rotation-window), o hacer el trabajo en tu build y servir la salida, mantiene la factura baja cuando no buscas una forma nueva en cada petición.

### Qué puede y qué no puede hacer el paquete que alojas

El `.wasm` que instalas aplica protección Free. Las transformaciones Pro se ejecutan solo en la infraestructura de AfterPack, así que el paquete no contiene nada que desbloquear: ninguna comprobación de clave que saltarse, ningún interruptor que convierta una copia alojada en un build Pro. Una [`key`](https://www.afterpack.dev/es/docs/config#key) dirige el trabajo a [la nube](https://www.afterpack.dev/es/docs/cloud-api); no activa nada en local.

El paquete publicado lleva un registro de procedencia de npm (versión del paquete, el workflow que lo compiló y el digest del artefacto), para que puedas relacionar los bytes que instalaste con el build que los produjo.

## Pro, desde tu propio Worker

Pasa tu [`key`](https://www.afterpack.dev/es/docs/config#key) en la configuración de la misma llamada a `obfuscate()`, por ejemplo desde un secreto del Worker, y el build se ejecuta en la nube de AfterPack con el motor Pro:

```javascript
import { obfuscate } from "@afterpack/wasm";

export default {
  async fetch(request, env) {
    const source = await request.text();
    const result = await obfuscate(
      { path: "app.js", source },
      { preset: "hard", key: env.AFTERPACK_KEY },
    );
    return new Response(result.bytes, {
      headers: { "content-type": "application/javascript" },
    });
  },
};
```

El paquete hace la petición a [`POST /v1/obfuscate/batch`](https://www.afterpack.dev/es/docs/cloud-api) por ti, así que un Worker nunca programa la API a mano. Los archivos demasiado grandes para un Worker pasan a contenedores automáticamente. El motor Pro nunca entra en tu Worker: se ejecuta solo en la infraestructura de AfterPack, que es la frontera entre niveles descrita arriba.

Cada llamada es un build Pro medido ([lo que cuesta Pro](https://www.afterpack.dev/es/docs/pro#what-pro-costs)) más una petición rápida dentro de la red de Cloudflare. Puedes hacer un build en cada petición, pero cada una se factura, así que mejor [guarda el resultado en caché durante una ventana de rotación](#cache-the-result-for-a-rotation-window).

Tu código fuente sale de tu Worker y se procesa en la nube de AfterPack, en memoria y sin conservarse, según [Privacidad y manejo de datos](https://www.afterpack.dev/es/docs/privacy). La llamada [falla de forma segura (fail closed)](https://www.afterpack.dev/es/docs/diagnostics#fail-closed-no-exceptions) si no se puede llegar a la nube: lanza un error y no se publica nada sin proteger. La [referencia de la API en la nube](https://www.afterpack.dev/es/docs/cloud-api) es el contrato de comunicación completo, y [Modos de despliegue](https://www.afterpack.dev/es/docs/deployment-modes) explica qué cambia cuando el motor se traslada.

> **Pro nunca se distribuirá como un wasm que alojes tú**
>
> Un paquete que afirme ejecutar transformaciones Pro en tu propia infraestructura no es nuestro. Comprueba el registro de procedencia de npm antes de instalar nada con este nombre.

## Guarda el resultado en caché durante una ventana de rotación

Deriva la [`seed`](https://www.afterpack.dev/es/docs/config#seed) de la ventana de tiempo actual y guarda el resultado en caché con una clave que incluya la ventana. Así cada ubicación construye un recurso una vez por ventana y lo sirve hasta que la ventana termina, tanto en Free como en Pro:

```javascript
import { obfuscate } from "@afterpack/wasm";
import source from "./pricing.client.js";

const WINDOW_SECONDS = 600;

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    if (url.pathname !== "/pricing.js") {
      return new Response("Not found", { status: 404 });
    }

    const now = Math.floor(Date.now() / 1000);
    const slot = Math.floor(now / WINDOW_SECONDS);
    const secondsLeft = (slot + 1) * WINDOW_SECONDS - now;
    const cacheKey = new Request(`${url.origin}/pricing.js?slot=${slot}`);

    let response = await caches.default.match(cacheKey);
    if (!response) {
      const result = await obfuscate(
        { path: "pricing.js", source },
        { preset: "hard", seed: `pricing.js:${slot}`, key: env.AFTERPACK_KEY },
      );
      response = new Response(result.bytes, {
        headers: {
          "content-type": "application/javascript",
          "cache-control": `public, max-age=${secondsLeft}`,
        },
      });
      ctx.waitUntil(caches.default.put(cacheKey, response.clone()));
    }

    response = new Response(response.body, response);
    response.headers.set("cache-control", `public, max-age=${secondsLeft}`);
    return response;
  },
};
```

Sin un secreto `AFTERPACK_KEY`, `key` queda en undefined y el mismo Worker hace el build en Free dentro de tu Worker. El archivo fuente se importa como cadena mediante una regla `Text` de wrangler, como en [el recorrido completo](https://www.afterpack.dev/es/blog/obfuscate-javascript-cloudflare-workers).

La ventana está tanto en la seed como en la clave de caché, así que cada ubicación que ejecuta el mismo despliegue sirve los mismos bytes durante una ventana, y una ventana nueva nunca lee la entrada anterior. El `cache-control` de salida cuenta hacia atrás hasta el final de la ventana, así que un navegador nunca conserva una copia más allá de ella. La Cache API guarda las entradas por centro de datos y no tiene efecto en el editor del dashboard, así que pruébala con `wrangler dev` o con una ruta desplegada.

## Cómo elegir

| Quieres | Usa |
|---|---|
| Ofuscación en un Worker, sin que nada salga de tu cuenta | [`@afterpack/wasm`](https://npmjs.com/package/@afterpack/wasm), esta página |
| Ofuscación en un build normal de Node | [`@afterpack/core`](https://www.afterpack.dev/es/docs/frameworks) o la [CLI](https://www.afterpack.dev/es/docs/cli) |
| Refuerzo [Pro](https://www.afterpack.dev/es/docs/pro) y [directivas](https://www.afterpack.dev/es/docs/directives) por región, desde un Worker | La misma llamada con una [`key`](https://www.afterpack.dev/es/docs/config#key), que hace el build en la nube de AfterPack |
| Una forma nueva por ventana sin un build por visitante | La [caché por ventana de rotación](#cache-the-result-for-a-rotation-window) |

## Siguiente

- [Modos de despliegue](https://www.afterpack.dev/es/docs/deployment-modes): local frente a nube, y qué almacena cada uno.
- [Referencia de la API en la nube](https://www.afterpack.dev/es/docs/cloud-api): el contrato al que llama un Worker para builds Pro.
- [Configuración](https://www.afterpack.dev/es/docs/config): cada clave que aceptan ambos paquetes.
- [Niveles](https://www.afterpack.dev/es/docs/tiers): qué incluye Free y qué añade Pro.
- [Privacidad y manejo de datos](https://www.afterpack.dev/es/docs/privacy): qué sale de tu máquina en cada modo.
