# Обфускація JavaScript усередині Cloudflare Worker

Запускайте AfterPack у власному Cloudflare Worker через @afterpack/wasm, передайте свій ключ Pro, щоб збирати в хмарі AfterPack, і кешуйте кожен результат на вікно ротації.

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

AfterPack працює з Cloudflare Worker двома способами, через той самий пакет і той самий виклик. Без ключа рушій Free працює всередині **вашого** Worker як модуль WebAssembly, який ви встановлюєте з npm. З вашим ключем [Pro](https://www.afterpack.dev/uk/docs/pro) збірка виконується в хмарі **AfterPack** на рушії Pro.

Ця сторінка описує обидва способи, а потім показує, як кешувати результат, щоб кожен ассет збирався один раз на вікно ротації.

## AfterPack у вашому власному Worker

[`@afterpack/wasm`](https://npmjs.com/package/@afterpack/wasm) — це AfterPack, скомпільований у WebAssembly. Це той самий рушій на Rust, який запускають [CLI](https://www.afterpack.dev/uk/docs/cli) і [плагіни для фреймворків](https://www.afterpack.dev/uk/docs/frameworks), зібраний для `wasm32`, а не для архітектури вашої машини.

```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" },
    });
  },
};
```

Це вся інтеграція. Ні виклику `init`, ні прапорця `nodejs_compat`, ні правила wrangler для файлу `.wasm`. Модуль не має імпортів WebAssembly, тож він синхронно інстанціюється під час першого виклику `obfuscate`, а мапа `exports` пакета сама віддає вашому бандлеру точку входу, придатну для Worker. Той самий імпорт працює в Node, де інша точка входу зчитує бінарний файл із диска.

Інтерфейс ідентичний [`@afterpack/core`](https://npmjs.com/package/@afterpack/core): `obfuscate`, `obfuscateAll`, `version`, усі асинхронні й усі кидають виняток на збірці, яку не можна випустити. Конфігурація — той самий об'єкт, описаний на сторінці [Конфігурація](https://www.afterpack.dev/uk/docs/config), зокрема [`preset`](https://www.afterpack.dev/uk/docs/config#preset) і [`seed`](https://www.afterpack.dev/uk/docs/config#seed).

> **Використовуйте `@afterpack/core`, якщо тільки це можливо**
>
> `@afterpack/core` постачає нативний бінарний файл і працює швидше. Беріть `@afterpack/wasm`, коли хост не може його завантажити: Worker, ізольований образ CI, edge-функція.

### Скільки це коштує

Бінарний файл рушія постачається всередині пакета, уже скомпільований.

| | Байти | Розмір |
|---|---:|---|
| Сирий `.wasm` | 4,573,321 | 4.36 MiB |
| Після gzip | 1,307,772 | 1.25 MiB |

Ліміт розміру Worker у Cloudflare — 3 MB у стисненому вигляді на безкоштовному плані і 10 MB на платному, тож рушій вміщується в обидва. Що не вміщується в безкоштовний план, так це *робота*: він обмежує CPU до 10 ms на виклик, а обфускація бандла на 185 KB на ядрі ноутбука займає менш ніж 0.3 секунди на пресеті за замовчуванням [`light`](https://www.afterpack.dev/uk/docs/config) і приблизно 1 секунду на `medium`. Усьому, що виходить за межі демо, потрібен Workers Paid.

Worker, який обфускує той самий ассет на кожен запит, щоразу платить за цей CPU. [Кешування результату на вікно ротації](#cache-the-result-for-a-rotation-window) або виконання цієї роботи у вашій збірці з подальшою роздачею результату тримає рахунок низьким там, де нова форма на кожен запит вам не потрібна.

### Що може і чого не може пакет, який ви хостите

`.wasm`, який ви встановлюєте, виконує захист рівня Free. Трансформації Pro працюють лише на інфраструктурі AfterPack, тож у пакеті немає нічого, що можна розблокувати: ні перевірки ключа, яку можна обійти, ні перемикача, що перетворює розміщену у вас копію на збірку Pro. [`key`](https://www.afterpack.dev/uk/docs/config#key) спрямовує роботу в [хмару](https://www.afterpack.dev/uk/docs/cloud-api); локально він нічого не вмикає.

Опублікований пакет має запис npm provenance — версію пакета, workflow, який його зібрав, і дайджест артефакту, — тож ви можете пов'язати встановлені байти зі збіркою, яка їх створила.

## Pro з вашого власного Worker

Передайте свій [`key`](https://www.afterpack.dev/uk/docs/config#key) у конфігурації того самого виклику `obfuscate()`, наприклад із секрету Worker, і збірка виконається в хмарі AfterPack на рушії 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" },
    });
  },
};
```

Пакет сам надсилає запит на [`POST /v1/obfuscate/batch`](https://www.afterpack.dev/uk/docs/cloud-api), тож Worker ніколи не програмує API вручну. Файли, завеликі для Worker, автоматично переходять у контейнери. Рушій Pro ніколи не потрапляє у ваш Worker: він працює лише на інфраструктурі AfterPack, і саме це є межею між тарифами, описаною вище.

Кожен виклик є тарифікованою збіркою Pro ([скільки коштує Pro](https://www.afterpack.dev/uk/docs/pro#what-pro-costs)) плюс швидким запитом усередині мережі Cloudflare. Можна збирати на кожен запит, але кожна збірка оплачується, тож краще [кешуйте результат на вікно ротації](#cache-the-result-for-a-rotation-window).

Ваш вихідний код залишає ваш Worker і обробляється в хмарі AfterPack, у пам'яті й без збереження, згідно з [Конфіденційністю і обробкою даних](https://www.afterpack.dev/uk/docs/privacy). Виклик [зупиняється з помилкою (fail closed)](https://www.afterpack.dev/uk/docs/diagnostics#fail-closed-no-exceptions), якщо хмара недоступна: він кидає виняток, і нічого незахищеного не публікується. [Довідник Cloud API](https://www.afterpack.dev/uk/docs/cloud-api) містить повний контракт протоколу, а [Режими розгортання](https://www.afterpack.dev/uk/docs/deployment-modes) пояснюють, що змінюється, коли рушій переїжджає.

> **Pro ніколи не постачатиметься як wasm, який ви хостите**
>
> Пакет, який стверджує, що виконує трансформації Pro на вашій власній інфраструктурі, не наш. Перевірте запис npm provenance, перш ніж встановлювати будь-що під цією назвою.

## Кешуйте результат на вікно ротації

Виводьте [`seed`](https://www.afterpack.dev/uk/docs/config#seed) з поточного часового вікна і кешуйте результат під ключем, що містить це вікно. Тоді кожна локація збирає ассет один раз на вікно й віддає його до кінця вікна, і на Free, і на 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;
  },
};
```

Без секрету `AFTERPACK_KEY` значення `key` дорівнює undefined, і той самий Worker збирає на Free всередині вашого Worker. Вихідний файл імпортується як рядок через правило wrangler `Text`, як у [повному розборі](https://www.afterpack.dev/uk/blog/obfuscate-javascript-cloudflare-workers).

Вікно є і в seed, і в ключі кешу, тож кожна локація з тим самим розгортанням віддає ті самі байти протягом вікна, а нове вікно ніколи не читає старий запис. Вихідний `cache-control` веде відлік до кінця вікна, тож браузер ніколи не тримає копію довше. Cache API зберігає записи окремо в кожному дата-центрі й не працює в редакторі дашборду, тож тестуйте через `wrangler dev` або на розгорнутому маршруті.

## Вибір

| Вам потрібно | Використовуйте |
|---|---|
| Обфускація у Worker, нічого не залишає ваш обліковий запис | [`@afterpack/wasm`](https://npmjs.com/package/@afterpack/wasm), ця сторінка |
| Обфускація у звичайній збірці на Node | [`@afterpack/core`](https://www.afterpack.dev/uk/docs/frameworks) або [CLI](https://www.afterpack.dev/uk/docs/cli) |
| Посилений захист [Pro](https://www.afterpack.dev/uk/docs/pro) і [директиви](https://www.afterpack.dev/uk/docs/directives) для окремих ділянок, з Worker | Той самий виклик із [`key`](https://www.afterpack.dev/uk/docs/config#key), який збирає в хмарі AfterPack |
| Нова форма на кожне вікно без збірки на кожного відвідувача | [Кеш на вікно ротації](#cache-the-result-for-a-rotation-window) |

## Далі

- [Режими розгортання](https://www.afterpack.dev/uk/docs/deployment-modes): локальний режим проти хмарного і що зберігає кожен із них.
- [Довідник Cloud API](https://www.afterpack.dev/uk/docs/cloud-api): контракт, який викликає Worker для збірок Pro.
- [Конфігурація](https://www.afterpack.dev/uk/docs/config): кожен ключ, який приймають обидва пакети.
- [Тарифи](https://www.afterpack.dev/uk/docs/tiers): що входить у Free і що додає Pro.
- [Конфіденційність і обробка даних](https://www.afterpack.dev/uk/docs/privacy): що залишає ваш комп'ютер у кожному режимі.
