@classytic/cart
v0.1.3
Published
Framework-agnostic universal cart engine for commerce — drafts, checkouts, reservations, the LineKind strategy, and a disciplined finalization saga. Powered by MongoDB via @classytic/mongokit. Zero framework deps — usable from Arc, Express, Nest, Next
Downloads
541
Readme
@classytic/cart
Framework-agnostic universal cart engine for commerce.
One cart primitive. Every business. Zero framework dependencies.
import { createCart, skuKind } from '@classytic/cart';
const cart = await createCart({
connection: mongoose.connection,
kinds: [skuKind],
bridges: { catalog: myCatalogBridge },
// Optional for durable event persistence. Recommended in production.
outbox: myAppOutboxStore,
});
// Add items — display snapshot captured automatically (zero populate)
await cart.repositories.draft.addItem(
{ kind: 'sku', payload: { skuRef: 'sku_shirt' }, quantity: 2 },
ctx,
);
// Freeze the draft into a checkout snapshot
const checkout = await cart.repositories.checkout.createFromDraft(
{ draftPublicId: draft.publicId, expectedPricingHash: hash },
ctx,
);
// Host does payment + order creation (cart doesn't own these)
const paymentResult = await stripe.paymentIntents.create({ ... });
const order = await Order.create({ checkoutRef: checkout.publicId });
// Commit (or cancel)
await cart.repositories.checkout.commit(
{ checkoutPublicId: checkout.publicId, externalRef: String(order._id) },
ctx,
);How features activate
No modes. No tiers. Pass the bridges you need:
| Bridge | What lights up |
|---|---|
| catalog (required) | Draft CRUD, checkout, pricing |
| + inventory | Reservation holds during checkout |
| + promotions | Promo-apply step in pricing pipeline |
| + tax | Tax step in pricing pipeline |
| + shipping | Shipping step in pricing pipeline |
What it fits
| Scenario | Uses cart? |
|---|---|
| Retail e-commerce | Yes — multi-item basket with SKU lines |
| Food delivery | Yes — basket + variants + delivery window |
| Cinema / event ticketing (web) | Yes — multi-seat with held-quote |
| Hotel / short-term stay | Yes — date-range pricing + room holds |
| Salon / studio (mixed basket) | Yes — appointment + retail + tip |
| POS scan-and-pay | No — sub-100ms budget, use @classytic/flow |
| Plain subscription signup | No — use @classytic/revenue |
| Pure single-resource booking | No — direct-commit, no basket |
Architecture
- LineKind — one strategy per line category (
sku,seat,stay,tip, custom). Each kind owns validate, price, revalidate, displayOf. - Display snapshots — denormalized on every line at add-time. UI
reads
line.display.namedirectly. Zero.populate()calls. - Optimistic concurrency —
versionfield +ConcurrencyErroron stale writes. - Checkout state —
open → finalized | canceled. Cart snapshots; host commits. - Arc-compatible — event definitions match Arc structurally, and hosts can inject their app-level outbox store for durable event persistence.
Event durability
Cart no longer provisions its own durable Mongo outbox by default.
- If you pass
eventTransportonly, cart is publish-only. - If you pass
eventTransport+outbox, cart gets durable event persistence. - If a write runs with
ctx.sessionand nooutboxis configured, cart throws instead of pretending the event was written durably.
Zero framework dependencies
peerDependencies: mongoose, @classytic/mongokit.
dependencies: zod.
Works from Arc, Express, Nest, Next.js API routes, raw Node, or any backend framework.
Tests
npm test