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

@useatlas/sdk

v0.1.0

Published

TypeScript SDK for the Atlas text-to-SQL agent API

Downloads

3,800

Readme

@useatlas/sdk

TypeScript SDK for the Atlas text-to-SQL agent API.

Install

bun add @useatlas/sdk

Quick Start

import { createAtlasClient } from "@useatlas/sdk";

const atlas = createAtlasClient({
  baseUrl: "https://api.example.com",
  apiKey: "my-key",
});

const result = await atlas.query("How many users signed up last week?");
console.log(result.answer);
console.log(result.sql);    // SQL queries the agent executed
console.log(result.data);   // Raw query results ({ columns, rows }[])

Authentication

Pass either apiKey (simple key auth) or bearerToken (BYOT / managed auth):

// Simple API key
const atlas = createAtlasClient({ baseUrl: "...", apiKey: "my-key" });

// Bearer token (JWT from your auth provider)
const atlas = createAtlasClient({ baseUrl: "...", bearerToken: "eyJ..." });

API Reference

Query

| Method | Description | |--------|-------------| | atlas.query(question, opts?) | Run a synchronous query. Returns { answer, sql, data, steps, usage } | | atlas.streamQuery(question, opts?) | Stream a query as an async iterator of typed StreamEvents | | atlas.chat(messages, opts?) | Start a streaming chat session. Returns a raw Response with SSE body (AI SDK UI Message Stream Protocol) |

Schema

| Method | Description | |--------|-------------| | atlas.listTables() | List all queryable tables with column details. Returns { tables: TableInfo[] } |

const { tables } = await atlas.listTables();
for (const t of tables) {
  console.log(`${t.table}: ${t.description}`);
  for (const col of t.columns) {
    console.log(`  ${col.name} (${col.type}) — ${col.description}`);
  }
}

Conversations

| Method | Description | |--------|-------------| | atlas.conversations.list(opts?) | List conversations (paginated) | | atlas.conversations.get(id) | Get a conversation with messages | | atlas.conversations.delete(id) | Delete a conversation | | atlas.conversations.star(id) | Star a conversation | | atlas.conversations.unstar(id) | Unstar a conversation |

Scheduled Tasks

| Method | Description | |--------|-------------| | atlas.scheduledTasks.list(opts?) | List scheduled tasks (paginated) | | atlas.scheduledTasks.get(id) | Get a task with recent runs | | atlas.scheduledTasks.create(input) | Create a scheduled task | | atlas.scheduledTasks.update(id, input) | Update a scheduled task | | atlas.scheduledTasks.delete(id) | Delete a scheduled task | | atlas.scheduledTasks.trigger(id) | Trigger immediate execution | | atlas.scheduledTasks.listRuns(id, opts?) | List past runs |

Admin (requires admin role)

| Method | Description | |--------|-------------| | atlas.admin.overview() | Dashboard overview data | | atlas.admin.semantic.entities() | List semantic layer entities | | atlas.admin.semantic.entity(name) | Get entity detail | | atlas.admin.semantic.metrics() | List metrics | | atlas.admin.semantic.glossary() | Get glossary | | atlas.admin.semantic.catalog() | Get catalog | | atlas.admin.semantic.stats() | Aggregate semantic stats | | atlas.admin.connections() | List datasource connections | | atlas.admin.testConnection(id) | Test connection health | | atlas.admin.audit(opts?) | Query audit log | | atlas.admin.auditStats() | Aggregate audit stats | | atlas.admin.plugins() | List installed plugins | | atlas.admin.pluginHealth(id) | Check plugin health |

Streaming

Use streamQuery() to receive typed events as the agent works:

import type { StreamEvent } from "@useatlas/sdk";

for await (const event of atlas.streamQuery("How many users signed up last week?")) {
  switch (event.type) {
    case "text":
      process.stdout.write(event.content);
      break;
    case "tool-call":
      console.log(`Calling ${event.name}`, event.args);
      break;
    case "tool-result":
      console.log(`${event.name} returned`, event.result);
      break;
    case "result":
      console.table(event.rows); // convenience: { columns, rows } from executeSQL
      break;
    case "error":
      // Mid-stream errors carry a typed `code` and a `retryAfterSeconds`
      // delta when the server emits a structured `ChatErrorInfo` body
      // (e.g. provider rate limiting). Older servers populate only `message`.
      console.error(event.code ?? "error", event.message);
      if (event.retryable && event.retryAfterSeconds !== undefined) {
        console.warn(`Server suggests retrying in ${event.retryAfterSeconds}s`);
      }
      break;
    case "parse-error":
      console.warn("Malformed SSE frame", event.raw);
      break;
    case "finish":
      console.log(`\nDone (${event.reason})`);
      break;
  }
}

Connection Drops & Partial Results

streamQuery() uses a one-shot SSE connection over fetch. If the connection drops mid-stream:

  • Events already yielded are still valid — the async generator delivers each event to your for await loop before advancing, so any text, result, or tool-result events you've already processed are safe to keep.
  • The generator throws an AtlasError with code network_error and a message starting with "Stream interrupted: ...".
  • There is no automatic reconnect or resume — start a new streamQuery() call to retry.
