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 currentShellContext; 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
- Add the
@module-federation/viteremote config exposing./App; setbuild.target: 'esnext'and the async bootstrap boundary. - Declare shared singletons matching the shell's config exactly.
- Remove/hide your own top-level navigation (detect
window.__CHRONO_ONE_SHELL__). - Consume identity via
context.auth.user; navigate viacontext.navigation.navigate(). - Remove your own Firebase initialisation — the shell owns it.
- Subscribe to
AUTH_TOKEN_UPDATED; never register your ownonIdTokenChanged. - Fetch product permissions via
GET /permissions/:productId.