Utils
Full API reference for @supacommerce/utils.
@supacommerce/utils
Shared utilities for supacommerce. Intentionally public — use these directly in your own application code.
Installation
pnpm add @supacommerce/utilsCurrency
All monetary values in supacommerce are stored as integers in the smallest currency unit — cents for USD, pence for GBP. These helpers handle conversion and formatting.
Zero-decimal currencies (JPY, KRW, VND, and others) are handled automatically.
import {
toMinorUnit,
fromMinorUnit,
formatCurrency,
addMoney,
subtractMoney,
} from "@supacommerce/utils";
// Convert decimal to storage integer
toMinorUnit(29.99, "USD"); // 2999
toMinorUnit(100, "JPY"); // 100 — JPY is zero-decimal
// Convert storage integer to decimal
fromMinorUnit(2999, "USD"); // 29.99
fromMinorUnit(100, "JPY"); // 100
// Format for display
formatCurrency(2999, "USD"); // "$29.99"
formatCurrency(2999, "USD", "de-DE"); // "29,99 $"
formatCurrency(2999, "GBP"); // "£29.99"
formatCurrency(300, "JPY"); // "¥300"
// Safe integer arithmetic
addMoney(1999, 500); // 2499
subtractMoney(1999, 500); // 1499Result type
A typed alternative to try/catch for operations that can fail predictably.
import { ok, err, isOk, isErr, unwrap, type Result } from "@supacommerce/utils";
function divide(a: number, b: number): Result<number, Error> {
if (b === 0) return err(new Error("Division by zero"));
return ok(a / b);
}
const result = divide(10, 2);
if (isOk(result)) {
console.log(result.value); // 5
}
if (isErr(result)) {
console.error(result.error.message);
}
// Unwrap — returns the value or throws the error
const value = unwrap(divide(10, 2)); // 5Error types
All @supacommerce/client methods throw these typed errors. Import them in your catch blocks for precise handling.
import {
SupacommerceError,
NotFoundError,
ValidationError,
UnauthorizedError,
ForbiddenError,
ConflictError,
InventoryError,
PaymentError,
} from "@supacommerce/utils";| Class | Status | Code |
|---|---|---|
NotFoundError | 404 | NOT_FOUND |
ValidationError | 400 | VALIDATION_ERROR |
UnauthorizedError | 401 | UNAUTHORIZED |
ForbiddenError | 403 | FORBIDDEN |
ConflictError | 409 | CONFLICT |
InventoryError | 422 | INVENTORY_ERROR |
PaymentError | 402 | PAYMENT_ERROR |
Every error has statusCode, code, and message properties inherited from SupacommerceError.
const e = new NotFoundError("Product", "abc-123");
e.statusCode; // 404
e.code; // "NOT_FOUND"
e.message; // "Product with id 'abc-123' not found"
// ValidationError also has a fields property
const ve = new ValidationError("Invalid input", {
email: "Must be a valid email address",
});
ve.fields; // { email: "Must be a valid email address" }Pagination
import {
buildPaginatedResult,
type PaginationParams,
type PaginatedResult,
} from "@supacommerce/utils";
const result = buildPaginatedResult(data, totalCount, { limit: 20, offset: 0 });
// result.data — the items
// result.count — total count across all pages
// result.limit — limit used
// result.offset — offset used
// result.hasMore — true if more pages existID generation
Generates Stripe/Medusa-style prefixed IDs.
import { generateId } from "@supacommerce/utils";
generateId("cart"); // "cart_a1b2c3d4e5f6g7h8"
generateId("order"); // "order_x9y8z7w6v5u4t3s2"Date helpers
import { nowISO, isPast, isFuture } from "@supacommerce/utils";
nowISO(); // "2024-06-15T12:34:56.789Z"
isPast("2020-01-01T00:00:00Z"); // true
isFuture("2099-01-01T00:00:00Z"); // trueType utilities
import type {
RequireKeys,
PartialExcept,
DeepPartial,
} from "@supacommerce/utils";
// Make specific keys required
type T = RequireKeys<{ a?: string; b?: string }, "a">;
// { a: string; b?: string }
// Make all keys optional except specified ones
type T = PartialExcept<{ a: string; b: string; c: string }, "a">;
// { a: string; b?: string; c?: string }
// Deep partial — all nested objects also become partial
type T = DeepPartial<{ a: { b: string } }>;
// { a?: { b?: string } }