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

@geektech/backone-utils

v0.5.2

Published

BackOne 通用工具包:数据库(多库 CRUD)、ID、缓存、JWT、HTTP、限流、校验、全局配置(零运行时依赖)

Readme

BackOne Utils

简体中文

General-purpose toolkit for BackOne.

Zero runtime dependencies — everything is built on Bun's built-in capabilities and standard Web APIs.

Modules:

  • db — multi-database connection management and type-safe CRUD on Bun.SQL
  • id — ID generators (uuidv4 / uuidv7 / nanoid / snowflake)
  • cache — in-memory cache (TTL expiry / LRU eviction) with getOrSet
  • jwt — HMAC JWT signing & verification (HS256 / HS384 / HS512)
  • http — fetch wrapper: timeouts, exponential-backoff retries, header merge, JSON parsing
  • rate-limit — fixed-window / sliding-window / token-bucket limiters
  • schema — lightweight zod-style validators with type inference
  • config — typed global configuration read from environment variables (fail-fast validation)

Supported database dialects: SQLite, PostgreSQL, MySQL, MariaDB.

Features

  • Zero runtime dependencies (built on Bun's Bun.SQL and standard Web APIs)
  • DatabaseManager for multiple named connections (register / get / has / remove / close)
  • Type-safe CRUD via Table: findById, findOne, findMany, count, exists, insert, insertMany, update, delete
  • Expressive where DSL: comparison, like, in / notIn, isNull, between, AND / OR groups
  • Dialect strategy pattern: identifier quoting and RETURNING support differ per dialect (SQLite/PostgreSQL return mutated rows; MySQL/MariaDB fall back to LAST_INSERT_ID / pre-count)
  • Transactions via db.transaction(fn) with automatic rollback
  • Raw queries: tagged templates (db.query\...`) and parameterized strings (db.raw(sql, params)`)
  • Every value is bound through ? placeholders; table/column names are quoted by the dialect — no string-concatenated SQL
  • Strategy-based ID generators, in-memory TTL/LRU cache, WebCrypto JWT, fetch client with retries, three rate-limiting strategies, zod-style validators

Requirements

  • Bun >= 1.3

Quick Start

import { DatabaseManager } from '@geektech/backone-utils';

interface User {
  id: number;
  name: string;
  age: number | null;
}

const db = new DatabaseManager();
db.register('primary', { url: process.env.DATABASE_URL }); // postgres://... / mysql://...
db.register('audit', { url: ':memory:' }); // sqlite in-memory

const users = db.get('primary').table<User>('users');

const created = await users.insert({ name: 'alice', age: 30 });
const list = await users.findMany({
  where: { age: { gte: 18 }, OR: [{ name: { like: 'a%' } }, { city: 'x' }] },
  orderBy: { id: 'desc' },
  limit: 20,
});
await users.update({ age: 31 }, { name: 'alice' });
await users.delete({ name: 'alice' });

await db.close();

Database Manager (multi-db)

| Method | Description | | --- | --- | | register(name, options) | Register a named connection and return its Database | | get(name) | Resolve a registered Database (throws DB_NOT_FOUND if missing) | | has(name) | Whether a name is registered | | names() / list() | Registered names / instances | | remove(name) | Close and remove one database | | close() | Close all databases |

DatabaseManager errors carry a stable code: DB_ALREADY_REGISTERED, DB_NOT_FOUND, DB_CLOSED, DB_MISSING_URL, DB_EMPTY_WRITE, DB_EMPTY_WHERE (see BackoneDbError).

Database

Connection options

interface DbConnectionOptions {
  name?: string;       // business name (default 'default')
  url?: string | URL;  // connection string, see examples below
  dialect?: 'sqlite' | 'postgres' | 'mysql' | 'mariadb'; // inferred from url
  readonly?: boolean;  // read-only marker (not enforced yet)
  max?: number;        // pool size (server dialects)
  idleTimeout?: number;
  connectionTimeout?: number;
  tls?: boolean | string | Record<string, unknown>;
  bigint?: boolean;
}

url examples:

  • postgres://user:pass@localhost:5432/mydb
  • mysql://user:pass@localhost:3306/mydb
  • :memory: / sqlite://:memory: — SQLite in-memory
  • ./data/app.db / sqlite://./data/app.db — SQLite file

Queries

// tagged template (safe parameter binding)
const row = await db.query<User>`SELECT * FROM users WHERE id = ${id}`;

// raw SQL with ? placeholders (parameterized by Bun)
const rows = await db.raw<User[]>('SELECT * FROM users WHERE age > ?', [18]);

// transactions (auto rollback on error)
const [a, b] = await db.transaction(async (tx) => {
  const t = tx.table<User>('users');
  return [await t.insert({ name: 'a' }), await t.insert({ name: 'b' })];
});

await db.close();

Table (CRUD)

const users = db.get('primary').table<User>('users');
// custom primary key: db.table<Row>('orders', { primaryKey: 'order_id' })

await users.findById(1);                       // T | null
await users.findOne({ where: { name: 'alice' } });
await users.findMany({
  where: { age: { between: [18, 30] } },
  orderBy: [{ age: 'desc' }, { id: 'asc' }],
  limit: 10,
  offset: 20,
  select: ['id', 'name'],
});
await users.count({ status: { in: ['active', 'pending'] } });
await users.exists({ name: 'alice' });

await users.insert({ name: 'bob', age: 25 });        // inserted row (with auto id)
await users.insertMany([{ name: 'a' }, { name: 'b' }]); // { rows, count }
await users.update({ age: 26 }, { name: 'bob' });     // { count, rows }
await users.delete({ name: 'bob' });                  // { count, rows }

Notes:

  • insert / insertMany return the inserted rows when the dialect supports RETURNING (SQLite / PostgreSQL). On MySQL / MariaDB, insert re-selects by the primary key (LAST_INSERT_ID, default primary key id); insertMany returns rows: [] with the affected count.
  • update / delete require a non-empty where (throws DB_EMPTY_WHERE) to prevent full-table mistakes.
  • On MySQL / MariaDB, update / delete report the matched row count computed before the statement (non-atomic unless run inside a transaction).

Where DSL

where: {
  // bare value = equality; null = IS NULL
  name: 'alice',
  // operators
  age: { gte: 18, lt: 65 },
  status: { in: ['active', 'pending'] },
  bio: { like: '%engineer%' },
  deleted_at: { isNull: true },
  score: { between: [80, 100] },
  id: { ne: 1 },
  // groups (AND binds tighter than OR)
  AND: [{ a: 1 }, { b: 2 }],
  OR: [{ role: 'admin' }, { role: 'editor' }],
}

Package exports

import {
  Database,          // single-connection wrapper
  DatabaseManager,   // multi-connection manager
  createDatabase,    // standalone Database factory
  Table,             // per-table CRUD handle
  BackoneDbError,    // typed errors with stable codes

  createId,          // quick ID generation (default uuidv7)
  createIdGenerator, // strategy-based ID generator factory
  createCache,       // TTL / LRU cache factory
  createJwt,         // JWT sign/verify service
  createHttpClient,  // fetch wrapper with retries/timeouts
  createRateLimiter, // fixed/sliding/token-bucket limiter factory
  v,                 // schema validator factory (zod style)
  defineConfig,      // typed config from env (prefix + coercion + fail-fast)
  EnvSource,         // env read source (Bun.env + process.env)
  ConfigError,       // config errors with stable codes
  BackoneError,      // shared error base with stable codes
  name, version,
} from '@geektech/backone-utils';

ID generation (id)

Strategy-based ID generators behind one IdGenerator interface (createIdGenerator(strategy) / createId(strategy), default uuidv7):

| Strategy | Description | | --- | --- | | uuidv4 | Random UUID (Web Crypto) | | uuidv7 | Time-ordered UUID (Bun built-in, DB-primary-key friendly) | | nanoid | Short URL-safe IDs, configurable alphabet / length | | snowflake | 41+10+12-bit snowflake IDs, monotonic, clock-drift safe |

import { createId, createIdGenerator } from '@geektech/backone-utils';
const id = createId('nanoid', { length: 12 });
const ids = createIdGenerator('snowflake');
await users.insert({ id: ids.generate(), name: 'alice' });

Cache (cache)

In-memory caches implementing one Cache interface (dependency inversion):

  • TtlCache — unbounded, lazy TTL expiry
  • LruCache — capacity-bound LRU eviction, optional TTL
  • createCache({ ttlMs, capacity }) picks LruCache when capacity is set
  • cache.getOrSet(key, factory, ttlMs?) — memoize async results (DB queries)
import { createCache } from '@geektech/backone-utils';
const rank = createCache<number>({ ttlMs: 60_000, capacity: 1000 });
const score = await rank.getOrSet('city:shanghai', () => loadScore('shanghai'));

JWT (jwt)

HMAC-signed JWT with Web Crypto — HS256 / HS384 / HS512, zero dependencies. verify checks structure, signature, exp / nbf, and optional issuer / audience; failures carry stable codes (JWT_EXPIRED, JWT_BAD_SIGNATURE, JWT_MALFORMED, JWT_NOT_YET_VALID, JWT_CLAIM_MISMATCH).

import { createJwt } from '@geektech/backone-utils';
const jwt = createJwt({ secret: process.env.JWT_SECRET });
const token = await jwt.sign({ sub: '42', role: 'admin' }, { expiresIn: 3600 });
const payload = await jwt.verify(token, { issuer: 'city-index' });

HTTP client (http)

fetch wrapper: base URL + query merging, header merge, JSON auto serialize/parse, timeout (merged with an external AbortSignal), exponential-backoff retries (retryable statuses, network errors, timeouts; per-request retry: false disables). Non-2xx responses throw HttpError with the status.

import { createHttpClient } from '@geektech/backone-utils';
const api = createHttpClient({
  baseUrl: 'https://api.example.com',
  timeoutMs: 5000,
});
const { data } = await api.get<{ total: number }>('/stats', {
  query: { year: 2026 },
});

Rate limiting (rate-limit)

Per-key in-memory limiters behind one RateLimiter interface:

  • fixed-window — counter per window, reset at window end
  • sliding-window — timestamp log, expiring requests dropped continuously
  • token-bucket — refills at a rate, allows bursts
import { createRateLimiter } from '@geektech/backone-utils';
const limiter = createRateLimiter({
  limit: 60,
  windowMs: 60_000,
  strategy: 'token-bucket',
});
const { allowed, remaining, resetAt } = limiter.consume(clientIp);

Validation (schema)

Lightweight zod-style validators with TypeScript inference:

import { v, type InferType } from '@geektech/backone-utils';

const citySchema = v.object({
  name: v.string({ minLength: 1 }),
  population: v.number({ min: 0, integer: true }),
  tags: v.array(v.string()),
  score: v.optional(v.number({ min: 0, max: 100 })),
});
type City = InferType<typeof citySchema>;

const result = citySchema.safeParse(body);
// { success: true, data: City } | { success: false, error: ValidationError }

Supported: string / number / boolean / literal / enum / array / object / record / optional / nullable / union. Failures aggregate all issues with field paths (ValidationError.issues).

Development

bun run build      # emit dist (types + bundle)
bun test           # unit tests (SQLite in-memory)
bun run lint       # eslint + prettier
bunx tsc --noEmit  # type check

Roadmap

  • DB: upsert / batch delete, connection retry & health check, migration helpers
  • Distributed cache / rate-limit backends (e.g. Redis adapters)

License

MIT