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

kuratchi-sdk

v0.0.9

Published

End-to-end auth and multi-tenant org databases backed by Cloudflare (Workers + D1 via Durable Objects gateway). This package provides:

Downloads

24

Readme

Kuratchi SDK for SvelteKit

End-to-end auth and multi-tenant org databases backed by Cloudflare (Workers + D1 via Durable Objects gateway). This package provides:

  • 🔐 Authentication - Magic links, OAuth (Google, GitHub, Microsoft), email/password
  • 🗄️ Database - Durable Objects-backed SQLite with HTTP client and typed ORM
  • 💾 Storage - KV, R2, D1 bindings with unified access
  • 🌐 Domains - Full DNS zone management via Cloudflare API
  • 🏢 Multi-tenancy - Organization management with per-org databases
  • 🛠️ CLI - Admin DB provisioning and migration generation
  • 🎯 Type-safe ORM - JSON schema-based with includes, JSON columns, migrations

Below is a quickstart for SvelteKit, required environment variables, admin DB setup, and organization workflows.

Full Documentation: src/docs/README.md | Auth | Database | ORM | CLI

Quickstart (SvelteKit)

  1. Install and build
npm install
npm run build
  1. Configure .env (at your app root)

Required for auth and DO access:

# Auth + email
KURATCHI_AUTH_SECRET=your-long-random-secret
RESEND_API_KEY=your-resend-api-key
EMAIL_FROM="Acme <[email protected]>"
ORIGIN=https://your-app.example.com # needed for OAuth callback URLs

# Cloudflare / DO access
CLOUDFLARE_WORKERS_SUBDOMAIN=your-subdomain
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_API_TOKEN=your-api-token
KURATCHI_GATEWAY_KEY=your-gateway-key

# Admin DB (filled in after provisioning step)
KURATCHI_ADMIN_DB_NAME=kuratchi-admin
KURATCHI_ADMIN_DB_TOKEN=to-be-set-after-cli

# Optional: Google OAuth
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...

Aliases the CLI also understands (if you already have these set): GATEWAY_KEY, CF_ACCOUNT_ID, CF_API_TOKEN, WORKERS_SUBDOMAIN, and KURATCHI_CLOUDFLARE_* variants.

  1. Wire the SvelteKit handle

Option A: Unified API (Recommended)

Configure everything in one place:

// src/hooks.server.ts
import { kuratchi } from 'kuratchi-sdk';
import { sessionPlugin, adminPlugin, organizationPlugin, emailAuthPlugin, oauthPlugin } from 'kuratchi-sdk/auth';
import { adminSchema } from '$lib/schemas/admin';
import { organizationSchema } from '$lib/schemas/organization';

const app = kuratchi({
  auth: {
    plugins: [
      sessionPlugin(),
      adminPlugin({ adminSchema }),
      organizationPlugin({ organizationSchema }),
      emailAuthPlugin({
        provider: 'resend',
        apiKey: process.env.RESEND_API_KEY!,
        from: process.env.EMAIL_FROM!
      }),
      oauthPlugin({
        providers: [
          {
            name: 'google',
            clientId: process.env.GOOGLE_CLIENT_ID!,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET!
          }
        ]
      })
    ]
  },
  storage: {
    kv: { default: 'MY_KV' },
    r2: { uploads: 'USER_UPLOADS' },
    d1: { analytics: 'ANALYTICS_DB' }
  }
});

export const handle = app.handle;

