npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@myexamsai/revelio

v3.0.1

Published

Revelio — Server-Driven UI library for React and React Native. Your server defines the UI as JSON, Revelio renders it live with surgical per-node re-renders, Zod validation, real-time WebSocket updates, and cross-node subscriptions.

Readme

@myexamsai/revelio

Server-Driven UI for React and React Native.
Your server ships JSON. Revelio renders it live — with surgical per-node re-renders, offline caching, real-time SSE updates, network-aware asset resolution, and full TypeScript + Zod support.


Table of Contents


Installation

npm install @myexamsai/revelio zod

Web peer deps (already a transitive dep of most React setups):

npm install react react-dom

React Native peer deps:

npm install react-native react-native-sse @react-native-community/netinfo

idb is a direct dependency (used by the Reel web backend) — it installs automatically.


Quick Start

Web

// app/page.tsx  (Next.js App Router, or any React web app)
import { useBlueprint } from '@myexamsai/revelio/web'
import { createBroadcast } from '@myexamsai/revelio/web'
import { createReel } from '@myexamsai/revelio/web'
import { createSignal } from '@myexamsai/revelio/web'
import { createLens } from '@myexamsai/revelio/web'
import { useBlock } from '@myexamsai/revelio/web'
import { z } from 'zod'

// 1. Define your component factories
const Hero = (nodeId, parentPath, renderNode) => {
  const { state } = useBlock({
    nodeId,
    schema: z.object({ title: z.string(), subtitle: z.string().optional() }),
  })
  return (
    <section>
      <h1>{state?.title}</h1>
      {state?.subtitle && <p>{state.subtitle}</p>}
      {renderNode(nodeId + '-cta', [...parentPath, nodeId])}
    </section>
  )
}

const components = { hero: Hero, button: Button, text: Text }

// 2. Create optional subsystems once (module scope or useMemo)
const reel      = createReel({ ttl: 300_000 })      // 5-min IndexedDB cache
const signal    = createSignal()                      // network-quality detection
const broadcast = createBroadcast({                   // SSE real-time updates
  url: 'https://api.example.com/sse/home-page',
})
const lens = createLens((asset, { dpr, tier }) => {   // ImageKit resolver
  const quality = tier === 'strong' ? 85 : tier === 'good' ? 70 : 50
  const scale   = Math.min(dpr, tier === 'strong' ? 2 : tier === 'good' ? 1.5 : 1)
  return `https://ik.imagekit.io/myapp/${asset.id}?tr=q-${quality},dpr-${scale}`
})

// 3. Render
export default function Page() {
  const { renderer, status, error } = useBlueprint({
    url: 'https://api.example.com/blueprints/home-page',
    components,
    reel,
    signal,
    broadcast,
    lens,
  })

  if (status === 'loading') return <Skeleton />
  if (status === 'error')   return <ErrorBanner error={error} />
  return renderer
}

React Native

import { useBlueprint } from '@myexamsai/revelio/native'
import { createBroadcast } from '@myexamsai/revelio/native'
import { createNativeReel } from '@myexamsai/revelio/native'
import { createNativeSignal } from '@myexamsai/revelio/native'
import NetInfo from '@react-native-community/netinfo'
import AsyncStorage from '@react-native-async-storage/async-storage'

const reel      = createNativeReel(AsyncStorage, { ttl: 300_000 })
const signal    = createNativeSignal(NetInfo)
const broadcast = createBroadcast({ url: 'https://api.example.com/sse/home' })

export default function HomeScreen() {
  const { renderer, status } = useBlueprint({
    url: 'https://api.example.com/blueprints/home',
    components,
    reel,
    signal,
    broadcast,
  })

  if (status === 'loading') return <LoadingSpinner />
  return renderer
}

Blueprint JSON Format

Revelio consumes Blueprint documents delivered as JSON from your server.

Structural Blueprint (full render)

