Режим без хоста

Авторитетное руководство по контракту проектирования с учётом автономного режима, который позволяет каждому приложению-микрофронтенду и веб-компоненту Wippy собираться, запускаться и тестироваться без оборачивающего их Wippy Web Host.

Состояние инъекций по умолчанию: оверлей разработчика стартует с отключёнными themeConfig, primevue, markdown и iframe, но с включёнными customCss и customVariables. Поэтому приложение, полагающееся только на пользовательские переопределения, может выглядеть работающим, тогда как приложение, ожидающее переменные темы платформы или стили PrimeVue, будет отрисовано без стилей, пока вы не включите эти инъекции. Откройте FAB оверлея → включите нужные инъекции → отметьте «Auto-accept on reload», чтобы сохранить их между перезагрузками.


Содержание


Ментальная модель — приложения и WC намеренно рассчитаны на автономный режим

Каждое приложение-микрофронтенд и веб-компонент Wippy построены вокруг небольшого намеренного ограничения:

Контракт времени выполнения — это поверхность proxy API. И ничего больше.

Что это значит на практике:

  • Единственное, к чему приложение или WC обращается во время выполнения, — поверхность proxy API: синхронные геттеры, импортируемые из @wippy-fe/proxy (host, api, on, config, state, ws, logger). И приложения, и WC используют одни и те же импорты; под капотом они разрешаются в один и тот же ProxyApiInstance, который среда исполнения устанавливает как внутренние глобальные переменные (window.$W, window.__WIPPY_APP_API__ — никогда не читайте их напрямую).
  • Приложения и WC не импортируют код из соседних приложений, из Lua-части родительского модуля, из Wippy Web Host или из другого модуля проекта. Они живут в собственной папке. Vite выводит каждую внешнюю зависимость Rollup из закреплённого import-map.json целевого хоста; package.json объявляет только npm-зависимости и корни peer-зависимостей, которые артефакт действительно импортирует.
  • Один и тот же app.ts (или index.ts для WC) корректно загружается в двух окружениях:
    1. С хостом — внутри Wippy Web Host, который внедряет proxy.js, AppConfig, importmap и CSS.
    2. Без хоста — при запуске app.html напрямую через dev-сервер Vite, file://, страницу модульных тестов, песочницу в стиле Storybook и т. д.

Каждое приложение/WC можно воспринимать как «небольшую программу с крошечной стандартизированной поверхностью ввода-вывода». Хост — одна из возможных сред исполнения; автономный режим — другая. Код приложения не знает, в какой из них он находится.

Это не случайность и не запоздалая мысль. Именно это делает возможным:

  • Локальную FE-итерацию без поднятия полного бэкенда Wippy.
  • Модульное тестирование WC в изоляции под vitest + jsdom.
  • Совместное использование приложений между модулями Wippy — каждое приложение-микрофронтенд и веб-компонент собираются одним и тем же инструментарием независимо от того, какой модуль их поставляет.
  • Жизнеспособность оверлеев под конкретного заказчика — операторы правят метаданные (темизацию, importmap, окружение) без пересборки FE-бандла.

Точка переключения @wippy/scripts — один тег, два пути загрузки

app.html каждого канонического приложения поставляется с одним тегом script, который решает путь загрузки во время загрузки страницы:

Это сокращённый пример body/загрузки. Вставьте полный валидный ответ import map, описанный в разделе Алгоритм снимка import map, обновляя его при смене закреплённого тега Web Host.

<!-- URL ДОЛЖЕН включать сегмент с тегом релиза: https://web-host.wippy.ai/<release-tag>/dev-proxy.js -->
<script
    src="https://web-host.wippy.ai/<release-tag>/dev-proxy.js"
    data-role="@wippy/scripts"
></script>

Полный каркас app.html — в разделе Приложение-микрофронтенд.

Два атрибута этого единственного тега несут весь контракт двойного режима:

Атрибут Роль Кем используется
data-role="@wippy/scripts" Маркер для хоста. При его наличии хост удаляет этот элемент <script> перед раздачей iframe и внедряет собственные loading.js + proxy.js + importmap + AppConfig перед маркером. В режиме с хостом элемент исчезает. Wippy Web Host
src="…/dev-proxy.js" Запасной URL. Используется, когда хоста нет: браузер загружает dev-proxy.js напрямую, и этот скрипт запускает страницу. В режиме с хостом атрибут src= не имеет значения (элемента <script> больше нет). Автономная загрузка в браузере

Выбирайте URL, соответствующий вашему окружению. Обратите внимание: URL Web Host всегда требует сегмента с тегом релиза в пути — /dev-proxy.js прямо от корня хоста НЕ валиден; вы должны адресовать конкретную сборку (/<release-tag>/dev-proxy.js). Это гарантирует, что каждая загрузка в режиме разработки закреплена за известным воспроизводимым бандлом, и исключает сюрпризы класса «CDN хоста обновился ночью, и мой предпросмотр сломался».

Окружение Пример значения src=
Публичный CDN (стандартно) https://web-host.wippy.ai/<release-tag>/dev-proxy.js
Самостоятельно развёрнутый Wippy https://<your-wippy-host>/<release-tag>/dev-proxy.js

Тег должен совпадать с версией релиза, используемой в fe_facade_url фасада. Закрепляйте его явно — /dev-proxy.js без сегмента тега невалиден. Один и тот же бандл работает для локальной итерации, CI и ссылок предпросмотра, которыми можно поделиться.

Таким образом, одна и та же строка HTML является и якорем «внедрите свои скрипты здесь» для хоста, и запасной загрузкой без хоста — без какой-либо условной логики.

Что попадает в importmap?

Загрузите полную карту один раз во время разработки, используя тот же тег, что и fe_facade_url и dev-proxy.js:

curl.exe -fsS "https://web-host.wippy.ai/<release-tag>/import-map.json" -o import-map.json

Установите текст элемента <script type="importmap"> в app.html равным полученному JSON-ответу дословно. Не помещайте комментарии, многоточия-заполнители или написанные вручную подстановки внутрь этого JSON. Контракт сборки и зависимостей определяет требования к снимку и происхождению; полученный ответ релиза предоставляет точный объект imports.

Соглашения:

  • Помещайте каждый полученный ключ во внешние зависимости Rollup, включая пока неиспользуемые ключи.
  • Держите тот же полный объект ключ/значение в app.html; не реконструируйте его через esm.sh.
  • Включайте импортируемый спецификатор в бандл только когда его точного ключа нет.
  • Перезагружайте карту при смене тега Web Host или при добавлении новой зависимости, чтобы проверить, может ли этот конкретный спецификатор быть внешним.

Автономный app.html разрешает полную скопированную карту. Режим с хостом использует карту, доставляемую тем же закреплённым релизом.

Предоставление package.json для dev-proxy (канонический каркас)

package.json каждого приложения Wippy несёт метаданные, определяющие значения по умолчанию во время выполнения: proxy-инъекции (wippy.proxy.injections.css.*), переопределения темизации на страницу (wippy.configOverrides.customization), коллекции иконок iconify и т. д. В режиме с хостом хост читает их из реестра. В режиме без хоста dev-proxy нужны те же данные, чтобы применить те же значения по умолчанию.

Канонический паттерн — wippyPagePlugin() из согласованного текущего семейства @wippy-fe/vite-plugin (0.0.46 на момент публикации), добавляемый один раз в ваш vite.config.ts. Плагин читает ваш package.json на этапе сборки и делает две вещи:

  1. Разрешает ссылки file:// в блоке wippy (любое строковое значение вида "file://<relative>" заменяется содержимым указанного файла в UTF-8 — см. соглашение об именовании *.do-not-link.<ext> в build-system.md).
  2. Выдаёт два результата с разрешённым JSON:
    • Внедряемый в <head> <script type="application/json" data-role="@wippy/package"> для загрузки без хоста / через dev-proxy.
    • wippy-meta.json в фактическом каталоге вывода Vite для режима с хостом Wippy.
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { wippyPagePlugin } from '@wippy-fe/vite-plugin'

