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​