{
  "version": "1.0.0",
  "metadata": {
    "id": "home-page",
    "kind": "structural",
    "updatedAt": "2025-01-15T10:30:00Z"
  },
  "root": {
    "id": "root",
    "type": "page",
    "state": { "title": "Home" },
    "children": [
      {
        "id": "hero-1",
        "type": "hero",
        "state": {
          "title": "Welcome back",
          "subtitle": "Here is what's new today."
        },
        "children": [
          {
            "id": "cta-1",
            "type": "button",
            "state": { "label": "Get started", "variant": "primary" }
          }
        ]
      }
    ]
  },
  "variables": {
    "userId": "u_abc123",
    "theme": "dark"
  }
}

Delta Blueprint (incremental update)

Sent by the server over SSE to patch only changed nodes. kind: "update" is required, as is a monotonically increasing sequenceNumber. Stale or duplicate sequence numbers are discarded automatically.

{
  "version": "1.0.0",
  "metadata": {
    "id": "home-page",
    "kind": "update",
    "sequenceNumber": 42
  },
  "root": { "id": "root", "type": "page", "state": {} },
  "nodes": [
    {
      "id": "hero-1",
      "type": "hero",
      "state": {
        "title": "New title from the server",
        "subtitle": "Updated in real time."
      }
    }
  ]
}

Only the nodes listed in nodes are updated. Every other node is untouched.

Metadata fields

| Field | Type | Description | |---|---|---| | id | string | Document identifier. | | kind | 'structural' \| 'update' | Defaults to 'structural' if omitted. | | sequenceNumber | number | Required for kind: 'update'. Duplicate/stale numbers are discarded. | | updatedAt | string | ISO timestamp used as part of the identity check in Screen. |


Component Factories

A ComponentFactory is a function that receives a node ID and parent path and returns a React element (or null). It is called for every node in the Blueprint tree.

type ComponentFactory = (
  id: string,
  parentPath: ParentPath,
  renderNode: RenderNodeFn,
) => React.ReactElement | null
import { useBlock } from '@myexamsai/revelio'
import { z } from 'zod'

const CardSchema = z.object({
  title: z.string(),
  imageId: z.string().optional(),
})

const Card: ComponentFactory = (nodeId, parentPath, renderNode) => {
  const { state, children } = useBlock({ nodeId, schema: CardSchema })

  return (
    <div className="card">
      <h2>{state?.title}</h2>
      {children.map((childId) => renderNode(childId, [...parentPath, nodeId]))}
    </div>
  )
}

Register your factories in the components map and pass it to useBlueprint or Screen:

const components = {
  card: Card,
  hero: Hero,
  button: Button,
  text: Text,
}

Architecture Overview

Server (JSON)
     │  HTTP fetch + SSE stream
     ▼
 useBlueprint
     ├── Reel.get()        ── immediate render from cache (IndexedDB / AsyncStorage)
     ├── fetch(url)        ── skipped when Signal tier is 'offline'
     ├── Stage.receive()   ── normalize + store the Blueprint
     ├── Reel.store()      ── persist to cache
     └── Broadcast (SSE)
           └── Stage.receive() + Reel.patch()  ── live delta updates

 Stage (store)
     ├── Ledger            ── flat map of node id → node state (normalized)
     ├── Oracle            ── per-node pub/sub for surgical re-renders
     ├── Archivist         ── routing: structural → full rebuild, update → delta merge
     └── Vars              ── global Blueprint variables

 React tree
     └── RevelioProvider
           └── ScreenInner
                 └── useRenderBlock
                       └── ComponentFactory → useBlock (per node)

Every component subscribes only to its own node via useBlock. A delta update that changes one node triggers a re-render of exactly one component.


API Reference

useBlueprint

The primary integration hook. Fetches a Blueprint, manages offline caching, wires up SSE real-time updates, and returns a ready-to-render element.

import { useBlueprint } from '@myexamsai/revelio/web'    // web
import { useBlueprint } from '@myexamsai/revelio/native'  // React Native
function useBlueprint(options: UseBlueprintOptions): UseBlueprintReturn

UseBlueprintOptions

