Skip to main content
Version: 2.x

TypeScript API

The package is ESM-only and requires Node.js 22 or newer.

npm install @zonastery/bursar pg

Constructing the facade

import { Bursar, PostgresStore } from "@zonastery/bursar";

const store = new PostgresStore({
postgres: databaseUrl,
tenantId,
providerEnvironment: "test",
});
const bursar = new Bursar({ creditStore: store });
  • creditStore is the only required option. PostgresStore is the bundled implementation; CreditStore is the abstract base for custom backends.
  • tenantId scopes every store operation to a single tenant.
  • billingStore plus commerceOptions enable the billing and commerce capabilities (ingestBillingEvent, checkouts, subscriptions, auto-recharge).
  • emitter receives credit lifecycle events (CreditEventEmitter).

Every facade method returns a Promise. Exact monetary inputs accept Decimal | string and reject native JavaScript numbers; returned and stored amounts are Decimal.

Error and retry contract

Every typed SDK failure extends BursarError and exposes stable code, category, and retryable fields. Use isBursarError() instead of relying only on instanceof; the guard also works when an application has two package copies. error.cause retains the underlying failure for diagnostics, while bursarErrorPublicMessage(error) provides safe user-facing copy.

import {
bursarErrorHttpStatus,
bursarErrorPublicMessage,
isBursarError,
retryBursarOperation,
} from "@zonastery/bursar";

try {
return await retryBursarOperation(
() => bursar.credits.getBalance(accountId),
{ maxAttempts: 3, signal: request.signal },
);
} catch (error) {
if (isBursarError(error)) {
return Response.json(
{ code: error.code, message: bursarErrorPublicMessage(error) },
{ status: bursarErrorHttpStatus(error) },
);
}
throw error;
}

Retries use p-retry for bounded exponential backoff, jitter, elapsed-time budgets, and cancellation. The default classifier retries only errors marked retryable. Retry a mutation only when it has a stable idempotency key; when StoreError.indeterminate is true, the original attempt may have committed.

Entry points

SubpathContents
@zonastery/bursarThe application API: facade, stores, PricingEngine, config, errors
@zonastery/bursar/nodeNode-only: loadConfigFile, runtime, maintenance, diagnostics, outbox recovery, ClickHouse, and S3 adapters
@zonastery/bursar/browserBrowser-safe: auto-recharge status types only (AUTO_RECHARGE_STATES, BillingAutoRechargeStatus)
@zonastery/bursar/opentelemetryOptional API-only OpenTelemetry adapter; the embedding application owns providers and exporters
@zonastery/bursar/providers/*Payment-provider adapters (Stripe, Dodo, mock)

The root and node entry points are server-only and depend on Node built-ins; browser contains no database stores or Node-only dependencies and is safe for Client Components.

Migrations

The TypeScript package ships no schema SQL and has no migration command. Install the Python CLI once and run:

Terminal
pip install "bursar[postgres]"
BURSAR_MIGRATION_DATABASE_URL=postgres://... bursar migrate

Where to go next