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 });
creditStoreis the only required option.PostgresStoreis the bundled implementation;CreditStoreis the abstract base for custom backends.tenantIdscopes every store operation to a single tenant.billingStorepluscommerceOptionsenable the billing and commerce capabilities (ingestBillingEvent, checkouts, subscriptions, auto-recharge).emitterreceives 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
| Subpath | Contents |
|---|---|
@zonastery/bursar | The application API: facade, stores, PricingEngine, config, errors |
@zonastery/bursar/node | Node-only: loadConfigFile, runtime, maintenance, diagnostics, outbox recovery, ClickHouse, and S3 adapters |
@zonastery/bursar/browser | Browser-safe: auto-recharge status types only (AUTO_RECHARGE_STATES, BillingAutoRechargeStatus) |
@zonastery/bursar/opentelemetry | Optional 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:
pip install "bursar[postgres]"
BURSAR_MIGRATION_DATABASE_URL=postgres://... bursar migrate
Where to go next
- Credits service — balances, metered charging, leases, plans, ledger, analytics, teams
- PricingEngine — pricing without a database
- Stores —
PostgresStore,PostgresBillingStore, and theCreditStorecontract - API reference — generated TypeDoc reference
- Concepts and configuration — the canonical config document