@savvifi/meridian-aion-web
v0.4.0
Published
The aion/web <-> meridian-ux render seam: renderMeridianEntityView (server — parse route, load composition, project to a ViewDescriptor, pre-fetch page-0, render + dehydrate) + MeridianEntityView (client hydration) + savviSkin. Lets an SSR host (e.g. aion
Readme
@savvifi/meridian-aion-web
The aion/web ↔ meridian-ux render seam. Lets an SSR host (e.g. aion/web on
Hono + Vite classic SSR) render aion entity routes through meridian-ux —
ViewDescriptor → muiKit — instead of the legacy React entity-page path, behind a
per-entity feature flag so parity can be proven before switching.
Meridian owns everything from the composition down (project → render → paginate); the host keeps the SSR harness, the app shell, navigation, providers, and the session-bound tRPC caller.
Install
pnpm add @savvifi/meridian-aion-webPeers the host provides: react react-dom (19), @mui/material (7),
@emotion/react @emotion/styled (11), and @aion/entity-route-runtime (the
composition loader + route parser — the host's version wins).
Server (SSR)
import { renderMeridianEntityView, NotFoundSignal } from "@savvifi/meridian-aion-web/server";
const { node, dehydrated } = await renderMeridianEntityView({
pathname, // "/entities/products" | "/entities/products/id/123"
caller, // session-bound createCaller -> caller.entities.ops
graph, // ctx.graph -> composition loader's systemGraphFactory
mode, // "light" | "dark" (default "light")
// notFound: "throw" (default) throws NotFoundSignal; "return" -> { node: null }
});
// stream `node` as the page content; embed `dehydrated` = { viewJson, initialData }
// in your boot script for the client.renderMeridianEntityView parses the route, loads the installed composition,
projects it to a ViewDescriptor, pre-fetches page-0 through the caller (so the
SSR HTML carries real rows), renders it through muiKit + the savviSkin theme,
and returns the node plus the dehydrated payload.
Client (hydration)
import { MeridianEntityView } from "@savvifi/meridian-aion-web/client";
import { createEntityOpsInvoker } from "@savvifi/meridian-aion-web";
// invoker over the app's browser tRPC client:
const invoker = createEntityOpsInvoker(client.entities.ops);
<MeridianEntityView
viewJson={dehydrated.viewJson} // a JSON value, not a string
initialData={dehydrated.initialData}
invoker={invoker}
mode={mode}
/>usePagedRows (in @savvifi/meridian-web-react) seeds page-0 from initialData
and skips the initial refetch, so hydration matches the server render with no flash.
Exports
@savvifi/meridian-aion-web/server—renderMeridianEntityView,NotFoundSignal,isNotFoundSignal,savviSkin@savvifi/meridian-aion-web/client—MeridianEntityView,savviSkin- root — all of the above +
createEntityOpsInvoker
License
UNLICENSED — © savvifi.
