
Security News
Feross on TBPN: How North Korea Hijacked Axios
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.
@inlay/render
Advanced tools
Server-side component resolution for Inlay element trees. Given an element like `<com.example.Greeting name="world">`, the renderer looks up the component implementation, expands it, and recurses until everything is primitives the host can render.
Server-side component resolution for Inlay element trees. Given an element like <com.example.Greeting name="world">, the renderer looks up the component implementation, expands it, and recurses until everything is primitives the host can render.
npm install @inlay/render @inlay/core
A minimal Hono server that accepts an element, resolves it, and returns HTML. Uses Hono's JSX for safe output.
/** @jsxImportSource hono/jsx */
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { $, serializeTree, deserializeTree, isValidElement } from "@inlay/core";
import { render } from "@inlay/render";
import type { RenderContext } from "@inlay/render";
// Records — in-memory stand-in for AT Protocol.
// Each component's rkey is the NSID it implements.
const records = {
// Primitives: no body. The host renders these directly.
"at://did:plc:host/at.inlay.component/org.atsui.Stack": {
$type: "at.inlay.component",
},
"at://did:plc:host/at.inlay.component/org.atsui.Text": {
$type: "at.inlay.component",
},
// Template: a stored element tree with Binding placeholders.
"at://did:plc:app/at.inlay.component/com.example.Greeting": {
$type: "at.inlay.component",
body: {
$type: "at.inlay.component#bodyTemplate",
node: serializeTree(
$("org.atsui.Stack", { gap: "small" },
$("org.atsui.Text", {}, "Hello, "),
$("org.atsui.Text", {}, $("at.inlay.Binding", { path: ["name"] })),
)
),
},
imports: ["did:plc:host"],
},
};
// Resolver — I/O layer that render() calls into
const resolver = {
async fetchRecord(uri) { return records[uri] ?? null; },
async xrpc(params) { throw new Error(`No handler for ${params.nsid}`); },
async resolveLexicon() { return null; },
async resolve(dids, collection, rkey) {
for (const did of dids) {
const uri = `at://${did}/${collection}/${rkey}`;
const record = records[uri];
if (record) return { did, uri, record };
}
return null;
},
};
// Primitives → JSX
const primitives = {
"org.atsui.Stack": async ({ props, ctx }) => {
const p = props as { gap?: string; children?: unknown[] };
return (
<div style={`display:flex;flex-direction:column;gap:${p.gap ?? 0}`}>
{await renderChildren(p.children, ctx)}
</div>
);
},
"org.atsui.Text": async ({ props, ctx }) => {
const p = props as { children?: unknown };
return <span>{await renderNode(p.children, ctx)}</span>;
},
};
// Walk loop — resolve elements, render primitives to JSX
async function renderNode(node, ctx) {
if (node == null) return <></>;
if (typeof node === "string") return <>{node}</>;
if (Array.isArray(node)) return <>{await Promise.all(node.map(n => renderNode(n, ctx)))}</>;
if (isValidElement(node)) {
const result = await render(node, ctx, { resolver });
if (result.node === null) {
// Primitive: host renders using element type + result.props
const Primitive = primitives[node.type];
if (!Primitive) return <>{`<!-- unknown: ${node.type} -->`}</>;
return Primitive({ props: result.props, ctx: result.context });
}
return renderNode(result.node, result.context);
}
return <>{String(node)}</>;
}
async function renderChildren(children, ctx) {
if (!children) return <></>;
const items = Array.isArray(children) ? children : [children];
return <>{await Promise.all(items.map(c => renderNode(c, ctx)))}</>;
}
// Server
const app = new Hono();
app.get("/", async (c) => {
const element = deserializeTree({
$: "$", type: "com.example.Greeting", props: { name: "world" },
});
const ctx = { imports: ["did:plc:host", "did:plc:app"] };
const body = await renderNode(element, ctx);
return c.html(<html><body>{body}</body></html>);
});
serve({ fetch: app.fetch, port: 3000 });
Returns:
<html><body>
<div style="display:flex;flex-direction:column;gap:small">
<span>Hello, </span>
<span>world</span>
</div>
</body></html>
The com.example.Greeting template expanded its Binding for name, resolved to org.atsui.Stack and org.atsui.Text primitives, and the host turned those into HTML.
A production host will also want:
See proto/ for a working implementation with all of the above.
render() resolves one element at a time. It returns node: null when the element is a primitive (the host renders it using element.type + result.props) or a non-null node tree when the element expanded into a subtree that needs more passes. The walk loop drives this to completion.
at.inlay.Binding elements in props are resolved against the current scope. The concrete values are available on result.props.at://{did}/at.inlay.component/{nsid} — first match wins.node: null with resolved props.componentStack stamped on the error. MissingError propagates for at.inlay.Maybe to catch.| Function | Description |
|---|---|
createContext(component, componentUri?) | Create a RenderContext from a component record |
render(element, context, options) | Render a single element. Returns Promise<RenderResult> |
resolvePath(obj, path) | Resolve a dotted path array against an object |
MissingError | Thrown when a binding path can't be resolved |
Render options:
resolver: Resolver — I/O callbacks (required)maxDepth?: number — nesting limit (default 30)Resolver — { fetchRecord, xrpc, resolveLexicon, resolve }RenderContext — { imports, component?, componentUri?, depth?, scope?, stack? }RenderResult — { node, context, props, cache? }RenderOptions — { resolver, maxDepth? }ComponentRecord — re-exported from generated lexicon defsCachePolicy — re-exported from generated lexicon defsFAQs
Server-side component resolution for Inlay element trees. Given an element like `<com.example.Greeting name="world">`, the renderer looks up the component implementation, expands it, and recurses until everything is primitives the host can render.
We found that @inlay/render demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.

Security News
OpenSSF has issued a high-severity advisory warning open source developers of an active Slack-based campaign using impersonation to deliver malware.

Research
/Security News
Malicious packages published to npm, PyPI, Go Modules, crates.io, and Packagist impersonate developer tooling to fetch staged malware, steal credentials and wallets, and enable remote access.