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

@dblm/react

v0.1.0

Published

React chat UI + client for querying dblm in natural language (NLQ) through the dblm broker/middleware. A drop-in, fully themeable ChatGPT-style widget with multi-turn sessions.

Readme

@dblm/react

A drop-in, fully themeable NLQ chat widget for React — a ChatGPT-style UI for querying your databases in plain English through dblm. Users chat, start new sessions, and go back and forth in a conversation; you handle auth and theme. We handle the business logic.

React app  ──►  @dblm/react  ──►  (your backend | @dblm/middleware)  ──►  dblm broker  ──►  database
  • 💬 Multi-turn chat with a session sidebar — new / rename / delete, persisted across reloads.
  • 🧠 Server-side memory kept in sync automatically (each turn replays the session id).
  • 🎨 Fully themeable via CSS variables, a theme prop, per-slot classNames, or whole-component overrides. Light + dark, zero styling deps.
  • 📊 Answers render the natural-language summary with collapsible generated SQL and a scrollable data table.
  • 🔌 Pluggable transport — talk to @dblm/middleware directly, proxy through your own backend, or mock it.
  • 🪝 Headless hook (useNlqChat) if you'd rather build your own layout.

Install

npm install @dblm/react

react and react-dom (v18+) are peer dependencies.

Quick start

Import the widget and the default skin, wrap it in a DblmProvider, and give it a transport. Here's a zero-backend demo using a mock transport:

import { DblmProvider, DblmChat, customTransport } from '@dblm/react';
import '@dblm/react/styles.css';

const transport = customTransport(async (req) => ({
  question: req.question,
  summary: `You asked: ${req.question}`,
  results: [{ source: 'pg', query: 'SELECT 1', result: { columns: ['n'], rows: [[1]], source: 'pg' } }],
}));

export default function App() {
  return (
    <DblmProvider config={{ transport }}>
      <div style={{ height: 600 }}>
        <DblmChat />
      </div>
    </DblmProvider>
  );
}

The widget fills its parent, so give the parent a height.

Connecting to dblm

@dblm/react never talks to a database directly — it sends questions to a dblm broker via @dblm/middleware. The /qd endpoint requires a service API key plus the end user's client certificate + key (from /register). There are two supported ways to wire this up — pick based on where you want those secrets to live.

Option A — proxyTransport (recommended for public apps)

Your browser posts to your own backend, which holds the API key + user cert and forwards to the middleware. No secrets ever reach client-side JS.

import { proxyTransport } from '@dblm/react';

const transport = proxyTransport({
  endpoint: '/api/dblm/qd',      // your route
  credentials: 'include',        // send your app's auth cookie
  // headers: () => ({ Authorization: `Bearer ${token}` }),  // or a bearer token
});

A minimal backend route (Express) that injects the secrets and forwards to the middleware:

app.post('/api/dblm/qd', requireAuth, async (req, res) => {
  const upstream = await fetch(`${MIDDLEWARE_URL}/qd`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-User-Id': req.user.id },
    body: JSON.stringify({
      ...req.body,                       // question, sessionId, module/connections
      clientCert: req.user.dblmCert,     // fetched from your store
      clientKey: req.user.dblmKey,
    }),
  });
  res.status(upstream.status).send(await upstream.text());
});

Your endpoint must return the broker's NLQ result JSON unchanged.

Option B — directTransport (internal / trusted apps)

The browser talks straight to @dblm/middleware, sending the API key + the user's own cert/key on every call. Simplest to wire, but the secrets live in client-side JS — use it for admin consoles, internal tools, and prototypes, not public sites.

import { directTransport } from '@dblm/react';

// bundle comes from a prior POST /register (client.register(...) or your onboarding flow)
const transport = directTransport({
  baseUrl: 'http://localhost:3000',
  apiKey: 'your-service-api-key',
  userId: bundle.user,
  clientCert: bundle.client_cert,
  clientKey: bundle.client_key,
});

Sessions

