# "Host-less Mode" _Path: en/frontend/micro-frontends/host-less-mode_ > "Run and test Wippy micro frontend apps and web components without the Web Host." ## Table of Contents - Host-less Mode ## Content # Host-less Mode Host-less mode lets a Wippy micro frontend app or web component build, run, and test **without** the Wippy Web Host wrapping it. > **Default injection state:** The dev overlay starts with `themeConfig`, `primevue`, `markdown`, and `iframe` **disabled**, but `customCss` and `customVariables` **enabled**. So an app that relies only on custom overrides may appear to work, while one that expects the platform theme variables or PrimeVue styles will render unstyled until you enable those injections. Open the overlay FAB → enable the injections you need → check "Auto-accept on reload" to persist across reloads. --- ## Table of contents - [Mental model — apps and WCs are intentionally standalone-aware](#mental-model-apps-and-wcs-are-standalone-aware) - [The `@wippy/scripts` switchpoint — one tag, two boot paths](#the-wippyscripts-switchpoint-one-tag-two-boot-paths) - [What `dev-proxy.js` actually does](#what-dev-proxyjs-actually-does) - [The dev overlay (config modal)](#the-dev-overlay-config-modal) - [Host stubs — the standalone `host` API](#host-stubs-the-standalone-host-api) - [Web components — host-less playground and tests](#web-components-host-less-playground-and-tests) - [Common deviations and how to spot them](#common-deviations-and-how-to-spot-them) - [Troubleshooting](#troubleshooting) - [Related docs](#related-docs) --- ## Mental model — apps and WCs are standalone-aware Every Wippy micro frontend app and web component follows one runtime constraint: > **The runtime contract is the proxy API surface.** What that means in practice: - The only thing an app or WC touches at runtime is the proxy API surface: the sync getters imported from `@wippy-fe/proxy` (`host`, `api`, `on`, `config`, `state`, `ws`, `logger`). Both apps and WCs use the same imports; under the hood they resolve to the same `ProxyApiInstance` that the runtime installs as internal globals (`window.$W`, `window.__WIPPY_APP_API__` — never read these directly). - Apps and WCs do **not** import code from neighboring apps, the parent module's Lua side, the Wippy Web Host, or another project module. They live in their own folder. Vite derives every Rollup external from the pinned target-host `import-map.json`; `package.json` declares only the npm dependencies and peer roots the artifact actually imports. - The same `app.ts` (or WC `index.ts`) boots correctly in two environments: 1. **Hosted** — inside a Wippy Web Host that injects `proxy.js`, AppConfig, importmap, and CSS. 2. **Host-less** — running its `app.html` through a Vite dev server, a unit-test page, a Storybook-style playground, or another HTTP development host. Each app or WC is a small program with a standardized I/O surface. The host is one possible runtime; standalone is another. Application code does not need to distinguish between them. This design supports: - Local frontend iteration without starting a full Wippy backend. - Isolated WC unit tests under Vitest and jsdom. - Apps shared between Wippy modules; every micro frontend app and web component builds with the same toolchain regardless of which module ships it. - Customer-specific overlays that let operators patch metadata (theming, import map, and environment) without rebuilding the frontend bundle. --- ## The `@wippy/scripts` switchpoint — one tag, two boot paths Every canonical app's `app.html` ships with **one** script tag that decides the boot path at load time: This is an abbreviated body/boot example. Insert the complete valid import-map response described by the [Import-map snapshot algorithm](./build-system.md#import-map-snapshot-algorithm), updated when the pinned Web Host tag changes. ```html ``` Full `app.html` scaffold in [Micro Frontend App](./micro-frontend-app.md). Two attributes on that one tag carry the entire dual-mode contract: | Attribute | Role | Used by | |---|---|---| | `data-role="@wippy/scripts"` | Marker for the host. When present, the host removes this ` ``` dev-proxy.js reads this synchronously at boot via `document.querySelector('script[data-role="@wippy/package"]')` and uses `wippy.proxy.injections` to seed the proxy-config defaults and `wippy.configOverrides.customization` to seed `appConfig.theming.global`. The data-role string `@wippy/package` is exported as `WIPPY_PACKAGE_DATA_ROLE` from `@wippy-fe/shared` so both sides of the boundary share the constant. This shape has the following properties: - **Single source.** The plugin reads `package.json` at build time; source files do not import it. - **Synchronous access.** Inline metadata is available to `dev-proxy.js` before application code runs. - **Defined ordering.** The plugin injects the metadata at the top of ``, before any script tag. Dev-proxy is a synchronous UMD script; module scripts are deferred. - **Plugin-owned template update.** The plugin injects the metadata without a hand-maintained block in `app.html`. - **Shared constant.** `@wippy-fe/shared` exports the `'@wippy/package'` value as `WIPPY_PACKAGE_DATA_ROLE`; dev-proxy and the plugin import it from there. - **Hosted compatibility.** Hosted processing reads package metadata from the registry server-side. The inline JSON tag is only consumed by the standalone development path and is otherwise inert. Dev-proxy reads the JSON during `resolveDevConfig()` and uses it to populate the development-overlay defaults. If the script tag is absent, dev-proxy falls back to `getDefaultProxyConfig()`, so older apps continue with the generic defaults. > **Why a plugin and not a runtime `window` global?** Dev-proxy.js is a non-module synchronous script that runs early during `` parsing — before any module script (including your `app.ts`) has loaded. So `app.ts` cannot set a global *before* dev-proxy reads it. A build-time HTML transform places the data in the DOM up-front, available the instant dev-proxy executes. > **Why one tag and not two?** A second ` ``` Same switchpoint, same dev overlay. Your WC's `index.ts` calls `define(import.meta.url, ...)` and the element registers itself; dev-proxy provides the host stubs. If `dev-proxy.js` fails to load (or you forget to include it), `entry.web-component.ts` throws an explicit error: > `@wippy-fe/proxy: Proxy globals not found. For dev/testing without the Wippy host, add to your HTML.` That error indicates that the host-less boot script is missing. ### Partial Vitest / jsdom test excerpt For unit tests the dev overlay is unnecessary — tests don't have a UI to interact with. The pattern is to **fake the host context directly** by attaching the wrapper object the host would attach: The excerpt below assumes a `jsdom` test environment and a setup file loaded before the test module. That setup must stub `window.__WIPPY_APP_API__` and `window.__WIPPY_APP_CONFIG__`; for jsdom versions whose `ElementInternals` lacks `states`, it must also provide that `CustomStateSet` surface. This is the component-level assertion, not a complete Vitest project. ```ts import { describe, expect, it } from 'vitest' import { WippyElement } from '@wippy-fe/webcomponent-core' 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) }) ``` The `__wippyHost` property is the contract the managed-layout host uses. Tests that need API or proxy globals can either mount dev-proxy via a vitest setup file, or stub `window.__WIPPY_APP_API__` themselves: ```ts // vitest.setup.ts ;(window as any).__WIPPY_APP_API__ = { api: mockApi, host: mockHost, on: mockOn, // ...other ProxyApiInstance fields } ``` In both approaches, test-owned code satisfies the proxy contract instead of a Wippy server. --- ## Common deviations and how to spot them When an app or WC has drifted from the standalone-aware contract, the symptoms are predictable: | Symptom | Probable cause | Fix | |---|---|---| | `app.html` has `` with no `src=` | Page cannot boot on an HTTP development host without Wippy injection. The proxy runtime never initializes, so application modules error when they evaluate `@wippy-fe/proxy`. | Add `src="https://web-host.wippy.ai//dev-proxy.js"` to the tag — the URL always requires a release-tag segment. | | `app.html` has the dev-proxy `