| Prop | Type | Required | Description | |---|---|---|---| | url | string | ✓ | URL of the Blueprint endpoint. | | components | Record<string, ComponentFactory> | ✓ | Map of node type → factory. | | componentOverrides | { byNodeId?, byNodeType? } | | Override factories per node id or type. | | fetchOptions | RequestInit | | Options forwarded to fetch(). | | reel | Reel | | Offline cache. Created with createReel() / createNativeReel(). | | signal | Signal | | Network quality detector. When tier is 'offline', the HTTP fetch is skipped and the cached Blueprint is used instead. | | broadcast | BroadcastChannel | | SSE channel for real-time updates. Created with createBroadcast(). | | lens | Lens | | Asset resolver for images. Created with createLens(). | | onLayoutChange | (bp: Blueprint) => void | | Called when a new structural Blueprint is applied. | | onError | (err: Error) => void | | Called on fetch/parse errors. |

UseBlueprintReturn

| Field | Type | Description | |---|---|---| | renderer | React.ReactElement \| null | The fully rendered UI tree. Render this directly. | | status | RevelioStatus | 'idle' \| 'loading' \| 'success' \| 'error' | | error | Error \| null | Set on fetch or validation failure. | | stage | Stage | The underlying store for imperative operations. | | dispatch | (action: RevelioAction) => Promise<RevelioActionResponse> | Fire an action to your server. |

Hydration sequence

  1. Reel.get() — If a cached Blueprint exists, render it immediately (instant first paint).
  2. fetch(url) — Unless signal.getTier() === 'offline', fetch a fresh Blueprint from the server.
  3. Stage.receive(fresh) — Replace or delta-merge the store.
  4. Reel.store(fresh) — Persist the fresh Blueprint to cache.
  5. Broadcast (SSE) — On every SSE message: Stage.receive(delta) + Reel.patch(delta).

Screen

Standalone renderer for when you manage your own Stage instance. No internal store is created — the component identity-tracks the document via metadata.id + version + metadata.updatedAt.

import { Screen } from '@myexamsai/revelio'
<Screen
  document={blueprint}
  components={components}
  componentOverrides={{ byNodeId: { 'hero-1': CustomHero } }}
  onLayoutChange={(bp) => console.log('new blueprint', bp)}
  onError={(err) => console.error(err)}
/>

Props

| Prop | Type | Required | Description | |---|---|---|---| | document | Blueprint | ✓ | The Blueprint to render. | | components | Record<string, ComponentFactory> | | Custom component map. | | componentOverrides | { byNodeId?, byNodeType? } | | Factory overrides per node. | | onLayoutChange | (bp: Blueprint) => void | | Called when the document identity changes. | | onError | (err: Error) => void | | Called on invalid Blueprint input. |


Stage

The central store. Holds all node state, manages subscriptions, and processes Blueprint updates.

import { Stage } from '@myexamsai/revelio'

const stage = new Stage()
stage.receive(blueprint)

Constructor

new Stage(initialState?, options?)

| Param | Type | Description | |---|---|---| | initialState | Partial<SduiLayoutStoreState> | Optional seed state (useful for SSR). | | options | SduiLayoutStoreOptions | { componentOverrides? } |

Methods

| Method | Signature | Description | |---|---|---| | receive | (blueprint: Blueprint) => void | Apply a Blueprint. Routes to a full Ledger rebuild (kind: 'structural') or incremental delta merge (kind: 'update'). This is the only write method you need in normal operation. | | updateNode | (id: string, state: Record<string, unknown>) => void | Imperatively update a single node's state and set the edited flag. | | cancelEdit | () => void | Clear the edited flag without reverting state. | | reset | () => void | Revert all node state to the last received structural Blueprint. | | clear | () => void | Hard teardown — clears all state and subscriptions. | | getBlueprint | () => Blueprint \| null | Returns the current Blueprint (denormalized from Ledger). | | subscribeVersion | (cb: () => void) => () => void | Subscribe to any store change. Returns an unsubscribe function. | | getSnapshot | () => number | Returns the current version counter (for useSyncExternalStore). | | getServerSnapshot | () => number | Always returns 0 (for SSR hydration). |

