Skip to content
🎁
tuvix.js
tuvix.js (Umbrella)
One package, one import path. Re-exports the orchestrator from @tuvix.js/core plus the public APIs of @tuvix.js/router, @tuvix.js/event-bus, @tuvix.js/loader and @tuvix.js/sandbox.

Installation ​

bash
npm install tuvix.js
# or pnpm add tuvix.js

Installing tuvix.js installs @tuvix.js/core, @tuvix.js/router, @tuvix.js/event-bus, @tuvix.js/loader and @tuvix.js/sandbox. All six packages ship ES module and CommonJS builds and declare "sideEffects": false.

When to use it ​

Install tuvix.js in a shell application that uses the orchestrator together with the router, the event bus or the sandbox. You get one dependency to version and one import path, and your bundler drops the exports you never import.

Install the scoped packages instead when:

  • A micro app only talks to the shell. @tuvix.js/event-bus alone gives it getGlobalBus(), without pulling the orchestrator into its node_modules.
  • You need an export the umbrella leaves out, such as registerModule or emitLifecycle from @tuvix.js/loader, or pathToRegex from @tuvix.js/router. See Not included.
  • You want your package.json to state which parts of Tuvix.js a package depends on.

Mixing both is safe. The umbrella re-exports the same modules, so getGlobalBus from tuvix.js and from @tuvix.js/event-bus return the same bus, and the lifecycle helpers share one registry on window.

Quick start ​

A shell that mounts about on /about, turns link clicks into router navigations and logs app errors:

ts
import { createOrchestrator, getGlobalBus, interceptLinks, OrchestratorEvent, type AppErrorEvent } from 'tuvix.js';

const orchestrator = createOrchestrator({ router: { routes: [] } });

orchestrator.register({
  name: 'about',
  entry: 'https://cdn.example.com/about/main.js',
  container: '#main',
  activeWhen: '/about',
  props: { who: 'us' },
});

getGlobalBus().on<AppErrorEvent>(OrchestratorEvent.APP_ERROR, ({ name, error, phase }) => {
  console.error(`${name} failed during ${phase}`, error);
});

await orchestrator.start();
interceptLinks({ navigate: (path) => orchestrator.navigateTo(path) });

The about app, built to https://cdn.example.com/about/main.js:

ts
import { defineMicroApp } from 'tuvix.js';

export default defineMicroApp({
  name: 'about',
  mount({ container, props }) {
    container.innerHTML = `<h1>About ${String(props?.who)}</h1>`;
  },
  unmount({ container }) {
    container.replaceChildren();
  },
});

Clicking <a href="/about?ref=nav"> pushes /about?ref=nav, and the orchestrator mounts about into #main (the query string does not affect matching).

API ​

Every export below is a re-export. The linked package page documents its signature, options and edge cases.

From @tuvix.js/core ​

Docs: @tuvix.js/core.

ExportKindWhat it does
createOrchestratorfunctionCreates an Orchestrator.
OrchestratorclassRegisters apps, reconciles them against the URL, follows a router (connectRouter), updates props, destroys.
defineMicroAppfunctionValidates a { name, bootstrap?, mount, unmount, update?, ssr? } object, registers it for the loader and makes it emit tuvix:mounted / tuvix:unmounted / tuvix:error.
OrchestratorEventenumEvent names the orchestrator emits on its bus (tuvix:app:mount, tuvix:app:error, ...).
OrchestratorConfig, MicroAppConfig, MicroAppDefinitiontypeArguments of createOrchestrator, register and defineMicroApp.
AppStatus, AppSandboxOption, RouterSource, RegisteredApptypeApp status union, the sandbox option, the connectRouter argument, the app record shape.
AppErrorEvent, AppErrorPhasetypeAPP_ERROR payload { name, error, phase } and its phase union ('load', 'bootstrap', 'mount', 'update', 'unmount').

From @tuvix.js/event-bus ​

Docs: @tuvix.js/event-bus.

ExportKindWhat it does
EventBusclassPub/sub bus with on, once, emit, off, onAny.
createEventBusfunctionCreates a new, separate EventBus.
getGlobalBusfunctionReturns the page-wide bus stored on window.__TUVIX_GLOBAL_BUS__. The orchestrator uses it by default.
resetGlobalBusfunctionDestroys the page-wide bus; the next getGlobalBus() creates a new one.
IEventBus, EventHandler, WildcardHandler, Unsubscribe, EventBusOptionstypeBus interface, handler signatures, unsubscribe function, constructor options.

From @tuvix.js/router ​

Docs: @tuvix.js/router.

