Ваш модуль в оболочке песочницы
Ваш модуль — это remote федерации. По-настоящему он работает только тогда, когда его загружает оболочка, и для вашего модуля это оболочка песочницы: тот же образ Building OS, развёрнутый отдельно и привязанный к вашей песочнице. В продуктивную оболочку ваш модуль не попадает никогда: код модуля выполняется с полным доверием той оболочки, которая его загрузила, поэтому стороннему коду выделяется собственная.
Начиная с 1.14.0
tv-sdk sandbox install и скрипты dev:remote / serve:remote требуют @tv/extension-sdk 1.14.0 или новее. У модуля, созданного версией 1.13.0 или более ранней, этих скриптов нет — пересоздайте каркас или добавьте их вручную, как показано в шаге 1.
1. Отдавайте remote со своей машины
@originjs/vite-plugin-federation создаёт remoteEntry.js при сборке, а не в dev-сервере. Поэтому pnpm dev отдаёт приложение вообще без remote, и оболочка, нацеленная на него, получает 404, который браузер показывает как ошибку CORS. Два терминала, две команды:
# терминал 1 — пересборка при каждом изменении
pnpm dev:remote # vite build --watch
# терминал 2 — отдача собранного
pnpm serve:remote # vite preview --port 6001 --strictPortВаш remote будет здесь:
http://localhost:6001/assets/remoteEntry.js6001 — закреплённый в каркасе порт preview (порт dev плюс 1000). strictPort задан намеренно: без него vite перескочит на следующий свободный порт и уведёт remote из-под записи реестра, которая на него ссылается.
Если в вашем каркасе нет dev:remote / serve:remote, их эквиваленты — vite build --watch и vite preview --port 6001 --strictPort плюс preview: { port: 6001, strictPort: true, cors: true } в vite.config.ts.
2. Установите модуль в свою песочницу
npx @tv/extension-sdk sandbox install module-manifest.json \
--sandbox=sbx_... \
--remote-entry=http://localhost:6001/assets/remoteEntry.jsДва вызова к tv-api вашей собственной песочницы, по порядку:
POST /api/v1/registry/modules/ingestс вашим манифестом и URL remote — это та запись реестра, из которой рисует оболочка.POST /api/v1/licensesна наполненное здание: оболочка рисует из состояния лицензий, поэтому зарегистрированный, но нелицензированный модуль просто отсутствует. Уже существующая лицензия отвечает409, и команда считает это успехом — её безопасно запускать повторно.
--sandbox=<id> за один запрос находит URL API, токен, наполненное здание и console URL. Без него используется пара TV_API_URL / TV_API_TOKEN, которую печатает tv-sdk sandbox connect.
Обоим эндпоинтам нужны права администратора платформы — и внутри своей песочницы они у вас есть: tv-api песочницы принимает собственный токен как администратора этого одного API и этой одной базы, и ничего больше (tv-platform#795). Если вам попалась старая заметка в SDK о том, что этот вызов ожидаемо падает с 401/403, она написана до этого изменения.
Смена адреса, с которого вы отдаёте remote, — это повторный запуск команды, а не пересборка оболочки.
3. Откройте
<хост вашей песочницы>/sandbox/<slug вашего модуля>Хост песочницы — это consoleUrl в записи песочницы (тот же хост, что и apiUrl: оболочка отдаётся на /, API остаётся на /api и /socket.io, поэтому UI и бэкенд у них общий origin). consoleUrl равен null, если для вашей песочницы оболочка не развёрнута.
Оболочка песочницы монтирует один общий маршрут /sandbox/:slug, который ведут поля remoteEntryUrl, federationName и exposeName из вашей записи реестра; маршруты /<slug> продуктивной оболочки тут ни при чём.
4. Войдите
Оболочка песочницы аутентифицируется в реалме Keycloak sandboxes. Самостоятельной регистрации там нет: учётную запись в этом реалме создаёт для вас команда Tango Vision, проставляя в клейм tenant_id значение organizationId вашей песочницы. Напишите на developers@tango.vision и укажите organizationId из записи песочницы.
Чего ожидать, а чего нет
- Chrome или Firefox, не Safari. Safari блокирует загрузку скрипта с
http://localhostна страницу с публичного HTTPS-origin. Chrome при первом запуске может спросить разрешение на доступ к локальной сети — разрешите. - Берите токен из контекста платформы, а не из
localStorage['access_token']. В оболочке песочницы мост к старому ключу выключен: оболочка, зеркалящая живой access-токен в ключ, читаемый любым скриптом, отдавала бы его чужому JavaScript. Модули, читающие этот ключ напрямую, в оболочке песочницы не аутентифицируются; модули, берущие токен изPlatformContextSDK, работают как обычно. См. PlatformContext. - Remote загрузится, только если его пропустят два независимых барьера — CSP браузера и собственный список разрешённых origin у оболочки. В оболочке песочницы оба настроены пропускать
localhost. Remote с другого адреса требует изменения обоих, и это запрос к нам, а не настройка на вашей стороне. - Лицензионной проверки на этом маршруте нет, но вход всё равно нужен: маршрут находится внутри защищённой области оболочки.
- Бэкенд вашего модуля всё это не разворачивает.
installрегистрирует и лицензирует фронтенд-remote. Бэкенд работает там, где вы его запустили, и обращается к tv-api песочницы по сети, как любой другой клиент.
Правила загрузки модуля с localhost
Всё сказанное выше в виде списка. Если страница пустая, одно из этих условий не выполнено.
- С localhost загружает только песочная оболочка. Content-Security-Policy рабочей оболочки не допускает
http://localhost, а универсальный маршрут модулей в ней выключен. Песочная оболочка допускает loopback-адреса и открывает любой модуль из реестра вашей песочницы по адресу/sandbox/<slug>. - Только loopback. Допускаются
http://localhost:<порт>иhttp://127.0.0.1:<порт>. Адрес в локальной сети, туннель или любой другой хост не допускаются. - Отдавайте сборку, а не dev-сервер.
vite devне создаётassets/remoteEntry.js: federation-remote существует только в сборке. Запуститеpnpm dev:remoteиpnpm serve:remoteодновременно. - Адрес фиксирован:
http://localhost:6001/assets/remoteEntry.js. Preview работает соstrictPort, поэтому занятый порт даёт ошибку, а не тихий переход на другой, и с включённым CORS. - Запись в реестре должна указывать на него.
tv-sdk sandbox installс параметром--remote-entry=http://localhost:6001/assets/remoteEntry.jsрегистрирует манифест в вашей песочнице и выдаёт лицензию на созданное здание. Slug в нижнем регистре через дефис, не длиннее 64 символов; remote экспортирует./Shell. - Chrome или Firefox, не Safari. Оболочка открыта по HTTPS и загружает скрипт с HTTP loopback; Safari это блокирует. Chrome может один раз запросить доступ к устройствам в локальной сети: разрешите.
- Модуль загружается только в вашем браузере.
localhostэто ваш компьютер. Коллега, открыв тот же адрес, получит ошибку загрузки. Чтобы показать модуль другому человеку, разместите собранный бандл на хосте, который допускает песочная оболочка, и попросите нас разрешить этот хост. - Авторизация только через SDK, не через
localStorage['access_token']. Песочная оболочка не записывает туда токен. React-контекст не пересекает границу federation, поэтому внутри remote используйте работающие там хуки, напримерuseSelectedBuildingId(). - Ваш бэкенд работает на вашем компьютере. Политика оболочки позволяет фронтенду обращаться к
http://localhost:<порт>, поэтому локальный бэкенд доступен из браузера. Платформа до него не дотянется: у песочницы нет исходящего доступа в интернет и маршрута к вашему компьютеру, поэтому Copilot не сможет вызвать MCP-эндпоинт на localhost. i18nextиreact-i18nextв shared либо оба, либо ни одного, и сохраняйте aliasuse-sync-external-store/shimиз шаблона. Нарушение проявляется только внутри оболочки, где ваш CI его не видит.
Если страница пустая
| Что вы видите | Обычная причина |
|---|---|
404 на remoteEntry.js, показанный как CORS | Не запущен pnpm dev:remote, либо запущен pnpm dev — dev-сервер remote не создаёт |
| Remote грузится с другого порта | Нет strictPort: vite сменил порт, а запись реестра по-прежнему указывает на 6001 |
| Пустой маршрут, запроса за remote нет | Модуль зарегистрирован, но не лицензирован — запустите sandbox install, он делает оба шага |
| Вход выполнен, но все вызовы API дают 401 | Модуль читает localStorage['access_token']; перейдите на контекст платформы |
| Падение из-за второй копии React при монтировании | Нет алиаса use-sync-external-store/shim; см. Федерация и оболочка |