@bloomreach/navapp-communication
v3.3.5
Published
<!-- Copyright 2021-2026 Bloomreach
Maintainers
Keywords
Readme
Navapp Communication
@bloomreach/navapp-communication is the library the Bloomreach navigation application (navapp) and the
applications it hosts use to talk to each other.
The navapp loads every application in its own iframe, possibly from another origin. This library sets up a typed, promise-based API across the iframe boundary in both directions:
- the parent (navapp) exposes a
ParentApithat applications call, e.g. to update the browser URL, show a mask or close a popup; - each child (an application in an iframe) exposes a
ChildApithat the navapp calls, e.g. to navigate, collect navigation items or log out.
Under the hood it uses Penpal (v4) for the postMessage handshake and method
calls.
┌──────────────────────── navapp (parent) ────────────────────────┐
│ connectToChild({ iframe, methods: ParentApi }) → ChildApi │
│ │
│ ┌──── iframe: CMS ─────┐ ┌──── iframe: popup app ────┐ │
│ │ connectToParent({ │ │ connectToParent({ │ │
│ │ methods: ChildApi │ │ methods: ChildApi │ │
│ │ }) → ParentApi │ │ }) → ParentApi │ │
│ └──────────────────────┘ └───────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘Children never talk to each other directly. When one application needs something from another (for example a popup
asking the CMS to open a document), it calls a ParentApi method and the navapp forwards the call to the right child.
Who uses it
| Consumer | Role |
|---|---|
| community/navigation-application | Parent: implements ParentApi in ConnectionService |
| CMS (Wicket) | Child: loads the UMD bundle and implements ChildApi in navapp-bridge.js |
| community/channel-manager/ui | Child |
| community/cms/frontend | Child |
| enterprise/wpm/frontend/project-management | Child |
| enterprise/ai-service/client/assistant-angular | Child (popup) |
Each consumer pins its own version in its package.json, so they don't all use the same release.
Usage
Install the library together with its peer dependency:
npm install @bloomreach/navapp-communication penpal@^4In a child application
Connect to the parent once, when the application starts, and keep the returned ParentApi:
import { ChildApi, connectToParent, ParentApi } from '@bloomreach/navapp-communication';
const childApi: ChildApi = {
getConfig: async () => ({ apiVersion: '3.3.0' }),
navigate: async (location, triggeredBy) => router.navigate(location.path),
beforeNavigation: async () => !hasUnsavedChanges(),
};
const parentApi: ParentApi = await connectToParent({
parentOrigin: window.location.origin, // must match the navapp origin, or the connection is refused
methods: childApi,
});
const { userSettings } = await parentApi.getConfig();
await parentApi.updateNavLocation({ path: 'documents/123', breadcrumbLabel: 'My document' });All ChildApi methods are optional. Implement only the ones your application supports.
In the parent (navapp)
import { connectToChild, ParentApi } from '@bloomreach/navapp-communication';
const childApi = await connectToChild({
iframe, // the iframe element hosting the child
methods: parentApi, // the ParentApi implementation
connectionTimeout: 30000, // ms to wait for the handshake
methodInvocationTimeout: 30000, // ms to wait for each child method call
});
const navItems = await childApi.getNavItems?.();Without a bundler
The library is also published as a UMD bundle (dist/index.umd.js) that registers the global
window.brNavappCommunication. It expects Penpal to be loaded first as the global Penpal. This is how the CMS uses
it.
Behavior to know about
- Optional methods are called with optional chaining. A connected app only exposes the methods it implements, and
an older app may not know a newer method, so call them as
api.someMethod?.(). - Method timeouts.
connectToChildwraps every child method so it rejects with"<method> call timed out"aftermethodInvocationTimeoutms, or 5 minutes if you don't set one.beforeNavigationis never wrapped, because it may wait for the user to answer a dialog. Pass0or a negative value to turn the timeouts off. - Handshake retries. Penpal v4 sends its handshake only once, so a child that loads before the parent is listening
would never connect.
connectToParentre-sends the handshake every second until the parent replies. - Version check.
getVersion()returns the library version. The navapp reports it asParentConfig.apiVersion, and children report the version they implement asChildConfig.apiVersion.
Adding a method to the API
A new capability usually touches the library, the navapp and at least one child:
- Add the method to
ParentApiand/orChildApiinsrc/lib/api.ts, with a doc comment. Make new methods optional (myMethod?: ...), so apps built against an older version still compile and run. Export any new types from the same file. - Implement it in the navapp's
ConnectionService(getParentApiMethods) and add a spec. - Implement or call it in the child applications that need it, using optional chaining.
- Bump the library version (minor for new optional methods), publish it (see Releasing), and update the dependency in every consumer that uses the new types.
TypeScript consumers only see the new types after step 4. Until then their builds fail with errors like
Property 'myMethod' does not exist on type 'ParentApi'. To try changes before releasing, build the library and copy
dist/* into the consumer's node_modules/@bloomreach/navapp-communication/dist/. The next npm ci reverts it.
The CMS bridge (navapp-bridge.js) is plain JavaScript and Penpal matches methods by name, so it doesn't need a new
library version.
Development
The toolchain for this library is older than the Angular applications' (see the repository CLAUDE.md for the
expected Node and npm versions).
npm ci # install dependencies
npm run build # build dist/ (UMD, ES module and type definitions) with rollup
npm start # rebuild on every change
npm run test:single-run # run the unit tests once
npm test # run the tests in watch mode, with coverage
npm run lint # lint src/
npm run docs # generate the API docs with TypeDocnpm run build prints a series of (!) Plugin typescript warnings about node_modules/@types/node and
undici-types. They come from the TypeScript version used here being too old to parse recent Node type definitions
that the test tooling brings in. They are harmless: the build ends with created dist/index.js and
created dist/index.d.ts.
Releasing
There is no CI job that publishes this library. A release is done by hand:
- Bump
versioninpackage.json(andpackage-lock.json). - Run the tests, then
npm publishfrom this directory.prepackbuildsdist/first. Publishing needs write access to the@bloomreachnpm scope. - Update the version in the consumers that need the release, and refresh their lockfiles.
