Pulse
pulse-client (JS)

Classes

Core classes for Pulse client functionality

Classes

Core classes for Pulse client functionality.


ChannelBridge

Manages bidirectional communication over a Pulse channel. You get a ChannelBridge instance from the usePulseChannel hook.

class ChannelBridge {
  readonly id: string;

  emit(event: string, payload?: any): void;
  request(event: string, payload?: any): Promise<any>;
  on(event: string, handler: ChannelEventHandler): () => void;
}

type ChannelEventHandler = (payload: any) => any | Promise<any>;

Properties

PropertyTypeDescription
idstringThe channel identifier

Methods

emit

Sends a fire-and-forget event to the server. The server receives the event but does not send a response.

emit(event: string, payload?: any): void
ParameterTypeDescription
eventstringEvent name
payloadanyOptional event data

Example:

const channel = usePulseChannel("notifications");

// Notify server of user activity (no response expected)
channel.emit("user_active", { page: "/dashboard" });

// Send typing indicator
channel.emit("typing", { conversationId: "123" });

request

Sends a request and waits for the server's response. This is like a remote procedure call.

request(event: string, payload?: any): Promise<any>
ParameterTypeDescription
eventstringEvent name
payloadanyOptional request data

Returns: Promise resolving to the server's response.

Throws: PulseChannelResetError if the channel closes before the response arrives.

Example:

const channel = usePulseChannel("api");

// Fetch data from server
const users = await channel.request("get_users", { limit: 10 });

// Perform action and get result
const result = await channel.request("process_order", {
  items: ["item-1", "item-2"],
  shipping: "express",
});

on

Registers an event handler. Returns a function to unsubscribe.

on(event: string, handler: ChannelEventHandler): () => void
ParameterTypeDescription
eventstringEvent name to listen for
handlerChannelEventHandlerCallback function

Returns: Unsubscribe function.

The handler can be sync or async. If multiple handlers are registered for the same event, all are called. For request-response patterns, the first handler that returns a non-undefined value provides the response.

Example:

const channel = usePulseChannel("notifications");

// Subscribe to events
useEffect(() => {
  const unsubscribe = channel.on("notification", (data) => {
    showToast(data.message);
  });

  return unsubscribe;
}, [channel]);

// Handle server requests
useEffect(() => {
  return channel.on("confirm_action", async (data) => {
    const confirmed = await showConfirmDialog(data.message);
    return { confirmed };
  });
}, [channel]);

Event Backlog

If an event arrives before any handler is registered, it's queued in a backlog. Once a handler is registered for that event, backlogged events are delivered immediately. This prevents race conditions during component mounting.

Complete Example

import { usePulseChannel, PulseChannelResetError } from "pulse-ui-client";
import { useState, useEffect } from "react";

function LiveAuction({ auctionId }: { auctionId: string }) {
  const channel = usePulseChannel(`auction-${auctionId}`);
  const [currentBid, setCurrentBid] = useState(0);
  const [error, setError] = useState<string | null>(null);

  // Listen for bid updates
  useEffect(() => {
    return channel.on("bid_update", (data) => {
      setCurrentBid(data.amount);
    });
  }, [channel]);

  // Place a bid
  const placeBid = async (amount: number) => {
    try {
      const result = await channel.request("place_bid", { amount });
      if (!result.success) {
        setError(result.error);
      }
    } catch (e) {
      if (e instanceof PulseChannelResetError) {
        setError("Connection lost. Please refresh.");
      }
    }
  };

  return (
    <div>
      <div>Current bid: ${currentBid}</div>
      {error && <div className="error">{error}</div>}
      <button onClick={() => placeBid(currentBid + 10)}>
        Bid ${currentBid + 10}
      </button>
    </div>
  );
}

PulseChannelResetError

Error thrown when a channel operation fails due to channel closure or disconnection.

class PulseChannelResetError extends Error {
  name: "PulseChannelResetError";
}

When It's Thrown

  • Channel is closed by the server
  • WebSocket connection is lost
  • request() is pending when the channel closes
  • Attempting to use a closed channel

Example

import { PulseChannelResetError } from "pulse-ui-client";

async function fetchData(channel: ChannelBridge) {
  try {
    return await channel.request("get_data");
  } catch (error) {
    if (error instanceof PulseChannelResetError) {
      // Handle channel closure gracefully
      console.log("Channel closed:", error.message);
      return null;
    }
    throw error;
  }
}

VDOMRenderer

Converts VDOM trees to React elements and applies incremental updates. This is used internally by PulseView, but you can use it directly for custom rendering scenarios.

class VDOMRenderer {
  constructor(
    client: PulseSocketIOClient,
    path: string,
    registry?: ComponentRegistry
  );

  init(view: PulsePrerenderView): ReactNode;
  applyUpdates(tree: ReactNode, updates: VDOMUpdate[]): ReactNode;
  renderNode(node: VDOMNode, currentPath?: string): ReactNode;
  evaluateExpr(expr: VDOMNode): unknown;
}

Constructor

constructor(
  client: PulseSocketIOClient,
  path: string,
  registry?: ComponentRegistry
)
ParameterTypeDescription
clientPulseSocketIOClientThe Pulse client for callbacks
pathstringView path for callback routing
registryComponentRegistryOptional component registry

Methods

init

Initializes rendering from a prerendered view.

init(view: PulsePrerenderView): ReactNode
ParameterTypeDescription
viewPulsePrerenderViewView with VDOM data

Returns: React node tree.

applyUpdates

Applies incremental VDOM updates to an existing React tree. This is the core of Pulse's efficient updates.

applyUpdates(tree: ReactNode, updates: VDOMUpdate[]): ReactNode
ParameterTypeDescription
treeReactNodeCurrent React tree
updatesVDOMUpdate[]Updates from server

Returns: New React tree with updates applied.

renderNode

Renders a single VDOM node to a React node.

renderNode(node: VDOMNode, currentPath?: string): ReactNode
ParameterTypeDescription
nodeVDOMNodeVDOM node to render
currentPathstringOptional path for callbacks

Returns: React node.

evaluateExpr

Evaluates a VDOM expression node. Used for run_js support and computed props.

evaluateExpr(expr: VDOMNode): unknown
ParameterTypeDescription
exprVDOMNodeExpression node to evaluate

Returns: Evaluated result.

Example: Custom Renderer

import { VDOMRenderer, usePulseClient, usePulsePrerender } from "pulse-ui-client";
import { useMemo, useState, useEffect } from "react";

function CustomView({ path }: { path: string }) {
  const client = usePulseClient();
  const prerender = usePulsePrerender(path);

  const renderer = useMemo(
    () => new VDOMRenderer(client, path, {}),
    [client, path]
  );

  const [tree, setTree] = useState(() => renderer.init(prerender));

  // Apply updates when they arrive (simplified)
  // In practice, you'd subscribe via client.attach()

  return <div className="custom-wrapper">{tree}</div>;
}

See Also

On this page