@afterpack/core і кожен плагін для фреймворку вже працюють за цим протоколом, щойно налаштовано ключ Pro: автентифікація, потокова передача multipart, повторні спроби, усе це. Читайте цю сторінку, коли підключаєте власну інтеграцію з CI, пишете клієнт поза офіційними пакетами або налагоджуєте відповідь, якої ваш інструментарій не очікував.
Базовий URL і версіонування
Базовий URL: https://api.afterpack.dev/v1/
API версіонується через URL, і v1 є єдиною версією. У її межах поля відповіді додаються, але ніколи не видаляються і не змінюють призначення; несумісна зміна вийшла б як v2 поруч із нею. GET /v1/version повертає розгорнуту версію та середовище, а GET / є перевіркою доступності без автентифікації.
Сумісність клієнтів
Два необов'язкові заголовки запиту; якщо їх немає або вони некоректні, вони ігноруються, а не спричиняють 400. Скрипт на curl або клієнт, старший за ці заголовки, це ніяк не зачіпає:
| Заголовок | Що передає |
|---|---|
X-AfterPack-Client-Version | Встановлену npm-версію @afterpack/core для клієнта, побудованого на ньому. |
X-AfterPack-Client | Пакет, що викликає API, і його версію, наприклад afterpack/0.1.0 або @afterpack/vite/0.1.0. |
Порівняння відбувається лише на сервері, за MAJOR.MINOR.PATCH, без урахування суфікса пререлізу (0.1.0-rc.3 читається як 0.1.0); версію, яку сервер не може розібрати, або відсутність заголовка завжди приймають. Поки мінімальну версію налаштовано, кожна відповідь містить X-AfterPack-Min-Client-Version, тож скрипт може перевірити свій стан, не наражаючись на відмову. Перевірка відбувається одразу після автентифікації ключа, до читання тіла запиту:
- Нижче налаштованого мінімуму:
426 DIAG_CLIENT_UPGRADE_REQUIREDізdetails.minVersion. - Під час періоду виведення з експлуатації:
410 DIAG_API_SUNSET.
За замовчуванням жодне з них не налаштовано. Робочий простір побачить їх лише тоді, коли AfterPack задасть мінімальну версію або дату виведення з експлуатації для цього API.
Відповідь також може містити масив notices верхнього рівня: в успішному batch-summary, у будь-якому тілі помилки та у відповіді 202 телеметрії тарифу Free. Формат {severity, code, message, url?}, не більше 5 елементів, кожне message обмежене 500 символами. Невідоме значення severity трактується як попередження; окрім показу, робити з ним нічого не потрібно.
Автентифікація
Два види облікових даних, і вони не взаємозамінні.
| Облікові дані | Заголовок | До чого дають доступ |
|---|---|---|
| API-ключ Pro | Authorization: Bearer ap_v1_<...> | POST /v1/obfuscate/batch і GET /v1/usage. Ключ сам визначає свій робочий простір, тож ідентифікатор робочого простору не надсилається. |
| Сесія дашборду | Сесія, яку тримає ваш браузер після входу | Усе, що керує обліковим записом: створення, перегляд і відкликання ключів, плани, оплата, учасники, проєкти. Потребує явного workspaceId і ролі в робочому просторі. |
Ключ є обліковими даними для збірки. Він обфускує та звітує про власне використання і не може створити інший ключ, прочитати дані про оплату чи змінити робочий простір. Саме в цьому обмеженні суть: ключ, що лежить у CI, може витрачати ваш ліміт, і нічого більше.
Ключі мають вигляд ap_v1_, за яким іде 40 символів base62. Ключ показується рівно один раз, під час створення. На сервері зберігається лише солений хеш HMAC-SHA256, тож сирий ключ пізніше неможливо відновити, лише відкликати. Створіть ключ у дашборді.
Ендпоінти
POST /v1/obfuscate/batch
Єдиний ендпоінт обфускації. Один виклик дорівнює одній збірці. Результати повертаються потоком у міру завершення кожного файлу.
Запит: multipart/form-data.
| Частина | Обов'язкова | Опис |
|---|---|---|
batch | Так | Маніфест пакета як рядкова JSON-частина. |
| по одній частині на файл | Так | Вихідний код JS під іменем partName, яке оголошує його запис у маніфесті. |
| по одній частині на source map | Необов'язкова | Вхідний source map під іменем sourceMapPartName цього запису. |
Маніфест має такий вигляд:
{
"version": "1",
"files": [
{
"filePath": "assets/app-a1b2c3.js", // echoed back on the result part
"partName": "file0", // names the form part carrying the source
"sourceMapPartName": "map0", // optional
"config": { "regions": [ /* ... */ ] } // optional, per-file
}
],
"config": { "preset": "hard", "seed": 42 }, // batch-level EngineConfig
"git": { "commitSha": "…", "ref": "…" } // optional build attribution
}config рівня пакета є об'єктом конфігурації рушія: кожен ключ зі сторінки Конфігурація, зокрема preset, seed і regions. filePath і git є полями рівня маніфесту; див. структуру маніфесту вище.
filePath не може містити лапки або символи CR/LF. Його дослівно повертають у Content-Disposition частини з результатом, а некоректний шлях відхиляється.
curl https://api.afterpack.dev/v1/obfuscate/batch \
-H "Authorization: Bearer ap_v1_xxx" \
-F 'batch={"version":"1","files":[{"filePath":"dist/app.js","partName":"file0"}],"config":{"preset":"hard"}}' \
-F file0=@dist/app.jsВідповідь: 200 з потоковим тілом multipart/mixed. Частини надходять у міру завершення файлів, тож записуйте кожну на диск, щойно вона прийшла, а не буферизуйте весь пакет:
- По одній частині
obfuscation-resultна файл:{ version, filePath, status, output, outputMap, processingManifest }. У разі помилкиoutputіoutputMapдорівнюютьnull, аprocessingManifest.diagnosticsпояснює причину. - Завершальна частина
batch-summary:{ version, totalFiles, successCount, failureCount, engineVersion, results, totalDurationMs, totalInputBytes, totalOutputBytes }.
Помилка в одному файлі не перериває пакет. Усі інші файли так само надходять потоком, а підсумок показує розподіл.
GET /v1/usage
Використання та ліміт робочого простору. Єдине читання з категорії керування, доступне ключу, щоб завдання CI могло перевірити свій залишок ліміту. Приймає або API-ключ Pro (який сам визначає свій робочий простір), або сесію дашборду разом із ?workspaceId=.
{
"tier": "indie",
"workspace": { "id": "…", "name": "…" },
"allowance": { "remainingMb": 412.5, "totalMb": 500, "resetAt": "…" },
"credits": { "balanceMb": 20 },
"subscription": { "status": "active", "plan": "indie_monthly", "renewsAt": "…", "graceUntil": null, "lapsed": false },
"recentBuilds": [
{
"buildId": "…", "builtAt": "…", "fileCount": 61,
"totalBytes": 3145728, "outputBytes": 9437184,
"meteredMb": 3.0, "allowanceMb": 3.0, "creditMb": 0, "overageMb": 0,
"projectId": "…", "cloudMs": 39120
}
]
}outputBytes і cloudMs дорівнюють null, коли значення невідоме, і ніколи не 0 як заглушка. allowanceMb / creditMb / overageMb показують, як meteredMb розподілено між трьома пулами.
Обмеження
| Обмеження | Значення |
|---|---|
| На файл | 50 MB |
| Файлів у пакеті | 200 |
| Загальний обсяг пакета | 500 MB |
Якщо перевищено будь-яке з них, запит відхиляється повністю з 413 і DIAG_FILE_TOO_LARGE або DIAG_BATCH_TOO_LARGE, ще до будь-якого обліку. Ті самі числа з боку дашборду наведено в розділі API-ключі.
Ліміти запитів
Рахуються в ковзному вікні 60 секунд, окремо для ключа та для IP-адреси клієнта:
| Область | Ліміт |
|---|---|
| На ключ, безкоштовний робочий простір | 10 запитів / хв |
| На ключ, платний робочий простір | 100 запитів / хв |
| На IP-адресу клієнта | 100 запитів / хв |
Перевищення будь-якої з областей повертає 429 з DIAG_RATE_LIMITED і заголовком Retry-After, що дорівнює часу, за який вікно, яке спрацювало, справді звільниться. Якщо саму перевірку ліміту неможливо виконати, запит зупиняється з помилкою (fail closed): 503 DIAG_RATE_CHECK_UNAVAILABLE і Retry-After: 5.
Часовий бюджет збірки
Увесь бандл обфускується в межах одного виклику з лімітом CPU у 120 секунд. Два бюджети реального часу утримують збірку в цьому ліміті.
| Минуло | Що відбувається |
|---|---|
| 45 s | Збирання даних для Protection Map припиняється. Обфускація триває до завершення; збережену мапу позначено як часткову з причиною пропуску deadline. |
| 90 s | Жоден новий файл більше не починається. Для кожного файлу, що залишився, потоком надходить явна помилка DIAG_BUILD_DEADLINE із зазначенням, скільки з N завершено, і потік закривається штатно. |
Якщо ви досягли другого бюджету, розділіть пакет.
Діагностичні коди
Помилки містять стабільний code разом зі статусом HTTP, у тому самому просторі імен DIAG_*, що й власна діагностика рушія. Ті, які має обробляти клієнт:
| Код | Статус | Значення |
|---|---|---|
DIAG_INVALID_KEY | 401 | Ключ відсутній або не розпізнаний. |
DIAG_KEY_REVOKED | 401 | Ключ відкликано. |
DIAG_KEY_EXPIRED | 401 | Термін дії ключа (expiresAt) минув. |
DIAG_KEY_OUT_OF_SCOPE | 403 | Дійсний ключ використано з IP-адреси поза його списком allowedIPs. |
DIAG_CLIENT_UPGRADE_REQUIRED | 426 | Версія клієнта нижча за налаштований мінімум. Містить details.minVersion. Див. Сумісність клієнтів. |
DIAG_API_SUNSET | 410 | Цю версію API виведено з експлуатації. Див. Сумісність клієнтів. |
DIAG_ENTITLEMENT_LAPSED | 402 | Платіж за підписку прострочено. Містить graceUntil і upgradeUrl. |
DIAG_QUOTA_EXHAUSTED | 402 | Ліміт + кредити (+ ліміт перевищення, якщо увімкнено) не покривають цю збірку. Містить remainingMb, requestedMb і reason, що розрізняє «ліміт вичерпано» та «досягнуто ліміту перевищення». |
DIAG_RATE_LIMITED | 429 | Див. ліміти запитів вище. Містить retryAfterSeconds. |
DIAG_RATE_CHECK_UNAVAILABLE | 503 | Перевірка ліміту запитів не вдалася; зупинка з помилкою (fail closed). Повторіть спробу. |
DIAG_INVALID_MANIFEST | 400 | Некоректне тіло multipart, відсутня частина batch або маніфест, що не пройшов валідацію. |
DIAG_FILE_TOO_LARGE | 413 | Один файл понад 50 MB. |
DIAG_BATCH_TOO_LARGE | 413 | Понад 200 файлів або понад 500 MB загалом. |
DIAG_BUILD_DEADLINE | н/д | Для окремого файлу, усередині потоку 200. Часовий бюджет збірки вичерпався до того, як цей файл було розпочато. |
DIAG_ENGINE_FAILURE | н/д | Для окремого файлу, усередині потоку 200. Рушій видав помилку на цьому файлі. |
Останні два не є статусами HTTP. Вони надходять як status: "failure" у частині obfuscation-result усередині успішної відповіді. Клієнт, який перевіряє лише статус HTTP, сприйме частково невдалу збірку за чисту.
Повторні спроби та відступ
| Статус | Повторювати | Поведінка |
|---|---|---|
400, 401, 402, 403, 413 | Ні | Виправте запит. |
429 | Один раз | Дотримуйтеся Retry-After точно, не більше 120 s; другий 429 зупиняє з помилкою (fail closed). |
500 | Ні | Помилка рушія; повідомте про неї. |
503, 504 | Так | 1 s, 4 s, 16 s (три повтори, усього чотири спроби), потім зупинка з помилкою (fail closed). |
Офіційний клієнт також застосовує 120-секундний тайм-аут на весь запит і трактує збої DNS, TLS і скидання з'єднання як недоступність хмари, а не повторює їх безкінечно.
Життєвий цикл ключа з боку клієнта
Створення, обмеження та відкликання ключів є операціями дашборду. Для коду, що викликає цей API, важливі дві особливості поведінки:
- Відкликання посеред збірки. Ключ, відкликаний під час потокової передачі пакета, повертає
401лише на наступний запит; усе, що вже виконується, завершується штатно. - Ключ, обмежений списком IP-адрес, повертає
403 DIAG_KEY_OUT_OF_SCOPE, коли його використовують звідкись іще. Це відрізняється від401, який отримує недійсний або відкликаний ключ, тож клієнт може відрізнити «не той ключ» від «правильний ключ, не те місце».
Пакет, який не вміщається в пули робочого простору, відхиляється повністю до того, як буде відправлено будь-який файл, а пули залишаються незмінними: ніколи не обробляється частково і ніколи не оплачується частково. Це форма правила fail closed на рівні пакета.
Поводження з вихідним кодом
Вихідний код, надісланий на обфускацію, обробляється лише в пам'яті, протягом одного цього запиту, і ніколи не записується на диск, у журнали чи в пошуковий індекс. Журнали містять ідентифікатори запитів, кількість файлів, сумарні обсяги в байтах і діагностичні коди, але ніколи не вміст файлів, імена ідентифікаторів чи значення констант.
Один артефакт живе довше за запит: Protection Map, яка зберігається для кожної збірки для дашборду. Вона містить ваш оригінальний вихідний код, розфарбований за тим, скільки трансформацій зазнав кожен токен. Вона увімкнена за замовчуванням для кожного проєкту, доступна для читання лише учасникам робочого простору-власника, зберігається один рік і в межах останніх 500 збірок на проєкт (залежно від того, що настане раніше), і її можна вимкнути в налаштуваннях проєкту. Докладніше в розділі Конфіденційність і обробка даних.
Далі
- Режими розгортання: яке місце цей протокол посідає порівняно з локальною збіркою Free.
- Pro: що Pro додає понад локальний рушій.
- Конфіденційність і обробка даних: що зберігається, як довго і хто може це читати.
- Тарифи: ліміти в MB, перевищення і що кожен план відкриває в цьому API.
- API-ключі: створення, обмеження, ротація та відкликання облікових даних, якими автентифікується цей API.
- Довідник CLI: офіційний клієнт, який реалізує ці правила.
- Конфігурація: конфігурація рушія, яку передає поле
config.