State

stage.state  // SduiLayoutStoreState

| Field | Type | Description | |---|---|---| | rootId | string \| null | ID of the root node. | | nodes | Record<string, SduiLayoutNode> | Flat map of all nodes (normalized). | | variables | Record<string, unknown> | Global Blueprint variables. | | version | number | Increments on every change. | | isEdited | boolean | True after updateNode() until cancelEdit() or reset(). |


RevelioProvider / useRevelioContext

RevelioProvider makes a Stage (and optionally Lens, Signal, Reel) available to the component tree below it. useBlueprint sets this up automatically — use these directly only when you are managing your own Stage.

import { RevelioProvider, useRevelioContext } from '@myexamsai/revelio'

<RevelioProvider store={stage} lens={lens} signal={signal}>
  <ScreenInner id={rootId} componentMap={components} />
</RevelioProvider>
const { store, lens, signal, reel } = useRevelioContext()

RevelioContextValue

| Field | Type | |---|---| | store | Stage | | lens | Lens \| undefined | | signal | Signal \| undefined | | reel | Reel \| undefined |


useBlock

Subscribe to a single node. Re-renders only when that specific node's state changes (useSyncExternalStore under the hood — tearing-safe with React 18+ concurrent mode).

import { useBlock } from '@myexamsai/revelio'
const MyButton: ComponentFactory = (nodeId) => {
  const { state, children, type } = useBlock({
    nodeId,
    schema: z.object({ label: z.string(), variant: z.enum(['primary', 'secondary']) }),
  })

  return <button data-variant={state?.variant}>{state?.label}</button>
}

UseBlockParams

| Param | Type | Required | Description | |---|---|---|---| | nodeId | string | ✓ | The node to subscribe to. | | schema | ZodSchema<TSchema> | | Zod schema to validate and type state. |

UseBlockReturn

| Field | Type | Description | |---|---|---| | state | TSchema \| undefined | Validated node state. undefined if the node is absent or validation fails. | | children | string[] | Ordered list of child node IDs. | | type | string \| undefined | Node type string (e.g. 'hero', 'button'). | | node | SduiLayoutNode \| undefined | The full raw node object. |


useNodeRef

Subscribe to one or more nodes by reference — useful for cross-node data (e.g. a tooltip that reads state from an unrelated node).

import { useNodeRef } from '@myexamsai/revelio'
const Modal: ComponentFactory = (nodeId) => {
  // source node has state.targetId = 'hero-1'
  const { nodes } = useNodeRef({
    nodeId,
    refKey: 'targetId',
    schema: z.object({ title: z.string() }),
  })

  const target = nodes[0]
  return <div>{target?.state?.title}</div>
}

UseNodeRefParams

| Param | Type | Required | Description | |---|---|---|---| | nodeId | string | ✓ | Source node that holds the reference. | | refKey | string | ✓ | Key in state whose value is a node ID (or array of node IDs) to watch. | | schema | ZodSchema | | Zod schema applied to each referenced node's state. |

Return

| Field | Type | Description | |---|---|---| | nodes | ReferencedNodeInfo[] | Array of { id, type, state, children } for each referenced node. |


useRenderBlock / useRenderNode

Low-level hook used internally to recursively render child nodes. You rarely call this directly — the renderNode argument passed to your factory already wraps it.

import { useRenderBlock } from '@myexamsai/revelio'

const { renderNode } = useRenderBlock({
  nodeId: rootId,
  componentMap: components,
  parentPath: [],
})

return renderNode(rootId, [])

useRenderNode is an alias for useRenderBlock.


useSduiLayoutAction

Returns the Stage store imperatively from within a component tree rendered by RevelioProvider. Useful for firing local mutations without going through dispatch.

import { useSduiLayoutAction } from '@myexamsai/revelio'

const stage = useSduiLayoutAction()
stage.updateNode('hero-1', { title: 'Edited locally' })

Broadcast (SSE)