import { AtlasError } from "@useatlas/sdk";

const collected: string[] = [];

try {
  for await (const event of atlas.streamQuery("Revenue by region")) {
    if (event.type === "text") collected.push(event.content);
    if (event.type === "result") console.table(event.rows);
  }
} catch (err) {
  if (err instanceof AtlasError && err.code === "network_error") {
    console.warn("Stream dropped — partial text:", collected.join(""));
    // Decide whether to retry or use the partial data you have
  } else {
    throw err;
  }
}

Cancellation

Pass an AbortSignal to cancel the stream. The signal aborts the underlying reader, and an AbortError is thrown from the for await loop. Events yielded before cancellation are already consumed by your code.

const controller = new AbortController();

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10_000);

try {
  for await (const event of atlas.streamQuery("...", { signal: controller.signal })) {
    if (event.type === "text") process.stdout.write(event.content);
  }
} catch (err) {
  if (err instanceof Error && err.name === "AbortError") {
    console.log("Stream cancelled");
  }
}

Stream Event Types

| Type | Fields | Description | |------|--------|-------------| | text | content | Text chunk from the agent | | tool-call | toolCallId, name, args | Agent is calling a tool | | tool-result | toolCallId, name, result | Tool returned a result | | result | columns, rows | Convenience event extracted from tool-result when executeSQL returns data. Both tool-result and result are emitted. | | error | message, code?, retryable?, retryAfterSeconds?, requestId? | Mid-stream error. When the server sends a structured ChatErrorInfo body (provider_rate_limit, provider_timeout, etc.) the typed code and upstream Retry-After delta travel alongside the human-readable message. Older servers populate only message. | | parse-error | raw, error | Client-side: an SSE frame contained invalid JSON. The raw data is preserved for debugging. | | finish | reason | Stream completed |

Context Warnings (Degraded Answer Signal)

The chat route emits data-context-warning frames whenever the agent ran past a non-fatal degradation — the org semantic layer or learned-patterns lookup failed, or the workspace is approaching its plan budget — and the run was allowed to proceed against reduced context. These are not stream events from streamQuery(); they arrive on the AI-SDK UI Message Stream channel that chat() returns. If you are routing a raw Response from atlas.chat() through your own SSE consumer, parse each data-context-warning frame body with parseContextWarning from @useatlas/types:

import { parseContextWarning, type ChatContextWarning } from "@useatlas/types";

// Inside your SSE frame handler:
if (frame.type === "data-context-warning") {
  const warning: ChatContextWarning | null = parseContextWarning(frame.data);
  if (warning) {
    console.warn(`[${warning.code}]`, warning.title, warning.detail ?? "");
    // Surface a per-message warning banner — the answer is still usable
    // but was generated against reduced context. Do NOT treat it as a
    // hard error.
  } else {
    // Log on null so a future server-side wire-shape change (unknown
    // code, missing title, wrong severity literal) is observable in dev
    // rather than silently dropped. In production, gate this behind a
    // one-shot ref so a runaway stream of malformed frames doesn't
    // flood the console — see the in-tree `useContextWarnings` hook for
    // an example dedup pattern.
    console.warn("[atlas-sdk] dropped malformed data-context-warning frame", frame.data);
  }
}

The frame's severity: "warning" discriminator separates these from data-error frames, so a single SSE consumer can route both without misclassifying a degraded answer as a failure.

Error Handling

All methods throw AtlasError on failure. See the full Error Codes Reference for every error code, HTTP status, retry guidance, and a complete retry-with-backoff example.

import { AtlasError } from "@useatlas/sdk";

try {
  await atlas.query("...");
} catch (err) {
  if (err instanceof AtlasError) {
    console.error(err.code);     // e.g. "rate_limited", "auth_error"
    console.error(err.status);   // HTTP status code
    console.error(err.message);  // Human-readable message
    console.error(err.retryable); // true if retrying may help
  }
}

AtlasError Properties

| Property | Type | Description | |----------|------|-------------| | code | AtlasErrorCode | Error code (e.g. rate_limited, network_error) | | status | number | HTTP status code (0 for client-side errors like network_error) | | message | string | Human-readable description | | retryable | boolean | Whether retrying the same request may succeed | | retryAfterSeconds | number \| undefined | Server-suggested delay — only populated when the server includes retryAfterSeconds in the error response body (currently only for rate_limited). Always guard against undefined |

Retry with Backoff

Use retryable for generic retry logic. For rate_limited, prefer the server-suggested delay when available, falling back to exponential backoff:

import { AtlasError } from "@useatlas/sdk";

try {
  await atlas.query("...");
} catch (err) {
  if (!(err instanceof AtlasError) || !err.retryable) throw err;

  // Use server delay if available, otherwise exponential backoff
  const delay = err.retryAfterSeconds != null
    ? err.retryAfterSeconds * 1000
    : 5000;
  await new Promise((r) => setTimeout(r, delay));
  await atlas.query("...");
}

License

MIT