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

spacetimedb-runes

v0.0.2

Published

A type-safe, reactive SpacetimeDB client library for Svelte 5, built with runes. This library is based on the React `useTable` implementation provided by SpacetimeDB.

Readme

spacetimedb-runes

A type-safe, reactive SpacetimeDB client library for Svelte 5, built with runes. This library is based on the React useTable implementation provided by SpacetimeDB.

Features

  • Svelte 5 Runes: Built from the ground up using Svelte 5's new reactivity system
  • Full Type Safety: TypeScript support with module augmentation for custom database schemas
  • Reactive Queries: Automatically updates your UI when database changes occur
  • Type-Safe Where Clauses: Build complex queries with full autocomplete support
  • Lifecycle Callbacks: React to inserts, updates, deletes, and initial snapshots
  • Persistent Auth: Automatic token persistence across sessions

Installation

npm install spacetimedb-runes

Peer Dependencies

This library requires:

  • svelte: ^5.0.0
  • spacetimedb: ^1.8.0 (automatically installed)

Quick Start

1. Set Up Your Connection

First, establish a connection to your SpacetimeDB instance and wrap your app with the SpacetimeDBContext component.

Example Layout File (src/routes/+layout.svelte)

<script lang="ts">
  import { SpacetimeDB, SpacetimeDBContext } from 'spacetimedb-runes';
  import { DbConnection } from '@module_bindings';
  import type { Identity } from 'spacetimedb';
  import { onMount } from 'svelte';

  let connection = $state<DbConnection | null>(null);

  onMount(() => {
    const onConnect = (conn: DbConnection, identity: Identity, token: string) => {
      SpacetimeDB.status.set('connected');
      SpacetimeDB.authToken.current = token;

      // Set up reducer callbacks
      conn.reducers.onSendMessage(() => {
        // Message sent callback
      });
    };

    const onDisconnect = () => {
      SpacetimeDB.status.set('disconnected');
    };

    const onConnectError = () => {
      SpacetimeDB.status.set('error');
    };

    const connectionBuilder = DbConnection.builder()
      .withUri('ws://localhost:3000')
      .withModuleName('your-module-name')
      .withToken(SpacetimeDB.authToken.current || undefined)
      .onConnect(onConnect)
      .onDisconnect(onDisconnect)
      .onConnectError(onConnectError);

    // Set status to 'connecting' before building the connection
    // This helps TableQuery properly setup subscriptions when the connection becomes active
    SpacetimeDB.status.set('connecting');
    connection = connectionBuilder.build();
  });

  let status = SpacetimeDB.status;
  let { children } = $props();
</script>