export default defineConfig({
  plugins: [
    vue(),
    wippyPagePlugin(),
  ],
  // …
})

Для веб-компонентов (view.component, только ESM — нет HTML-точки входа, куда можно внедрять) используйте wippyComponentPlugin() из того же пакета. Он лишь выдаёт wippy-meta.json в фактическом каталоге вывода; шага transformIndexHtml нет.

// vite.config.ts для веб-компонента
import { wippyComponentPlugin } from '@wippy-fe/vite-plugin'
export default defineConfig({ plugins: [wippyComponentPlugin()] })

wippyPackagePlugin остаётся устаревшим псевдонимом для совместимости. Новый код страниц использует wippyPagePlugin(); сборки только компонентов используют wippyComponentPlugin().

Плагин выдаёт следующее в начало <head> собранного app.html:

<script type="application/json" data-role="@wippy/package">
{ "name": "@wippy/your-app", "version": "1.0.0", "wippy": { "proxy": {...}, "configOverrides": {...} } }
</script>

dev-proxy.js читает это синхронно при загрузке через document.querySelector('script[data-role="@wippy/package"]') и использует wippy.proxy.injections для инициализации значений по умолчанию конфигурации прокси, а wippy.configOverrides.customization — для инициализации appConfig.theming.global. Строка data-role @wippy/package экспортируется как WIPPY_PACKAGE_DATA_ROLE из @wippy-fe/shared, поэтому обе стороны границы используют одну константу.

Почему такая форма:

  • Никакого дублирования. package.json — единственный источник истины: плагин читает его на этапе сборки, ничто в вашем src/ на него не ссылается.
  • Никакого запроса. Данные встроены в раздаваемый HTML — читаются синхронно dev-proxy.js до запуска любого кода приложения.
  • Правильный порядок. Внедряются в начало <head> перед любым тегом script, поэтому уже находятся в DOM к моменту выполнения dev-proxy (dev-proxy — синхронный UMD-скрипт; модульные скрипты отложены и выполняются позже).
  • Никакой правки app.html. Шаблон остаётся чистым; внедрением владеет плагин.
  • Константа из общего пакета. Строка '@wippy/package' живёт ровно в одном месте (@wippy-fe/sharedWIPPY_PACKAGE_DATA_ROLE); приложения не ссылаются на неё напрямую, и dev-proxy, и плагин импортируют её оттуда.
  • Аккуратно игнорируется под реальным хостом. processWebPage хоста читает package.json из реестра на стороне сервера; встроенный JSON-тег — безобидные метаданные.

dev-proxy читает JSON во время resolveDevConfig() и использует его для заполнения значений по умолчанию оверлея разработчика. Если тег script отсутствует (старое приложение, плагин ещё не добавлен), dev-proxy откатывается к getDefaultProxyConfig(). Поэтому добавление плагина чисто аддитивно — приложения без него продолжают работать с общими значениями по умолчанию.

Почему плагин, а не глобальная переменная window во время выполнения? Dev-proxy.js — немодульный синхронный скрипт, выполняющийся рано, во время разбора <head>, до загрузки любого модульного скрипта (включая ваш app.ts). Поэтому app.ts не может установить глобальную переменную до того, как dev-proxy её прочтёт. Трансформация HTML на этапе сборки размещает данные в DOM заранее, и они доступны в момент выполнения dev-proxy.

Почему один тег, а не два? Второй блок <script> (например, if (!window.__WIPPY__) load dev-proxy) выполнился бы только после завершения внедрения хоста; если маркера уже нет, условию не к чему привязаться. Паттерн с одним тегом означает, что маркер всегда присутствует в исходном HTML, а задача хоста — ровно «удалить этот маркер и заменить его». Автономный случай происходит именно тогда, когда никто его не удалил.

Контракт хоста требует, чтобы HTML-файл, указанный в wippy.path, ОБЯЗАТЕЛЬНО содержал элемент <script type="text/javascript" data-role="@wippy/scripts">, куда будут автоматически внедряться дополнительные скрипты.

