@supacommerce/utils
v0.13.0
Published
Shared utilities for supacommerce — currency helpers, error types, pagination, Result type
Readme
@supacommerce/utils
Shared utilities for supacommerce. Intentionally public — use these in your own application code.
Installation
pnpm add @supacommerce/utilsCurrency
All monetary values in supacommerce are stored as integers in the smallest currency unit. These helpers handle conversion and formatting.
import { toMinorUnit, fromMinorUnit, formatCurrency, addMoney } 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, 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
} else {
console.error(result.error.message)
}
// Or throw on error
const value = unwrap(divide(10, 2)) // 5Error types
import {
SupacommerceError,
NotFoundError,
ValidationError,
UnauthorizedError,
ForbiddenError,
ConflictError,
InventoryError,
PaymentError,
} from "@supacommerce/utils"
// Each error has statusCode and code properties
const err = new NotFoundError("Product", "abc-123")
err.statusCode // 404
err.code // "NOT_FOUND"
err.message // "Product with id 'abc-123' not found"
const validationErr = new ValidationError("Invalid email", { email: "Must be a valid email" })
validationErr.fields // { email: "Must be a valid email" }Pagination
import { buildPaginatedResult, type PaginationParams, type PaginatedResult } from "@supacommerce/utils"
// Used internally by @supacommerce/core — available for your own queries too
const result = buildPaginatedResult(data, totalCount, { limit: 20, offset: 0 })
// result.data — the items
// result.count — total count
// result.limit — limit used
// result.offset — offset used
// result.hasMore — boolean — whether more pages existID generation
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 }License
MIT
