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

@kalamdb/orm

v0.5.1-beta.2

Published

Drizzle ORM driver and schema generator for KalamDB

Readme

@kalamdb/orm

Drizzle ORM driver, KalamDB table helpers, live table helpers, and schema generator for KalamDB.

Use this package with @kalamdb/client when you want Drizzle-style queries in a browser app, admin UI, or Node service. Use @kalamdb/consumer separately for topic workers and agents. Use the same generated table definitions with @kalamdb/react when a React UI needs typed live-query hooks and component wrappers.

Install

npm i @kalamdb/client @kalamdb/orm drizzle-orm

Driver quick start

import { Auth, createClient } from '@kalamdb/client';
import { kalamDriver } from '@kalamdb/orm';
import { drizzle } from 'drizzle-orm/pg-proxy';
import { messages } from './schema';

const client = createClient({
  url: 'http://localhost:2900',
  authProvider: async () => Auth.basic('admin', 'AdminPass123!'),
});

const db = drizzle(kalamDriver(client));
const rows = await db.select().from(messages).limit(20);

The driver normalizes KalamDB temporal wire values for Drizzle columns: timestamp(..., { mode: 'date' }), date(..., { mode: 'date' }), and time(...) are converted from the numeric wire representation when needed.

Table helpers

import { bytes, embedding, file, kTable } from '@kalamdb/orm';
import { bigint, jsonb, text, timestamp, uuid } from 'drizzle-orm/pg-core';

export const docs = kTable.shared('app.docs', {
  id: bigint('id', { mode: 'bigint' }).primaryKey(),
  owner_id: uuid('owner_id').notNull(),
  title: text('title').notNull(),
  body: text('body'),
  metadata: jsonb('metadata'),
  attachment: file('attachment'),
  raw_bytes: bytes('raw_bytes'),
  embedding: embedding('embedding', 384),
  created_at: timestamp('created_at', { mode: 'date' }).notNull(),
});

file(), bytes(), and embedding() are KalamDB-specific Drizzle custom columns. file() maps values to FileRef | null, bytes() maps to Uint8Array | null, and embedding() maps to number[] | null.

Use the table-kind helpers when the table kind matters to live views or agents:

kTable.shared('app.docs', columns);
kTable.user('app.messages', columns);
kTable.stream('app.events', columns);
kTable.system('system.users', columns);

Pass { systemColumns: true } when your app needs typed _seq/_deleted fields for ordering, resume checks, or diagnostics. Streams only receive _seq; shared and user tables receive _seq and _deleted.

Consumer And ORM Coverage

The package test suite includes dedicated generated-schema plus runConsumer() scenarios for chat, AI chat memory, job dispatch, support CRM, and commerce fulfillment domains. Each scenario combines shared, user, and stream table definitions with topic consumption and Drizzle builders through kalamDriver() or executeAsUser().

Generate schema.ts

kalamdb-orm \
  --url http://localhost:2900 \
  --user admin \
  --password AdminPass123! \
  --namespace app \
  --include-system-columns \
  --out src/db/schema.ts

Generator options:

  • --namespace <name>: limit output to one or more namespaces. Repeat it or pass comma-separated names.
  • --include-system: include system and dba tables.
  • --include-system-columns [all|_seq,_deleted]: add KalamDB hidden system columns to generated table types.
  • --bigint-mode <string|bigint|number>: choose how generated BIGINT columns are emitted. Default is string to preserve Int64 precision.
  • --no-type-aliases: skip generated $inferSelect and $inferInsert aliases.

The generator introspects SHOW TABLES, uses DESCRIBE when column metadata is incomplete, preserves primary keys and non-null columns, and emits imports only for builders used by the generated schema. Generated schemas include ${tableName}Config, $inferSelect, and $inferInsert exports next to each table, so browser apps and agents can import schema.generated.ts directly without a wrapper file.

Watch schema.ts in local development

Add a generator script to your app:

{
  "scripts": {
    "schema:gen": "kalamdb-orm --url http://localhost:2900 --user admin --password AdminPass123! --namespace app --out src/db/schema.ts"
  }
}

Then let the Kalam CLI rerun that script whenever system.tables changes:

# Watch one namespace
kalam --watch-schema --namespace app --run "npm run schema:gen" --run-on-start

# Watch several namespaces
kalam --watch-schema --namespace chat --namespace billing --run "npm run schema:gen"

# Watch one table only
kalam --watch-schema --table app.messages --run "npm run schema:gen"

--watch-schema polls every 5 seconds by default. Override that with --interval 2s, --interval 500ms, or another supported duration when you need faster feedback.

KalamDB datatype mapping

| KalamDB type | Generated Drizzle helper | Wire/read note | |---|---|---| | BOOLEAN | boolean() | boolean | | INT | integer() | number | | SMALLINT | smallint() | number | | BIGINT | text() by default | Int64 is transported as a string; use --bigint-mode bigint or number if desired | | DOUBLE | doublePrecision() | number | | FLOAT | real() | number | | TEXT | text() | string | | TIMESTAMP / DATETIME | timestamp(..., { mode: 'date' }) | numeric timestamp is normalized to Date | | DATE | date(..., { mode: 'date' }) | date-day wire value is normalized to Date | | TIME | time() | microseconds since midnight normalize to HH:mm:ss[.fraction] | | JSON | jsonb() | plain JSON | | BYTES | bytes() | Uint8Array | | EMBEDDING(n) | embedding(name, n) | number[] | | UUID | uuid() | string UUID | | DECIMAL(p,s) | numeric() | exact decimal string | | FILE | file() | FileRef | null |

Live table helpers

import { liveTable } from '@kalamdb/orm';
import { messages } from './schema';

const stop = await liveTable(client, messages, (rows) => {
  console.log(rows);
}, { lastRows: 50 });

await stop();

liveTable() reuses the same @kalamdb/client connection and normalizes timestamp/date/time fields according to the Drizzle table definition.

liveTable() accepts the same row-oriented live options as @kalamdb/client.live(), including lastRows, from, limit, getKey, and onCheckpoint when you want to persist a resume cursor. Raw event streams stay in @kalamdb/client.liveEvents().

React Typed Mode

@kalamdb/react can compile the same Drizzle table descriptors into live query controllers:

import { LiveQueries } from '@kalamdb/react';
import { asc, eq } from 'drizzle-orm';
import { messages, typing } from './schema.generated';

<LiveQueries
  queries={{
    messages: {
      table: messages,
      where: (table) => eq(table.conversationId, conversationId),
      orderBy: (table) => asc(table.createdAt),
      deps: [conversationId],
    },
    typing: {
      table: typing,
      where: (table) => eq(table.conversationId, conversationId),
      deps: [conversationId],
    },
  }}
>
  {({ messages, typing }) => <ChatView messages={messages.rows} typing={typing.rows} />}
</LiveQueries>

This keeps schema ownership in the ORM package and avoids a separate React-specific schema layer.

Execute as a user

Agents and service workers can compile a Drizzle builder and run it through KalamDB's EXECUTE AS USER path:

import { executeAsUser } from '@kalamdb/orm';

await executeAsUser(
  client,
  db.insert(messages).values({
    room: 'main',
    role: 'assistant',
    author: 'KalamDB Copilot',
    sender_username: 'alice',
    content: 'Done.',
  }),
  'alice',
);

Only pass a user id that your service account is authorized to impersonate.

License

Licensed under the Apache License, Version 2.0 (Apache-2.0). See the packaged LICENSE.txt and NOTICE files.