ExportKindWhat it does
RouterclassHistory or hash router that maps paths to app names.
createRouterfunctionCreates a Router.
matchPathfunctionMatches a path against a pattern (:param, /*, query and hash ignored). activeWhen strings use it.
matchRoutefunctionReturns the first route in a RouteConfig[] that matches a path in full, with params and query.
normalizePathfunctionAdds a leading slash and removes a trailing one.
interceptLinksfunctionTurns same-origin link clicks into a navigate(path) call, with optional intent prefetch.
resolveLinkfunctionReturns { anchor, url, path } for the HTML link an event happened on, or null when the browser should handle it.
IRouter, RouterConfig, RouterMode, RouteConfig, MatchedRoutetypeRouter interface, options and route shapes.
NavigationEvent, NavigationGuard, RouteChangeHandlertypeNavigation payload, guard and listener signatures.
PathMatch, MatchPathOptions, InterceptLinksOptions, ResolvedLink, ResolveLinkOptionstypematchPath result and options, interceptLinks options, resolveLink result and options.

From @tuvix.js/loader ​

Docs: @tuvix.js/loader.

ExportKindWhat it does
ModuleLoaderclassLoads an entry (URL, script/style config or loader function) with timeout, retries and caching.
createLoaderfunctionCreates a ModuleLoader.
whenMountedfunctionResolves when an app emits its mounted lifecycle event.
onLifecyclefunctionListens for tuvix:mounted, tuvix:unmounted or tuvix:error DOM events.
isMountedfunctiontrue while any container of the app is mounted, according to the lifecycle events.
Entry, EntryConfig, MicroAppModule, MountContext, UnmountContext, UpdateContexttypeEntry formats and the lifecycle contract a micro app implements.
LoaderOptions, LoadResult, LoadStatus, PrefetchStrategytypeLoader options and results.
LifecycleType, LifecycleDetail, LifecycleEventtypeLifecycle event names, payload and DOM event type.
ts
import { onLifecycle, whenMounted } from 'tuvix.js';

const off = onLifecycle('error', ({ name, error }) => console.error(name, error));

const { container, hydrated } = await whenMounted('footer', { signal: AbortSignal.timeout(5000) });
console.log(container.id, hydrated);
off();

From @tuvix.js/sandbox ​

Docs: @tuvix.js/sandbox.

ExportKindWhat it does
CssSandboxclassMoves a container into a shadow root and loads stylesheets inside it. The orchestrator uses it for sandbox: true.
JsSandboxclassProxy window that keeps globals a script writes away from the real window.
SandboxclassCssSandbox and JsSandbox behind one activate / deactivate pair.
createSandboxfunctionCreates a Sandbox.
SandboxOptions, ISandbox, ICssSandbox, IJsSandboxtypeOptions and interfaces of the sandboxes.
AdoptStylesOptions, AdoptedStylestypeArgument and return value of CssSandbox.adoptStyles.

Not included ​

The umbrella leaves these out. Install the package that provides them.

You needInstall
registerModule, unregisterModule, getRegisteredModule, emitLifecycle, hasServerMarkup, normalizeEntry, loadScript, loadStyle, removeScript, removeStyle, prefetchResource, and the types EntryLoader, EntryLoaderContext, MicroAppBus, ScriptType, CacheEntry, ResourceOptions, LoadScriptOptions, LoadStyleOptions, PrefetchOptions@tuvix.js/loader
pathToRegex, parseQuery, isPathActive@tuvix.js/router
React, Vue, Svelte or Angular micro apps@tuvix.js/react, @tuvix.js/vue, @tuvix.js/svelte, @tuvix.js/angular
Server-side composition@tuvix.js/server
Module Federation remotes@tuvix.js/module-federation
Island assets in Vite builds@tuvix.js/vite
The DevTools panel@tuvix.js/devtools
Project scaffoldingcreate-tuvix-app

Edge cases ​

  • tuvix.js has one entry point. Subpath imports such as tuvix.js/core or tuvix.js/router fail to resolve; import from tuvix.js or from the scoped package.
  • The umbrella pins its dependencies to the same release. Installing tuvix.js next to a different version of a scoped package gives you two copies of that package. getGlobalBus() and the lifecycle registry still share state through window, but instanceof Orchestrator or instanceof EventBus checks fail across the copies.
  • whenMounted, onLifecycle and isMounted react to tuvix:* DOM events. Modules from defineMicroApp and the framework adapters emit them; a hand-written module emits none unless it calls emitLifecycle from @tuvix.js/loader. Events for a container outside the document are dispatched on document.
  • resetGlobalBus() destroys the bus a running orchestrator holds. The orchestrator keeps that destroyed bus, so its events no longer reach subscribers of the next getGlobalBus(). Call it between tests, after orchestrator.destroy().
  • Behaviour changes and breaking changes in @tuvix.js/core and @tuvix.js/router apply here unchanged. See Migrating from 0.4.

What it does not do ​

  • It adds no code of its own. Every export comes from one of the five scoped packages.
  • It does not include the framework adapters, the server package, Module Federation, Vite or DevTools.
  • It does not offer subpath entries per package.