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

@keeldotrun/client

v0.1.0

Published

TypeScript client library for the Keel.run platform API

Downloads

13

Readme

@keeldotrun/client

TypeScript client library for the Keel.run platform API.

Development (monorepo)

This package lives in the keel.run monorepo alongside the SvelteKit frontend. The root package.json declares both as npm workspaces:

keel.run/
├── package.json          # workspaces: ["client", "frontend"]
├── client/               # @keeldotrun/client  ← you are here
└── frontend/             # SvelteKit app (imports @keeldotrun/client)

From the repo root, a single npm install links everything together. The frontend references the client locally via workspace protocol:

// frontend/package.json
{
  "dependencies": {
    "@keeldotrun/client": "workspace:*"
  }
}

No publish step needed during development — Vite resolves the workspace symlink automatically. Import it like any other package:

import { createClient } from "@keeldotrun/client";

Install (external consumers)

The package is also published to Codeberg's npm registry for use outside the monorepo. Add an .npmrc line (or configure your registry globally), then install:

# One-time project setup — tells npm where @keeldotrun packages live
echo "@keeldotrun:registry=https://codeberg.org/api/packages/Keeldotrun/npm/" >> .npmrc

npm install @keeldotrun/client

Or globally:

npm config set @keeldotrun:registry https://codeberg.org/api/packages/Keeldotrun/npm/

Publishing

First, authenticate with Codeberg (one-time):

# Interactive — prompts for username / password / email
npm login --registry=https://codeberg.org/api/packages/Keeldotrun/npm/
# Username: your Codeberg username
# Password: a Codeberg access token (not your account password)
# Email:    your Codeberg email

Generate a token at https://codeberg.org/user/settings/applications (scopes: read:user and packages).

For CI, skip npm login and create client/.npmrc instead:

//codeberg.org/api/packages/Keeldotrun/npm/:_authToken=<token>

This file is git-ignored so your token stays local.

Then publish:

cd client
npm run build
npm publish

The publishConfig.registry in package.json already points at Codeberg — no --registry flag needed.

Quick start

import { createClient } from "@keeldotrun/client";

const keel = createClient({
  baseUrl: "https://api.keel.run",
  getToken: () => localStorage.getItem("access_token"),
});

// Every method returns a Result<T> — check .error first.
const result = await keel.databases.list({ page: 1, perPage: 20 });

if (result.error) {
  // result.errorValue is a typed KeelError — inspect .code
  console.error(result.errorValue.code, result.errorValue.message);
  return;
}

// result.data is fully typed
for (const db of result.data.databases) {
  console.log(db.name, db.status);
}

Error handling

Every API call returns Result<T> — a discriminated union:

type Result<T> =
  | { data: T;        error: false; errorValue: null }
  | { data: null;     error: true;  errorValue: KeelError };

This forces callers to handle errors before accessing data, just like Go's val, err := fn() pattern. TypeScript narrows the type when you check result.error.

const result = await keel.databases.get("db_123");

if (result.error) {
  switch (result.errorValue.code) {
    case "not_found":        /* 404 */ break;
    case "unauthenticated":  /* 401 */ break;
    case "permission_denied": /* 403 */ break;
    case "rate_limited":     /* 429 */ break;
    default:                 /* ... */ break;
  }
  return;
}

// TypeScript knows result.data is Database here
console.log(result.data.engine);

Error codes

| Code | HTTP | Meaning | |---|---|---| | invalid_argument | 400 | Validation failed (check .fields) | | unauthenticated | 401 | Missing or invalid credentials | | permission_denied | 403 | Authenticated but not authorized | | not_found | 404 | Resource doesn't exist | | already_exists | 409 | Unique constraint violation | | rate_limited | 429 | Too many requests | | internal_error | 500 | Server bug | | unavailable | 502/503 | Downstream service unreachable | | deadline_exceeded | 504 | Request timed out |

API reference

Auth

await keel.auth.register({ email, password, name });
await keel.auth.login({ email, password });
await keel.auth.refresh({ refreshToken });
await keel.auth.logout({ refreshToken });
await keel.auth.forgotPassword({ email });
await keel.auth.resetPassword({ token, newPassword });
await keel.auth.changePassword({ currentPassword, newPassword });
await keel.auth.verifyEmail({ token });
await keel.auth.resendVerification();
await keel.auth.verify2FA({ tempToken, code });

Users

await keel.users.me();
await keel.users.get(id);
await keel.users.list({ page, perPage });
await keel.users.update(id, { name });
await keel.users.delete(id);

Teams

await keel.teams.create({ name });
await keel.teams.get(id);
await keel.teams.list({ page, perPage });
await keel.teams.addMember(teamId, { userId, role });
await keel.teams.removeMember(teamId, userId);

Tokens

await keel.tokens.create({ name });
await keel.tokens.list({ page, perPage });
await keel.tokens.revoke(id);
await keel.tokens.validate(token);

Databases

await keel.databases.create({ name, plan: "micro", region: "us-east" });
await keel.databases.get(id);
await keel.databases.list({ page, perPage });
await keel.databases.delete(id);
await keel.databases.resetPassword(id);

Billing

await keel.billing.listInvoices({ page, perPage });
await keel.billing.getInvoice(id);
await keel.billing.getUsage();
await keel.billing.listPaymentMethods();
await keel.billing.addPaymentMethod({ ... });
await keel.billing.deletePaymentMethod(id);

Use with SvelteKit

// src/lib/keel.ts
import { createClient } from "@keeldotrun/client";
import { browser } from "$app/environment";

export const keel = createClient({
  baseUrl: import.meta.env.VITE_API_URL ?? "http://localhost:8080",
  getToken: () => {
    if (!browser) return null;
    return localStorage.getItem("access_token");
  },
});

Then in your load functions or components:

import { keel } from "$lib/keel";

export async function load() {
  const result = await keel.databases.list();
  if (result.error) {
    return { databases: [], error: result.errorValue.message };
  }
  return { databases: result.data.databases };
}

Advanced: raw HTTP

If you need to call an endpoint not yet covered by the service modules, use the raw HTTP helpers:

import { get, post, patch, del } from "@keeldotrun/client";

const result = await get<MyCustomType>(config, "/v1/custom/endpoint");

Testing

The service interfaces are exported as types — mock them in your tests:

import type { DatabasesService } from "@keeldotrun/client";
import { ok } from "@keeldotrun/client";

const mockDb: DatabasesService = {
  list: () => Promise.resolve(ok({ databases: [], pagination: { page: 1, perPage: 20, total: 0 } })),
  // ... etc
};

License

MIT