Skip to content

Extension API: версия и совместимость

Отдельно от версии пакета расширения (manifest.version, semver для каталога и автообновления) и от engines.rynt (совместимость с версией лаунчера).

Два разных «версии»

ПолеФорматНазначение
manifest.versionsemver (0.0.1, 1.2.0)Релиз пакета, каталог, автообновление с маркетплейса
manifest.extensionApiцелое число как строка ("1")Контракт API платформы (реестры, setup, expose, host APIs)
engines.ryntsemver rangeМинимальные возможности лаунчера (NW, пути, модели)

Путать extensionApi с semver пакета или с ^1.0.0 не нужно — в v1 везде "1".

Политика Extension API

  1. Сейчас одна версия API: 1. В SDK: EXTENSION_API_VERSION = 1 (число) или экспорт строки "1" — в манифесте только "1".
  2. Обратная совместимость обязательна: новые версии API (когда появится 2) только добавляют возможности; старые расширения с extensionApi: "1" продолжают работать на хосте с API 2.
  3. Изменений ломающих контракт не планируется — эволюция через новые реестры, опциональные поля манифеста, новые методы host API.
  4. Лаунчер автообновляемый — пользователь получает новый хост; расширения с диска/каталога тоже должны мягко подтягивать новые релизы пакета (см. ниже).

Манифест

json
{
  "extensionApi": "1"
}

Рекомендуется указывать явно. Если поле опущено в тестовых пакетах — хост трактует как "1" с предупреждением в dev.

Не использовать в v1:

json
"extensionApi": "^1.0.0"

При валидации манифеста (сборка / discovery / parseRyntManifest) — нормализация: если значение похоже на semver range, в dev warning и приведение к major 1 (или ошибка только в strict-режиме CI).

Проверка в лаунчере (мягкая)

При старте лаунчер вызывает checkExtensionApi (major API, не semver):

text
required = parseExtensionApiMajor(manifest.extensionApi)  // 1
host     = EXTENSION_API_VERSION                           // 1

if required === host:
  OK
if required < host:
  OK   // старое расширение на новом лаунчере — ожидаемый случай
if required > host:
  SOFT: предупреждение + запись в errors[id] с понятным текстом,
        по умолчанию всё равно попытка load (флаг strictExtensions=false)
СитуацияПоведение по умолчанию
extensionApi: "1", host 1загрузка
поле отсутствуетзагрузка, warn «считаем API 1»
extensionApi: "2", host 1warn + в UI «нужен более новый лаунчер»; не падать всем графом
legacy ^1.0.0warn + трактовать как 1

Жёсткий отказ (strictExtensionApi: true в dev/CI) — опционально, не для продакшн-пользователя.

Нужна ли проверка вообще?

Да, но мягкая: единственный случай, когда она полезна — required > host (расширение собрано под будущий лаунчер). Всё остальное совместимо по политике. Semver-диапазоны для extensionApi не дают этой ясности — от них отказываемся.

Сервис маркетплейса

При публикации:

  • extensionApi обязателен, значение — "1" (или целое ≤ MAX_PUBLISHED_EXTENSION_API).
  • Расширение с extensionApi: "2" при MAX = 1VALIDATION_ERROR (каталог не принимает пакет для несуществующего API).
  • Это жёстче, чем лаунчер: в каталог попадает только то, что осмысленно раздавать.

Автообновление расширений (лаунчер)

Отдельно от проверки API. Используется manifest.version (semver пакета), не extensionApi.

Правила (мягкие)

ПравилоОписание
Источниккаталог (is_latest, downloadUrl) — 05-marketplace-service.md
Сравнениеsemver: semver.gt(remote, local)
Когдапри старте лаунчера
Отключениеsettings.extensionsAutoUpdate === false (переключатель в настройках расширений)
Сбой сетине блокировать запуск; использовать локальные копии
extensionApiпри автообновлении не меняется логика: новый zip того же id должен оставаться на "1" до выхода API 2
Зависимостипосле обновления пакета A перезапуск резолва графа; если сломались deps — warn в настройках

UI

  • Настройки → Расширения: переключатель «Автообновление с маркетплейса».
  • Управление расширениями: блок «Доступны обновления» + «Обновить» / «Обновить все».
  • После автообновления при старте: Callout в настройках расширений (extensionAutoUpdateNotice).

Не в v1 (можно позже)

  • Дельта-патчи; подпись артефактов; откат версии; периодический фоновый poll.

Реализация (чеклист)

  • [x] EXTENSION_API_VERSION = 1 в SDK
  • [x] parseExtensionApiMajor() + normalizeExtensionApiInManifest() + мягкая проверка при загрузке
  • [x] Тестовые vite.config.ts: extensionApi: '1'
  • [x] Документировать в 02-manifest-contract.md
  • [x] extension-marketplace-service: валидация extensionApi при publish
  • [x] Клиент: listExtensionUpdatesAvailable / applyExtensionUpdates / runExtensionAutoUpdateOnStartup
  • [x] UI: переключатель автообновления, список pending updates, ручное обновление
  • [x] extension-build: нормализация extensionApi при emit dist/manifest.json

Когда появится API 2

  1. Поднять EXTENSION_API_VERSION до 2 в SDK.
  2. Хост принимает extensionApi: "1" и "2".
  3. В каталоге разрешить публикацию с "2" для пакетов, которым нужны новые возможности.
  4. Расширения на "1" не трогать, пока не понадобится явно новый контракт.

Это согласовано с автообновлением лаунчера: пользователь обновил лаунчер → старые .ryntextension с "1" работают; обновили расширение с каталога → получили новый функционал того же API 1 через новый version пакета.