Real-time updates via Server-Sent Events. On disconnect, createBroadcast waits one second, re-fetches the full Blueprint first, then reconnects — so you never miss a structural change during an outage.

import { createBroadcast } from '@myexamsai/revelio/web'    // uses native EventSource
import { createBroadcast } from '@myexamsai/revelio/native'  // uses react-native-sse
const broadcast = createBroadcast({
  url: 'https://api.example.com/sse/home-page',
  eventName: 'blueprint',      // default: 'message'
  withCredentials: true,
  onMessage: (blueprint) => {
    stage.receive(blueprint)
  },
  onError: (err) => console.error('SSE error', err),
  onStatusChange: (status) => console.log('SSE status:', status),
})

broadcast.connect()
// later:
broadcast.disconnect()

When passed to useBlueprint, the connect() / disconnect() lifecycle and onMessage wiring are managed automatically.

BroadcastOptions

| Option | Type | Default | Description | |---|---|---|---| | url | string | required | SSE endpoint URL. | | eventName | string | 'message' | SSE event type to listen for. | | withCredentials | boolean | false | Set EventSource.withCredentials. | | onMessage | (bp: Blueprint) => void | | Called for each inbound Blueprint. | | onError | (err: Error) => void | | Called on SSE errors. | | onStatusChange | (s: 'connecting' \| 'live' \| 'offline') => void | | Connection state change callback. |

BroadcastChannel

| Member | Type | Description | |---|---|---| | connect() | () => void | Open the SSE connection. | | disconnect() | () => void | Close the SSE connection. | | status | 'connecting' \| 'live' \| 'offline' | Current connection state. |


Signal (Network Quality)

Signal detects the user's network quality and exposes a SignalTier. Revelio uses it to skip HTTP fetches when offline and to cap image DPR/quality in Lens.

import { createSignal }       from '@myexamsai/revelio/web'    // web: navigator.connection
import { createNativeSignal } from '@myexamsai/revelio/native'  // native: NetInfo
// Web
const signal = createSignal()

// React Native
import NetInfo from '@react-native-community/netinfo'
const signal = createNativeSignal(NetInfo)

signal.getTier()  // 'strong' | 'good' | 'weak' | 'offline'

const unsub = signal.subscribe((tier) => console.log('tier changed:', tier))
unsub()  // unsubscribe

SignalTier

| Tier | Meaning | Lens DPR cap | Lens quality | |---|---|---|---| | 'strong' | Fast WiFi / 5G | 2.0× | 85 | | 'good' | LTE / decent WiFi | 1.5× | 70 | | 'weak' | 3G or degraded | 1.0× | 50 | | 'offline' | No connectivity | — | — (fetch skipped) |

When @react-native-community/netinfo or navigator.connection is not available, createSignal / createNativeSignal return a stub that always reports 'strong'.

Signal interface

interface Signal {
  getTier(): SignalTier
  subscribe(cb: (tier: SignalTier) => void): () => void
}

Access inside components with useSignal():

import { useSignal } from '@myexamsai/revelio'
const tier = useSignal()  // SignalTier | null

Lens (Asset Resolution)

Lens resolves asset references (IDs or URLs) into final image URLs, DPR-capped and quality-adjusted for the current network tier.

import { createLens, useImageResolver } from '@myexamsai/revelio/web'
const lens = createLens((asset, { containerWidth, dpr, tier }) => {
  const quality  = tier === 'strong' ? 85 : tier === 'good' ? 70 : 50
  const cappedDpr = Math.min(dpr, tier === 'strong' ? 2 : tier === 'good' ? 1.5 : 1)
  const w = Math.round(containerWidth * cappedDpr)

  if ('url' in asset) return asset.url   // passthrough for bare URLs

  // Example: ImageKit
  return `https://ik.imagekit.io/myapp/${asset.id}?tr=w-${w},q-${quality}`
})

Pass lens to useBlueprint. Inside component factories, use useImageResolver:

import { useImageResolver } from '@myexamsai/revelio'

