Skip to main content

SDK contract (@chrono-one/shell-sdk)

The shared contract package is a TypeScript-only workspace package. It contains only types and thin utilities — no React, no Firebase SDK import, no business logic. Every integrated microfrontend depends on it to conform to the shell without importing shell internals.

import type { ShellContext, MicrofrontendMount } from '@chrono-one/shell-sdk';

ShellContext​

Handed to every mounted microfrontend:

interface ShellContext {
auth: {
user: UserIdentity | null; // uid, email, displayName, photoURL
idToken: () => Promise<string>; // always-fresh Firebase ID token
signOut: () => Promise<void>;
};
permissions: {
globalRoles: GlobalRole[]; // e.g. ['admin', 'viewer']
hasGlobalRole: (role: GlobalRole) => boolean;
};
navigation: {
navigate: (path: string) => void; // delegates to the shell router
currentPath: string;
};
events: ShellEventBus;
}

Event bus​

A zero-dependency typed wrapper over the browser's native CustomEvent on window. Microfrontends subscribe to:

  • chrono:auth:token-updated — fresh ID token (register here, never your own Firebase listener).
  • chrono:auth:signed-out — the session ended; drop any cached token.
  • chrono:nav:route-changed — detail { path }; emitted by the shell on every top-level navigation, so a remote can react to routing without reaching into the host's router.
  • chrono:context:sync — detail is the current ShellContext; emitted whenever the contract object is rebuilt (identity, global roles or path changed), so a remote mounted earlier is never left holding a stale context.

All four are emitted by the shell's ShellContextProvider / AuthProvider; a microfrontend only ever subscribes.

const off = context.events.on('chrono:auth:token-updated', ({ idToken }) => {
/* update your local token reference */
});

The remote entry contract​

Each microfrontend exposes a single ./App default export:

const mount: MicrofrontendMount = {
mount(container, context) {
const root = createRoot(container);
root.render(<ShellContextProvider value={context}><App /></ShellContextProvider>);
return () => root.unmount();
},
};
export default mount;

Adapter checklist​

  1. Add the @module-federation/vite remote config exposing ./App; set build.target: 'esnext' and the async bootstrap boundary.
  2. Declare shared singletons matching the shell's config exactly.
  3. Remove/hide your own top-level navigation (detect window.__CHRONO_ONE_SHELL__).
  4. Consume identity via context.auth.user; navigate via context.navigation.navigate().
  5. Remove your own Firebase initialisation — the shell owns it.
  6. Subscribe to AUTH_TOKEN_UPDATED; never register your own onIdTokenChanged.
  7. Fetch product permissions via GET /permissions/:productId.