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

@procedures/splay

v0.1.0

Published

Render procedure results with splay: a bridge between the client procedures and the splay recursive renderer, with stream helpers.

Readme

@procedures/splay

npm

npm install @procedures/splay

License: MIT TypeScript Node.js

Bridge between splay's rendering model and client's procedure system. Enables server-side component rendering via RPC.

Table of Contents


Overview

client-splay bridges the splay rendering model with the RPC procedure system:

  • Client Registry - Call component procedures via RPC
  • Streaming Registry - Handle async component streams
  • Hydration - Convert descriptors to framework elements (React, etc.)
  • Utilities - Walk, transform, and validate descriptor trees

Installation

npm install github:mark1russell7/client-splay#main

Architecture

System Overview

graph TB
    subgraph "Application"
        App[Your Application]
    end

    subgraph "client-splay"
        subgraph "Registry Layer"
            ClientReg[ClientRegistry<br/>Sync calls]
            StreamReg[StreamingRegistry<br/>Async iterables]
            DualReg[DualRegistry<br/>Both modes]
        end

        subgraph "Hydration Layer"
            Hydrate[createHydrate]
            ReactHydrate[createReactHydrate]
        end
    end

    subgraph "RPC Layer"
        Client[client.call]
        Stream[client.stream]
    end

    subgraph "Output"
        Descriptor[ComponentOutput]
        Element[React Element]
    end

    App --> ClientReg
    App --> StreamReg
    ClientReg --> Client
    StreamReg --> Stream
    Client --> Descriptor
    Stream --> Descriptor
    Descriptor --> Hydrate
    Descriptor --> ReactHydrate
    ReactHydrate --> Element

Component Rendering Flow

sequenceDiagram
    participant App as Application
    participant Reg as ClientRegistry
    participant RPC as client.call
    participant Server as Procedure Server
    participant Hydrate as Hydrator

    App->>Reg: registry.get("user-card")(ctx)
    Reg->>RPC: call(["ui", "user-card"], input)
    RPC->>Server: Execute procedure
    Server-->>RPC: ComponentOutput
    RPC-->>Reg: Descriptor
    Reg-->>App: ComponentOutput

    App->>Hydrate: hydrate(descriptor)
    Hydrate->>Hydrate: Map to React components
    Hydrate-->>App: React.Element

Registry Types

graph TB
    subgraph "ClientRegistry"
        CR[Sync calls]
        CROut[Single ComponentOutput]
    end

    subgraph "StreamingRegistry"
        SR[Async streams]
        SROut[AsyncIterable of ComponentOutput]
    end

    subgraph "DualRegistry"
        DR[Both modes]
        DROut[Sync + Streaming]
    end

    CR --> CROut
    SR --> SROut
    DR --> DROut

Quick Start

import { createClientRegistry, createReactHydrate } from "@procedures/splay";
import { call } from "client";

// 1. Create registry backed by client procedures
const registry = createClientRegistry(call, { namespace: "ui" });

// 2. Create hydrator for React
const hydrate = createReactHydrate(React, {
  components: {
    "user-card": UserCard,
    "badge": Badge,
  },
});

// 3. Render a component
const ctx = { data: { userId: 123 }, size: { width: 400, height: 300 } };
const descriptor = await registry.get("user-card")!(ctx);
const element = hydrate(descriptor);

API Reference

Registry

ClientRegistry

Sync registry for single-response component calls.

const registry = createClientRegistry(call, {
  namespace?: string,  // Procedure path prefix (e.g., "ui")
});

// Usage
const descriptor = await registry.get("user-card")!(ctx);

StreamingRegistry

Streaming registry for multiple responses over time.

const streaming = createStreamingRegistry(stream, {
  namespace?: string,
  bufferSize?: number,  // Default: 16
});

// Usage
for await (const descriptor of streaming.get("live-feed")!(ctx)) {
  render(descriptor);
}

DualRegistry

Combined registry supporting both sync and streaming.

const dual = createDualRegistry(call, stream, isStreaming, options);

// Automatically chooses based on component type
const result = await dual.get("component-name")!(ctx);

Hydration

createHydrate (Generic)

Framework-agnostic hydration.

const hydrate = createHydrate<TComponent>({
  components: ComponentMap,       // Map of type -> component
  fallback?: Component,           // For unknown types
  onNull?: () => TComponent,      // Handle null descriptors
  onFragment?: (children) => TComponent,  // Handle fragments
});

createReactHydrate

React-specific hydration with createElement integration.

const hydrate = createReactHydrate(React, {
  components: {
    "user-card": UserCard,
    "badge": Badge,
    "button": Button,
  },
  fallback?: UnknownComponent,
});

// Usage
const element = hydrate(descriptor);
// Returns: React.Element

Descriptor Utilities

// Traverse descriptor tree
walkDescriptor(descriptor, (node, path) => {
  console.log(`Node at ${path.join(".")}: ${node.type}`);
});

// Transform nodes
const transformed = transformDescriptor(descriptor, node => ({
  ...node,
  props: { ...node.props, className: "styled" }
}));

// Find matching nodes
const buttons = findInDescriptor(descriptor, n => n.type === "button");

// Get all component types used
const types: Set<string> = getUsedTypes(descriptor);

// Validate against known types
const { valid, unknownTypes } = validateDescriptor(descriptor, knownTypes);

// Serialization
const json = serializeDescriptor(descriptor);
const parsed = parseDescriptor(json);
const compact = compactDescriptor(descriptor); // Remove empty values

Stream Utilities

// Merge multiple component streams
for await (const output of mergeStreams(stream1, stream2)) {
  render(output);
}

// Rate limiting
for await (const output of throttleStream(stream, 100)) {
  // Max 1 update per 100ms
  render(output);
}

for await (const output of debounceStream(stream, 200)) {
  // Wait 200ms of quiet before emitting
  render(output);
}

ComponentOutput Format

The wire format for component descriptors:

interface ComponentOutput {
  type: string;                    // Component type or "__null__" / "__fragment__"
  props: Record<string, unknown>;  // Component props
  children?: ComponentOutput[];    // Nested children
  key?: string | number;           // React key
}

Special Types

graph LR
    subgraph "Standard"
        Component["type: 'user-card'"]
    end

    subgraph "Null"
        Null["type: '__null__'<br/>Renders nothing"]
    end

    subgraph "Fragment"
        Fragment["type: '__fragment__'<br/>Wraps children"]
    end

Example Descriptor

{
  "type": "user-card",
  "props": { "userId": 123, "showBadge": true },
  "children": [
    {
      "type": "badge",
      "props": { "label": "Admin" }
    }
  ]
}

Integration

Built-in Procedures

| Path | Description | |------|-------------| | splay.bridge.info | Package information | | splay.bridge.health | Health check |

Re-exports from client/components

import {
  // Types
  ComponentOutput, FragmentOutput, NullOutput, Size,
  ComponentContext, ComponentFactory, ComponentDefinition,

  // Helpers
  nullOutput, fragment, isFragment, isNullOutput,
  defineComponent, simpleComponent, streamingComponent,
} from "@procedures/splay";

Package Hierarchy

graph BT
    subgraph "UI Layer"
        SplayReact[splay-react]
    end

    subgraph "Bridge"
        ClientSplay[client-splay]
    end

    subgraph "Core"
        Splay[splay]
        Client[client]
    end

    SplayReact --> ClientSplay
    ClientSplay --> Splay
    ClientSplay --> Client

Requirements

  • Node.js >= 25
  • Dependencies:
    • @procedures/client
    • @mark1russell7/splay

License

MIT