const HeroImage: ComponentFactory = (nodeId) => {
  const { state } = useBlock({ nodeId })
  const resolve = useImageResolver({ containerWidth: 800 })

  if (!state?.image) return null
  return <img src={resolve(state.image)} alt="" />
}

createLens

function createLens(resolver?: LensResolver): Lens

Called with no resolver, returns a passthrough lens (raw URL, or asset ID as a string).

LensResolver

type LensResolver = (asset: Asset, ctx: LensContext) => string

interface LensContext {
  containerWidth: number
  dpr: number        // window.devicePixelRatio (read at call time, capped by Signal tier)
  tier: SignalTier
}

type Asset    = AssetRef | AssetUrl
interface AssetRef { id: string }
interface AssetUrl { url: string }

useImageResolver

function useImageResolver(options: { containerWidth: number }): (asset: Asset) => string

Returns a resolver function that automatically reads the current dpr and tier from RevelioContext.


Reel (Offline Cache)

Reel persists Blueprints so the last-known UI is available instantly on the next load, before the network request completes.

import { createReel }       from '@myexamsai/revelio/web'    // IndexedDB via idb
import { createNativeReel } from '@myexamsai/revelio/native'  // AsyncStorage
// Web
const reel = createReel({ ttl: 300_000 })   // 5-minute TTL (optional, default: Infinity)

// React Native
import AsyncStorage from '@react-native-async-storage/async-storage'
const reel = createNativeReel(AsyncStorage, { ttl: 300_000 })

Pass reel to useBlueprint and the full store → get → patch lifecycle is handled automatically.

Reel interface

interface Reel {
  store(key: string, blueprint: Blueprint): Promise<void>
  get(key: string): Promise<Blueprint | null>
  patch(key: string, delta: Blueprint): Promise<void>
  clear(key?: string): Promise<void>
}

| Method | Description | |---|---| | store(key, blueprint) | Persist a full structural Blueprint under key. | | get(key) | Retrieve a cached Blueprint. Returns null if absent or expired. | | patch(key, delta) | Apply a delta Blueprint to the cached structural one in-place. | | clear(key?) | Delete one entry (or all entries if key is omitted). |

The cache key is derived from the Blueprint URL. In SSR environments where IndexedDB is unavailable, Reel falls back to an in-memory store automatically — the API is always safe to call.


Types Reference

All types are re-exported from @myexamsai/revelio and the platform entry points.

// Blueprint document
import type { Blueprint, BlueprintNode, BlueprintMetadata } from '@myexamsai/revelio'

// Schema / node
import type { SduiLayoutDocument, SduiLayoutNode, SduiNode } from '@myexamsai/revelio'

// Store
import type { SduiLayoutStoreOptions, SduiLayoutStoreState } from '@myexamsai/revelio'

// Component system
import type {
  ComponentFactory,
  RenderNodeFn,
  SduiComponentProps,
  ParentPath,
} from '@myexamsai/revelio'

// Hooks
import type {
  UseBlockParams,
  UseBlockReturn,
  UseNodeRefParams,
  ReferencedNodeInfo,
  UseRenderNodeParams,
  UseRenderNodeReturn,
  RevelioContextValue,
} from '@myexamsai/revelio'

// useBlueprint
import type {
  UseBlueprintOptions,
  UseBlueprintReturn,
  RevelioAction,
  RevelioActionResponse,
  RevelioStatus,
} from '@myexamsai/revelio/web'

// Signal
import type { Signal, SignalTier } from '@myexamsai/revelio'

// Lens
import type { Lens, LensResolver, LensContext, Asset, AssetRef, AssetUrl } from '@myexamsai/revelio'

// Reel
import type { Reel } from '@myexamsai/revelio'

// Broadcast
import type { BroadcastChannel, BroadcastOptions } from '@myexamsai/revelio/web'

// Normalization utilities (advanced)
import {
  normalizeSduiLayout,
  normalizeSduiNode,
  denormalizeSduiLayout,
  denormalizeSduiNode,
} from '@myexamsai/revelio'
import type { NormalizedSduiEntities } from '@myexamsai/revelio'

License

MIT © MyExamsAI