{#if $status === 'connecting'}
  <div class="status-info">Connecting to SpacetimeDB...</div>
{/if}

{#if $status === 'disconnected'}
  <div class="status-warning">
    Disconnected from SpacetimeDB. Are you already connected from another tab?
  </div>
{/if}

{#if $status === 'error'}
  <div class="status-error">
    An error occurred while connecting to SpacetimeDB.
  </div>
{/if}

{#if connection && $status === 'connected'}
  <SpacetimeDBContext {connection}>
    {@render children()}
  </SpacetimeDBContext>
{:else}
  <p>Initializing connection...</p>
{/if}

<style>
  .status-info,
  .status-warning,
  .status-error {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    padding: 0.5rem;
    text-align: center;
    z-index: 1000;
  }

  .status-error {
    background-color: tomato;
    color: white;
  }

  .status-warning {
    background-color: orange;
    color: black;
  }

  .status-info {
    background-color: deepskyblue;
    color: white;
  }
</style>

2. Query Your Database

Use the TableQuery class to subscribe to table changes:

<script lang="ts">
  import { TableQuery } from 'spacetimedb-runes';

  const users = new TableQuery('User');
</script>

{#if users.state === 'loading'}
  <p>Loading users...</p>
{:else}
  <ul>
    {#each users.rows as user}
      <li>{user.name}</li>
    {/each}
  </ul>
{/if}

Working Example

For a complete working example of spacetimedb-runes in action, check out this game project: spacetime-game (It's based on the Unreal tutorial for SpacetimeDB)

Useful SpacetimeDB Commands

Here are some helpful SpacetimeDB CLI commands you can add to your package.json scripts:

{
  "scripts": {
    "generate": "spacetime generate --lang typescript --out-dir ./src/module_bindings --project-path ../spacetimedb",
    "compile": "cd ../spacetimedb && spacetime build",
    "publish": "spacetime publish --server local --project-path ../spacetimedb your-module-name"
  }
}
  • generate - Generates TypeScript client bindings from your SpacetimeDB module
  • compile - Builds your SpacetimeDB module
  • publish - Publishes your module to a SpacetimeDB server (local or remote)

Note: Adjust the paths (../spacetimedb, ./src/module_bindings) and module name (your-module-name) to match your project structure.

Adding Type Safety

To get full type safety and autocomplete for your database schema, you need to augment the Register interface using TypeScript's module augmentation. This can be done in your src/app.d.ts file or any other .d.ts file that TypeScript picks up.

Example: src/app.d.ts

import type { DbConnection } from '@module_bindings';

declare global {
  namespace App {
    // ... other App interfaces
  }
}

// Augment the spacetimedb-runes Register interface
declare module 'spacetimedb-runes' {
  interface Register {
    connection: DbConnection;
  }
}

export {};

Important: Replace DbConnection with your actual generated connection type from SpacetimeDB. The @module_bindings import should point to your SpacetimeDB generated client code.

Setting Up the @module_bindings Alias

The examples in this README use @module_bindings to import SpacetimeDB generated client code. To configure this alias in your SvelteKit project, add it to your svelte.config.js:

const config = {
  kit: {
    alias: {
      '@module_bindings': 'src/module_bindings',
      // ... other aliases
    }
  }
};

This allows you to import your SpacetimeDB generated types and connection from @module_bindings instead of using relative paths.

This enables full autocomplete for:

  • Table names in TableQuery
  • Column names in where clauses
  • Row types in callbacks

Type-Safe Where Clauses

The library provides a callback-based API for building where clauses with full type safety:

<script lang="ts">
  import { TableQuery } from 'spacetimedb-runes';

  // Filter users where isActive = true
  const activeUsers = new TableQuery('User', ({ where, eq }) =>
    where(eq('isActive', true))
  );

  // Complex queries with AND/OR
  const complexQuery = new TableQuery('User', ({ where, eq, and, or }) =>
    where(
      and(
        eq('role', 'admin'),
        or(
          eq('department', 'Engineering'),
          eq('department', 'Product')
        )
      )
    )
  );
</script>

Where Clause API

The where clause callback receives these helper functions:

  • eq(column, value) - Equality check (column = value)
  • and(...expressions) - Logical AND
  • or(...expressions) - Logical OR
  • where(expression) - Wraps the final expression

Type Safety: Column names are autocompleted based on your table schema, and values are type-checked against the column type.

Lifecycle Callbacks

TableQuery supports four lifecycle callbacks to react to database changes:

onInsert

Called when a new row is inserted that matches your query:

<script lang="ts">
  const users = new TableQuery('User', undefined, {
    onInsert: (row) => {
      console.log('New user added:', row.name);
    }
  });
</script>

onUpdate

Called when a row that matches your query is updated:

<script lang="ts">
  const users = new TableQuery('User', undefined, {
    onUpdate: (oldRow, newRow) => {
      console.log(`User ${oldRow.name} updated to ${newRow.name}`);
    }
  });
</script>

onDelete

Called when a row that matches your query is deleted:

<script lang="ts">
  const users = new TableQuery('User', undefined, {
    onDelete: (row) => {
      console.log('User deleted:', row.name);
    }
  });
</script>

onInitialSnapshot

Important: This callback runs only once when the subscription first receives the initial snapshot of existing data from the database.

Use this when you need to perform operations on existing data, since onInsert, onUpdate, and onDelete only fire for new changes that occur after the subscription is established.

<script lang="ts">
  const users = new TableQuery('User', undefined, {
    onInitialSnapshot: (rows) => {
      console.log(`Loaded ${rows.length} existing users from database`);

      // Example: Initialize local state based on existing data
      const userMap = new Map(rows.map(u => [u.id, u]));
    },
    onInsert: (row) => {
      console.log('NEW user inserted:', row);
    }
  });
</script>

Lifecycle Flow:

  1. TableQuery subscribes to the database
  2. Database sends initial snapshot → onInitialSnapshot fires once
  3. Future inserts → onInsert fires
  4. Future updates → onUpdate fires
  5. Future deletes → onDelete fires

Advanced Usage

Filtering with Callbacks

The where clause callbacks ensure type safety while building complex queries:

<script lang="ts">
  import { TableQuery } from 'spacetimedb-runes';

  const query = new TableQuery(
    'Task',
    ({ where, eq, and }) => where(
      and(
        eq('status', 'active'),
        eq('assignedTo', currentUserId)
      )
    ),
    {
      onInitialSnapshot: (tasks) => {
        console.log(`You have ${tasks.length} active tasks`);
      },
      onInsert: (task) => {
        showNotification(`New task assigned: ${task.title}`);
      },
      onUpdate: (oldTask, newTask) => {
        if (oldTask.status !== newTask.status) {
          console.log(`Task ${newTask.title} status changed`);
        }
      }
    }
  );
</script>

Manual Cleanup

If you need manual control over the lifecycle, use the destroy() method:

<script lang="ts">
  import { TableQuery } from 'spacetimedb-runes';
  import { onDestroy } from 'svelte';

  const users = new TableQuery('User');

  onDestroy(() => {
    users.destroy();
  });
</script>

Connection Status

Monitor connection status reactively:

<script lang="ts">
  import { SpacetimeDB } from 'spacetimedb-runes';

  let status = SpacetimeDB.status;
</script>

<div>
  Status: {$status}
</div>

Status values: 'disconnected', 'connecting', 'connected', 'error'

API Reference

SpacetimeDB

Static utilities for managing the SpacetimeDB connection:

  • SpacetimeDB.status - Writable store tracking connection status
  • SpacetimeDB.authToken - Persisted state for authentication token
  • SpacetimeDB.getContext<Connection>() - Retrieve connection from Svelte context
  • SpacetimeDB.setContext(connection) - Set connection in Svelte context

TableQuery<TableName, RowType>

Reactive class for subscribing to database tables:

Constructor:

new TableQuery(
  tableName: TableName,
  whereClause?: (helpers) => Expression,
  callbacks?: UseQueryCallbacks<RowType>
)

Properties:

  • rows: readonly RowType[] - Reactive array of matching rows
  • state: 'loading' | 'ready' - Current subscription state

Methods:

  • destroy(): void - Cleanup subscriptions and listeners

UseQueryCallbacks<RowType>

interface UseQueryCallbacks<RowType> {
  onInsert?: (row: RowType) => void;
  onDelete?: (row: RowType) => void;
  onUpdate?: (oldRow: RowType, newRow: RowType) => void;
  onInitialSnapshot?: (rows: readonly RowType[]) => void;
}

License

MIT

Credits

Built with Svelte 5 and SpacetimeDB.