Канонические приложения app-template поставляются с заполненным src="…/dev-proxy.js". Это рекомендуемая форма: всегда включайте запасной src=, если только ваше приложение не может работать без хоста (редко и требует обоснования).


Что на самом деле делает dev-proxy.js

dev-proxy.js — бандл загрузки без хоста, раздаваемый с CDN Wippy Web Host по адресу https://web-host.wippy.ai/<release-tag>/dev-proxy.js.

Его задача — сделать так, чтобы геттеры @wippy-fe/proxy корректно разрешались без какого-либо хоста, устанавливая те же внутренние глобальные переменные (window.$W, window.__WIPPY_APP_API__), что установил бы настоящий хост. Код приложений и WC никогда не трогает эти глобальные переменные; он просто импортирует из @wippy-fe/proxy, и геттеры работают. dev-proxy делает это примерно в пять шагов:

  1. Устанавливает защиту истории (installHistoryGuard()) — подменяет pushState / replaceState, чтобы vue-router не пытался менять историю браузера вне контекста iframe-srcdoc.
  2. Разрешает конфигурацию (resolveDevConfig() в src/proxy/dev/resolve-dev.ts):
    • Читает localStorage['@wippy-dev/config'] и localStorage['@wippy-dev/proxy-config'].
    • Если localStorage['@wippy-dev/auto-accept'] === 'true' И сохранённая конфигурация существует → использует её немедленно, отрисовывая оверлей в режиме мониторинга.
    • Иначе → отрисовывает оверлей в режиме ожидания (FAB пульсирует синим, всплывающая подсказка «Accept config to continue loading») и блокирует загрузку, пока разработчик не нажмёт Accept.
  3. Собирает поддельный ProxyApiInstance, связанный с:
    • Принятым ChildAppConfig (то, что возвращает config из @wippy-fe/proxy).
    • Эмиттером nanoevents для подписок on(...) и симуляций @history / @visibility.
    • Заглушками host, которые логируют в консоль каждый метод (createDevHostAPI() в src/proxy/dev/host-stubs.ts).
    • Реальным экземпляром axios, стоящим за api из @wippy-fe/proxy, настроенным на URL, введённый разработчиком (env.APP_API_URL по умолчанию равен ${location.origin}/api).
    • Заглушками logger / state / ws, повторяющими форму продуктового прокси.
  4. Применяет внедрение CSS согласно выбранной разработчиком конфигурации прокси:
    • themeConfig: true → внедряет theme-config.css из @wippy-fe/theme.
    • iframe, primevue, markdown → то же самое, встроенные CSS-бандлы из src/proxy/dev/css-inline.ts.
    • customCss / customVariables → применяет appConfig.theming.global.customCSS / cssVariables (включая блоки @dark/@light, описанные в micro-frontend-app-theming.md).
  5. Устанавливает внутренние глобальные переменные прокси той же формы, что и entry.iframe.ts, чтобы геттеры @wippy-fe/proxy (config, host, api, on, logger, state, ws, loadWebComponent) разрешались. Любой код приложения или WC, импортирующий из @wippy-fe/proxy, работает без изменений. (Сами глобальные переменные — window.$W и прочие — внутренние; см. Прокси и изоляция § Внутреннее устройство.)

ChildAppConfig по умолчанию (из getDefaultConfig() в config-store.ts):

{
  $schema: '<built schema URL>',
  auth: { token: 'dev-token', expiresAt: '' },
  env: {
    APP_API_URL: `${location.origin}/api`,
    APP_AUTH_API_URL: `${location.origin}/api`,
    APP_WEBSOCKET_URL: `${location.origin.replace(/^http/, 'ws')}/ws`,
  },
  theming: { global: {} },
  context: { resourceId: '', resourceType: 'page' },
}

Любое из этого можно переопределить в модальном окне (или отредактировав localStorage['@wippy-dev/config']).


Оверлей разработчика (модальное окно конфигурации)