The broker keeps conversation memory internally, keyed by session id, but doesn't expose it for reading back. So @dblm/react owns the transcript and the session list on the client (persisted to localStorage by default) and keeps the server in sync by sending the active session's id as the broker sessionId on every turn:

  • New chat → a fresh session id + empty transcript.
  • Continue → same id replayed, so the broker threads prior turns into the LLM context.
  • Switching threads in the sidebar swaps both the local transcript and the server session.

Swap persistence with the storage config (memoryStorage() for none / SSR, or your own DblmStorage).

Theming

Everything is driven by --dblm-* CSS variables with a polished default skin. Configure it four ways, from simplest to most powerful:

1. Theme tokens (typed, per-provider or per-widget):

<DblmChat theme={{ colorPrimary: '#0ea5e9', radius: '16px', fontFamily: 'Inter, sans-serif' }} />

2. Color schemecolorScheme="light" | "dark" | "auto" (auto follows the OS).

3. Override CSS variables yourself:

.dblm-chat { --dblm-color-user-bubble: #16a34a; --dblm-radius: 4px; }

4. Per-slot class names / whole-component overrides for full control:

<DblmChat
  classNames={{ bubble: 'my-bubble', composer: 'my-composer' }}
  components={{ MessageBubble: MyCustomBubble }}   // replace a slot entirely
/>

Common tokens: colorPrimary, colorBg, colorSurface, colorSurfaceAlt, colorText, colorTextMuted, colorBorder, colorUserBubble, colorAssistantBubble, colorError, fontFamily, fontMono, radius, radiusSm, spacing, shadow.

Headless usage

Prefer to build your own layout? useNlqChat holds all the state and logic; the widget is just a default view over it.

import { useNlqChat, useDblm } from '@dblm/react';

function MyChat() {
  const { client, storage } = useDblm();
  const chat = useNlqChat({ client, storage, target: { module: 'sales' } });

  return (
    <div>
      {chat.messages.map((m) => <div key={m.id}>{m.role}: {m.text}</div>)}
      <button onClick={() => chat.send('how many orders today?')} disabled={chat.sending}>Ask</button>
      <button onClick={() => chat.newSession()}>New chat</button>
    </div>
  );
}

Returns: sessions, activeSession, messages, sending, error, and actions send, newSession, selectSession, deleteSession, renameSession, clearSession, retry, stop.

<DblmChat> props

| Prop | Type | Description | | --- | --- | --- | | target | { module } \| { connections } | Database(s) to query for new chats. | | showSidebar | boolean | Show the session sidebar (default true). | | theme | DblmTheme | Theme tokens, merged over the provider's. | | colorScheme | 'light' \| 'dark' \| 'auto' | Overrides the provider scheme. | | persist | boolean | Persist sessions (default true). | | placeholder | string | Composer placeholder. | | welcome | ReactNode | Empty-thread content. | | classNames | DblmClassNames | Per-slot class overrides. | | components | DblmComponents | Whole-subcomponent overrides. | | onSend / onResult / onError / onSessionChange | callbacks | Lifecycle hooks. |

Testing

Three layers, from no-backend to the full live path:

1. React logic (no backend). Unit + integration tests over useNlqChat and <DblmChat> using a mock transport:

npm test

2. The widget by hand (no backend). Run the demo in "Mock" transport mode:

cd example && npm install && npm run dev   # transport = Mock

3. Full live path — React → middleware → broker → database. The harness at scripts/e2e.sh boots a dblm broker + @dblm/middleware, provisions a user (issuing a client cert via /register), then asks a real question through /qd and asserts the whole stack executed:

# prereqs: dblm binary built (go build -o dblm .), a broker master initialized
# (dblm server init), an indexed connection, a configured LLM provider, jq.
bash scripts/e2e.sh
# env overrides: E2E_CONN=chinook  E2E_QUESTION="..."  DBLM_BIN=./dblm

It validates API-key auth, the X-User-Id identity, /register cert issuance, mTLS to the broker, the ACL grant, schema loading, and engine execution — i.e. everything the mock tests can't reach. Point the demo's "Direct → middleware" transport at the same middleware to drive that path from the actual UI.

Example

A runnable demo (mock + direct transports, theme switcher, sidebar) lives in example/:

cd example
npm install
npm run dev

License

MIT