Note: You must define your own schemas. See node_modules/kuratchi-sdk/src/lib/schema/*.example.ts for reference structures.

Option B: Modular API

Import only what you need:

// src/hooks.server.ts
import { createAuthHandle, sessionPlugin, adminPlugin, emailAuthPlugin } from 'kuratchi-sdk/auth';

export const handle = createAuthHandle({
  plugins: [
    sessionPlugin(),
    adminPlugin(),
    emailAuthPlugin({
      provider: 'resend',
      apiKey: process.env.RESEND_API_KEY!,
      from: process.env.EMAIL_FROM!
    })
  ],
  // Storage bindings
  kvNamespaces: { default: 'MY_KV' },
  r2Buckets: { uploads: 'USER_UPLOADS' }
});

What You Get:

  • Session cookies managed under locals.kuratchi, locals.user, and locals.session
  • Magic link endpoints: /auth/magic/send and /auth/magic/callback
  • OAuth routes: /auth/oauth/{provider}/start and /auth/oauth/{provider}/callback
  • Storage access: locals.kuratchi.kv, locals.kuratchi.r2, locals.kuratchi.d1
  • Domains: domains.zones.createZone(), domains.zones.listZones(), etc.
  • Server helper: locals.kuratchi.orgDatabaseClient(orgId?) for org DB ORM client
  1. Use in routes (examples)
// src/routes/+layout.server.ts
export const load = async ({ locals }) => {
  return { user: locals.user, session: locals.session };
};

// Protect a page
// src/routes/app/+page.server.ts
export const load = async ({ locals }) => {
  if (!locals.session) return { status: 302, redirect: '/login' } as any;
  return {};
};

// Magic link send (client fetch to this endpoint expected)
// POST /auth/magic/send with { email, organizationId?, redirectTo? }

// Google OAuth start URL (server)
import { redirect } from '@sveltejs/kit';
import { auth } from 'kuratchi-sdk';
export const GET = async ({ url }) => {
  const orgId = url.searchParams.get('org')!; // resolve per your app
  const href = auth.signIn.oauth.google.startUrl({ organizationId: orgId, redirectTo: '/' });
  throw redirect(302, href);
};

Provision the Admin Database

The admin DB stores organizations, users, and per-org database metadata. Use the CLI from this repo root (it auto-loads .env):

# Try with migrate=true; on failure it falls back to migrate=false
node bin/kuratchi-sdk.mjs init-admin-db --debug

Output includes a token. Add it to your .env:

KURATCHI_ADMIN_DB_TOKEN=... # from CLI output (100-year TTL)

Important: Admin tokens have a 100-year TTL to prevent expiration deadlock. If compromised or expired, regenerate with:

npx kuratchi-sdk refresh-admin-token

See TOKEN_MANAGEMENT.md for details.

If envs are not picked up, pass flags explicitly:

node bin/kuratchi-sdk.mjs init-admin-db \
  --gatewayKey "$KURATCHI_GATEWAY_KEY" \
  --workersSubdomain "$CLOUDFLARE_WORKERS_SUBDOMAIN" \
  --accountId "$CLOUDFLARE_ACCOUNT_ID" \
  --apiToken "$CLOUDFLARE_API_TOKEN" \
  --debug

Migrations

  • The SDK ships admin schema DSL internally; init-admin-db applies it when creating the admin DB (with automatic fallback if migration fails).
  • For organization databases, the SDK uses an organization schema. Migrations are applied when new org databases are created via the SDK.

Generate migrations from a schema file if you maintain your own DSL:

node bin/kuratchi-sdk.mjs generate-migrations --schema path/to/schema.ts --outDir migrations --tag init

Builds will package your library (dist/). At runtime the SDK applies the required migrations during DB creation flows.

API Reference

Import Paths

The SDK supports two import styles:

// Unified API (all features)
import { kuratchi } from 'kuratchi-sdk';
import type { KuratchiConfig } from 'kuratchi-sdk';

// Modular imports (subpaths)
import { createAuthHandle, sessionPlugin, adminPlugin } from 'kuratchi-sdk/auth';
import { database, KuratchiDatabase } from 'kuratchi-sdk/database';
import { kv } from 'kuratchi-sdk/kv';
import { r2 } from 'kuratchi-sdk/r2';
import { d1 } from 'kuratchi-sdk/d1';
import { domains } from 'kuratchi-sdk/domains';

// Legacy namespace imports (backward compatible)
import { auth, database, kv, r2, d1, domains } from 'kuratchi-sdk';

Database Operations

import { database } from 'kuratchi-sdk';
import { organizationSchema } from '$lib/schema/organization';

// ORM client with typed schema
const ormClient = await database.client({
  databaseName: 'my-org-db',
  dbToken: 'token-here',
  schema: organizationSchema
});

// Type-safe queries
const { data: users } = await ormClient.users
  .where({ deleted_at: { is: null } })
  .include({ posts: true })
  .many();

// Direct SQL access (schema-less)
const httpClient = database.forDatabase({
  databaseName: 'my-org-db',
  dbToken: 'token-here'
});

const result = await httpClient.query('SELECT * FROM users WHERE active = ?', [true]);

// Admin database helper
const admin = await database.admin();
const { data: orgs } = await admin.orm.organizations.many();

// Create new database
const newDb = await database.create({
  name: 'new-org-db',
  migrate: true,
  schema: organizationSchema
});

Auth Operations (Programmatic)

import { auth } from 'kuratchi-sdk';

// Admin operations
const adminAuth = await auth.admin();
const newOrg = await adminAuth.createOrganization({
  organizationName: 'Acme Corp',
  organizationSlug: 'acme',
  email: '[email protected]',
  password: 'secure-password'
});

// Magic link (requires email auth plugin in handle)
// POST to /auth/magic/send with { email, organizationId?, redirectTo? }

// Credentials (requires credentials plugin)
// POST to /auth/credentials/login with { email, password, organizationId? }

// OAuth (requires oauth plugin)
// Redirect to /auth/oauth/google/start?org=xxx&redirectTo=/dashboard

Domains (Cloudflare DNS)

The domains module provides full DNS zone management through Cloudflare's API. It uses your existing Cloudflare API credentials to manage zones and DNS records.

Required Environment Variables:

# Cloudflare credentials (same as used for other CF services)
CF_API_TOKEN=your-api-token  # or CLOUDFLARE_API_TOKEN
CF_ACCOUNT_ID=your-account-id  # or CLOUDFLARE_ACCOUNT_ID

Basic Usage:

import { domains } from 'kuratchi-sdk';

// Create a new DNS zone
const zone = await domains.zones.createZone('example.com');
if (zone) {
  console.log('Zone created:', zone.name, zone.id);
  console.log('Nameservers:', zone.name_servers);
}

// List all zones
const zones = await domains.zones.listZones({ status: 'active' });

// Find a specific zone
const zone = await domains.zones.findZoneByName('example.com');

// Add DNS records
if (zone) {
  // A record
  await domains.zones.createDnsRecord(zone.id, {
    type: 'A',
    name: 'www',
    content: '192.0.2.1',
    proxied: true  // Enable Cloudflare proxy
  });
  
  // CNAME record
  await domains.zones.createDnsRecord(zone.id, {
    type: 'CNAME',
    name: 'api',
    content: 'example.com',
    ttl: 300
  });
  
  // MX record
  await domains.zones.createDnsRecord(zone.id, {
    type: 'MX',
    name: '@',
    content: 'mail.example.com',
    priority: 10
  });
}

// List DNS records
const records = await domains.zones.listDnsRecords(zone.id, { type: 'A' });

// Update DNS record
if (records && records.length > 0) {
  await domains.zones.updateDnsRecord(zone.id, records[0].id, {
    type: 'A',
    name: 'www',
    content: '192.0.2.2',  // New IP
    proxied: true
  });
}

// Zone management
await domains.zones.pauseZone(zone.id);    // Pause zone
await domains.zones.unpauseZone(zone.id);  // Resume zone
await domains.zones.purgeZoneCache(zone.id);  // Clear cache

// Zone settings
const settings = await domains.zones.getZoneSettings(zone.id);
await domains.zones.updateZoneSetting(zone.id, 'ssl', 'full');

// Delete resources (be careful!)
await domains.zones.deleteDnsRecord(zone.id, recordId);
await domains.zones.deleteZone(zone.id);

Type Safety:

All functions return properly typed objects with IntelliSense support:

import type { Zone, DnsRecord, ListZonesOptions } from 'kuratchi-sdk';

const options: ListZonesOptions = {
  status: 'active',
  per_page: 50,
  order: 'name',
  direction: 'asc'
};

const zones: Zone[] | null = await domains.zones.listZones(options);

Error Handling:

The domains module gracefully handles missing credentials and API errors by returning null instead of throwing. Check console for warnings:

const zones = await domains.zones.listZones();
if (!zones) {
  console.log('Unable to fetch zones - check credentials or connection');
} else {
  console.log(`Found ${zones.length} zones`);
}

Runtime ORM: Quickstart and Includes

The SDK ships a tiny runtime ORM optimized for Workers/DO. You’ll typically obtain a typed client in two ways:

  • SvelteKit server: await locals.kuratchi.orgDatabaseClient()
  • Programmatic admin helper: const admin = await auth.admin({ organizationSchema }); const db = admin.getOrganizationDb(orgId)

Both paths create a JSON-schema based client, so JSON columns declared in your schema (type: 'json') are automatically serialized on writes and deserialized on reads.

Basic operations

// Insert
await db.users.insert({ id: crypto.randomUUID(), email: '[email protected]', name: 'Alice' });

// Query many
const users = await db.users
  .where({ deleted_at: { is: null } })
  .orderBy({ created_at: 'desc' })
  .limit(20)
  .offset(1) // page 1 when limit set
  .many();

// Get single
const one = await db.users.where({ id: 'user_1' }).first();

// Count
const cnt = await db.users.count({ status: 'active' });

// Delete
await db.users.delete({ id: 'user_1' });

Chainable updates (single vs many)

// Single-row update: updates the first matched row (by id when present)
await db.users
  .where({ email: '[email protected]', deleted_at: { is: null } })
  .update({ status: 'active' });

// Multi-row update: updates all rows that match the filter
await db.users
  .where({ status: 'pending', deleted_at: { is: null } })
  .updateMany({ status: 'active' });

JSON columns

When your schema marks a column with { type: 'json' }, you can pass/receive rich objects:

// metadata is a JSON column
await db.organizations
  .where({ id: 'org_1' })
  .update({ metadata: { theme: 'dark', features: ['a', 'b'] } });

const res = await db.organizations.where({ id: 'org_1' }).first();
// res.data.metadata is an object, not a JSON string

Includes (schema‑driven, simple)

include() lets you eager‑load related tables based on foreign keys defined in your JSON schema. It “just works”:

  • If the current table has a foreign key to users.id, then .include({ users: true }) will attach the joined users row as row.users.
  • For 1‑to‑many, use the related table name; it will attach row.<table> as an array.

Basic include (parent):

// posts(userId) -> users(id)
const res = await db.posts
  .where({ published: true })
  .include({ users: true })
  .many();

// res.data[0].users.name -> parent user's name

Basic include (children):

// users(id) <- session(userId)
const res = await db.users
  .where({ deleted_at: { is: null } })
  .include({ session: true })
  .many();

// res.data[0].session -> array of sessions for this user

Select specific columns and alias:

const res = await db.orders
  .include({
    users: { select: ['id', 'name'], as: 'buyer' },
  })
  .many();

// res.data[0].buyer -> { id, name }

Notes:

  • Includes are resolved from schema foreign keys; no manual key wiring required.
  • You can combine include with select, orderBy, limit, and offset on the base query.

Organizations Model

  • Each organization has its own database. Users belong to one or more orgs via admin DB relations.
  • Session cookies include an organizationId and are validated per org.
  • Server helper locals.kuratchi.orgDatabaseClient() returns an ORM client for the active org (from cookie) or for a provided orgId.
  • Creating an organization with the Auth API sets up its database and issues a usable API token.

Typical flows:

  • Admin signs up and creates an organization; receives a session cookie scoped to that org.
  • Users sign in via magic link, credentials, or Google OAuth; the SDK resolves org by email mapping in the admin DB.