Визуально оверлей разработчика — крошечный веб-компонент в shadow DOM (<wippy-dev-overlay>), который отрисовывает:

  • FAB (плавающую кнопку действия) в правом нижнем углу — единственный видимый элемент до нажатия.
  • Всплывающую подсказку в режиме ожидания: «Accept config to continue loading».
  • Панель, открывающуюся по нажатию на FAB. У панели три раздела:
    • Monitor — живой вывод текущего пути, заголовка документа, размера viewport; кнопка «Trigger Refresh», которая генерирует @visibility(true), чтобы приложение могло перезапросить данные.
    • Configuration (сворачиваемый):
      • App Config (JSON) — полный ChildAppConfig как редактируемый JSON. Валидируется при нажатии Accept.
      • Proxy Injections — флажки для каждого признака proxy-инъекции (themeConfig, iframe, primevue, markdown, customCss, customVariables, tailwindConfig, resizeObserver, preventLinkClicks, iconifyIcons, refreshWhenVisible, historyPolyfill, errorCapture).
      • Options — флажок «Auto-accept on reload» (записывает признак авто-принятия в localStorage).
    • Footer — Reset (очищает все ключи localStorage @wippy-dev/*), Accept (сохраняет конфигурацию и разрешает промис загрузки).

Используемые им ключи localStorage (определены в src/proxy/dev/config-store.ts):

Ключ Что хранит
@wippy-dev/config Принятый JSON ChildAppConfig
@wippy-dev/proxy-config Принятый частичный ProxyConfig (признаки инъекций)
@wippy-dev/auto-accept 'true', чтобы пропускать ручное принятие при перезагрузке

Авто-принятие делает «итерацию против сборки без хоста» почти нативной: обновляете страницу, приложение сразу загружается с последней известной конфигурацией, FAB остаётся видимым, чтобы можно было наблюдать или что-то подправить.


Заглушки хоста — автономный API host

API host (import { host } from '@wippy-fe/proxy') — поверхность, через которую приложение просит хост что-то сделать: показать уведомление, перейти по ссылке, открыть сессию, задать контекст, отформатировать URL и т. д. При отсутствии настоящего хоста dev-proxy подставляет слой заглушек в src/proxy/dev/host-stubs.ts:

Метод Поведение в автономном режиме
host.toast(message) Только вывод в консоль
host.confirm({ message }) Браузерный window.confirm()
host.startChat(token, options) Вывод в консоль
host.openSession(uuid, options) Вывод в консоль
host.openArtifact(uuid, options) Вывод в консоль
host.navigate(url) Вывод в консоль + генерация @history, чтобы маршрутизатор дочернего приложения это подхватил, + обновление отображаемого пути в оверлее
host.onRouteChanged(path) Вывод в консоль + обновление отображаемого пути в оверлее
host.handleError(code, error) console.error
host.setContext(context, sessionUUID, source) Вывод в консоль
host.formatUrl(rel) Возвращает `${appConfig.routePrefix
host.classifyLink(href) Реальная реализация — использует mountRoutes / routePrefix из принятой конфигурации
host.layout.* Пустые заглушки, удовлетворяющие контракту типов

Заглушки намеренно многословны: вывод в консоль заменяет реальные побочные эффекты хоста, чтобы разработчик видел, что бы произошло, без фактического подключения хоста. Если корректность вашего приложения зависит от побочного эффекта (например, host.openSession действительно открывает сессию), тестируйте этот путь под хостом; заглушки этого не сделают.


Веб-компоненты — автономная песочница и тесты

Веб-компоненты используют тот же дизайн двойного режима, но загружаются как ES-модули, а не как iframe. Контракт прокси для WC — import { api, host, on, ... } from '@wippy-fe/proxy', и этот импорт разрешается во время выполнения чтением window.__WIPPY_APP_API__ (устанавливается либо настоящим прокси, либо dev-proxy).

HTML-страница песочницы / демо

<!-- demo.html в вашем проекте WC -->
<!DOCTYPE html>
<html>
<head>
    <!-- Обязательный полный скрипт import map опущен в этом сокращённом примере. -->
    <script src="https://web-host.wippy.ai/webcomponents-1.0.44/dev-proxy.js" data-role="@wippy/scripts"></script>
</head>
<body>
    <my-component prop1="value"></my-component>
    <script type="module" src="./src/index.ts"></script>
</body>
</html>

Та же точка переключения, тот же оверлей разработчика. index.ts вашего WC вызывает define(import.meta.url, ...), и элемент регистрирует себя; dev-proxy предоставляет заглушки хоста.

Если dev-proxy.js не загрузится (или вы забудете его подключить), entry.web-component.ts выбрасывает явную ошибку:

@wippy-fe/proxy: Proxy globals not found. For dev/testing without the Wippy host, add <script src="dev-proxy.js"></script> to your HTML.

Эта ошибка — канонический признак того, что у вас отсутствует скрипт загрузки без хоста.

Тесты Vitest / jsdom

Для модульных тестов оверлей разработчика не нужен — у тестов нет UI для взаимодействия. Паттерн в том, чтобы подделать контекст хоста напрямую, присоединив объект-обёртку, который присоединил бы хост:

import { describe, expect, it } from 'vitest'
import { WippyElement } from './base-element'

class TestEl extends WippyElement {
  static get wippyConfig() {
    return { propsSchema: { properties: {} }, hostCssKeys: [] }
  }
  protected onMount(): void {}
  protected onUnmount(): void {}
}

const TAG = 'wippy-test-el'
customElements.define(TAG, TestEl)

it('reads host wrapper attached by resolver as __wippyHost', () => {
  const el = document.createElement(TAG) as TestEl
  const fakeHost = { layout: { broadcast: () => {} } }
  ;(el as any).__wippyHost = fakeHost
  expect(el.host).toBe(fakeHost)
})

Свойство __wippyHost — контракт, который использует хост с управляемой раскладкой. Тесты, которым нужны API или глобальные переменные прокси, могут либо смонтировать dev-proxy через setup-файл vitest, либо самостоятельно подставить window.__WIPPY_APP_API__:

// vitest.setup.ts
;(window as any).__WIPPY_APP_API__ = {
  api: mockApi,
  host: mockHost,
  on: mockOn,
  // ...остальные поля ProxyApiInstance
}

Оба подхода «без хоста» в том же смысле, что и браузерный dev-proxy: контракт прокси удовлетворяется кодом, которым владеет тест, а не реальным сервером Wippy.


Частые отклонения и как их заметить

Когда приложение или WC отходит от контракта с учётом автономного режима, симптомы предсказуемы:

Симптом Вероятная причина Исправление
В app.html есть <script data-role="@wippy/scripts"></script> без src= Страница не может загрузиться без хоста. Прямое открытие файла даёт пустую страницу — среда выполнения прокси не устанавливается, поэтому импорты @wippy-fe/proxy не разрешаются. Добавьте src="https://web-host.wippy.ai/<release-tag>/dev-proxy.js" в тег — URL всегда требует сегмента с тегом релиза.
В app.html есть <script src=…> для dev-proxy, но нет <script type="importmap"> выше него Браузер не может разрешить внешние bare-спецификаторы. Первая загрузка модульного скрипта падает с Failed to resolve module specifier. Загрузите <release-tag>/import-map.json, скопируйте его полный объект imports в <head> перед dev-proxy и используйте все ключи как внешние зависимости Rollup.
В теле app.html собственный SVG-спиннер / <div>Loading…</div> вместо <wippy-loading title="…"> Загрузчик до старта не соответствует каноническому идиому Wippy. Собственная разметка продолжает отображаться, пока экосистема WC (которая отрисовала бы стилизованный, учитывающий тему загрузчик) полностью загружается. Замените на <wippy-loading title="Loading..."></wippy-loading>. Веб-компонент <wippy-loading> регистрируется dev-proxy.js (который синхронно импортирует @wippy-fe/loading) до разбора <body>, поэтому элемент разрешается корректно даже на очень ранней стадии загрузки страницы.
import из исходников соседнего приложения Общий код копируется через границы модулей. Вынесите в пакет рабочего пространства или продублируйте намеренно; никогда не тянитесь через папки приложений.
Жёстко зашитые вызовы fetch('/api/…') Обходят экземпляр axios, предоставляемый прокси; не подхватят переопределения env.APP_API_URL. Используйте useApi() (приложения) или import { api } from '@wippy-fe/proxy' (WC).
new EventSource(...) для живых данных Обходит мост аутентификации/ретрансляции хоста; в автономном режиме эквивалента нет. Используйте on('your.topic', cb) — работает в обоих режимах (в автономном топик просто не срабатывает, если вы его не симулируете).
document.documentElement.setAttribute('data-theme', ...) для переключения темы data-theme не является протоколом темы Wippy. Используйте режим Auto или управляемые хостом классы .w-theme-light / .w-theme-dark. Настроенные значения @light / @dark поддерживают оба пути. См. micro-frontend-app-theming.md.
import '@wippy-fe/theme/theme-config.css' в app.ts Избыточно — хост внедряет theme-config через proxy-инъекцию themeConfig: true. В режиме без хоста dev-proxy тоже её внедряет. Уберите импорт.
Жёстко зашитые базовые URL API в модулях api/ Не будут работать в режиме без хоста против другого окружения. Читайте из appConfig.env.APP_API_URL через useApi().

Устранение неполадок

Ошибка «Proxy globals not found». Бандл WC выполнился, но ни настоящий прокси, ни dev-proxy не инициализировали window.__WIPPY_APP_API__. Проверьте, что <script src=".../dev-proxy.js" data-role="@wippy/scripts"> присутствует на странице и URL достижим. В режиме с продуктовым хостом эта ошибка означает, что хост не смог внедрить proxy.js — проверьте логи хоста.

Оверлей разработчика не появляется. Оверлей — пользовательский элемент в shadow DOM, добавляемый в document.body после DOMContentLoaded. Если вы загружаете dev-proxy.js изнутри <head>, а body отсутствует или имеет display: none, оверлей не может отрисоваться. Переместите скрипт в конец body или сделайте body видимым.

Авто-принятие «застряло» с плохой конфигурацией. Если сохранённая конфигурация сломана, а авто-принятие включено, оверлей всё равно отрисовывается (в режиме мониторинга); нажмите FAB → Reset, чтобы очистить все ключи localStorage @wippy-dev/*, затем перезагрузите страницу.

Неправильная тема в режиме разработки. По умолчанию getDefaultProxyConfig() включает customCss и customVariables, но отключает themeConfig, iframe, primevue, markdown. Если ваше приложение ожидает CSS theme-config от PrimeVue, переключите эти флажки на панели. Авто-принятие это запомнит.

Несовпадение importmap между режимом с хостом и автономным. Перезагрузите import-map.json закреплённого релиза, замените полный объект imports для режима без хоста и заново сгенерируйте из него ключи внешних зависимостей Rollup. Не правьте отдельные записи и не поддерживайте вручную отобранное подмножество.

Тест WC падает с «host getter returned null». Тестам нужно установить el.__wippyHost = fakeWrapper до срабатывания connectedCallback. Либо установите его перед document.body.appendChild(el), либо подделайте обёртку через тот паттерн резолвера, который использует ваш набор тестов.


Смежная документация

  • proxy-api.md — полный справочник @wippy-fe/proxy (работает одинаково в режиме с хостом и без него)
  • micro-frontend-app.md — сборка приложений-микрофронтендов (путь загрузки — паттерн двойного режима app.html, который описывает этот документ)
  • web-component.md — сборка веб-компонентов (WippyVueElement, define(), песочница и тесты без хоста)
  • theming.md — переопределения темы на страницу через config_overrides (также передаются в dev-proxy через theming.global.cssVariables / customCSS)
  • compliance-checklist.md — §9, чек-лист режима без хоста с полными правилами REJECT