4.5 KiB
OpenSpec Change: enterprise-gaming-economy
- Change ID: enterprise-gaming-economy
- Package:
@suaveplan/economy - Category: gaming/narrative
- Target path:
gaming/narrative/economy - Status: PROPOSED
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/economy.md
Summary
Deliver @suaveplan/economy on the genesis branch from scratch at enterprise quality. The package provides an in-game economy with player wallets, peer-to-peer trading, a marketplace (fixed-price listings), and an auction system with bidding and expiry. Wallet tracks multi-currency balances with transactional add/subtract; Marketplace holds Listing objects with configurable stock and price; AuctionHouse manages Auction objects with bidding history, reserve price, and expiry timers. Genesis adds Zod schemas, structured errors, OTel transaction counters, injectable clock, and 100% coverage with concurrency invariant tests.
Current-state audit (genesis)
Package does not exist on genesis yet — fresh build.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
EconomyError; ECONOMY.* codes |
@suaveplan/types (workspace:^) |
Clock, IdGenerator |
@suaveplan/events (workspace:^) |
economy:purchase, economy:bid, economy:auction_end events |
zod (catalog:forms) |
Listing, Auction, WalletConfig schemas |
@opentelemetry/api (catalog:observability) |
Transaction counter, active listings gauge |
Public API surface
function createWallet(playerId: string, options?: WalletOptions): Wallet
interface WalletOptions {
currencies?: string[] // registered currency codes, default ['GOLD']
initialBalances?: Record<string, number>
}
interface Wallet {
getBalance(currency?: string): number
add(amount: number, currency?: string, reason?: string): void
subtract(amount: number, currency?: string, reason?: string): void
canAfford(amount: number, currency?: string): boolean
getHistory(): Transaction[]
}
interface Transaction {
id: string
type: 'credit' | 'debit'
amount: number
currency: string
reason?: string
timestamp: number
}
// Marketplace
function createMarketplace(options?: MarketplaceOptions): Marketplace
interface Marketplace {
createListing(seller: string, itemId: string, price: number, stock: number, currency?: string): Listing
getListing(listingId: string): Listing | undefined
listAll(filter?: ListingFilter): Listing[]
purchase(listingId: string, buyerWallet: Wallet, quantity?: number): PurchaseResult
removeListing(listingId: string): void
}
interface Listing {
id: string
seller: string
itemId: string
price: number
currency: string
stock: number
createdAt: number
}
interface PurchaseResult {
itemId: string
quantity: number
totalCost: number
}
// Auction house
function createAuctionHouse(options?: AuctionHouseOptions): AuctionHouse
interface AuctionHouse {
createAuction(options: AuctionOptions): Auction
bid(auctionId: string, bidderId: string, bidderWallet: Wallet, amount: number): BidResult
tick(dt: number): ExpiredAuction[]
getAuction(id: string): Auction | undefined
listActive(): Auction[]
}
interface Auction {
id: string
seller: string
itemId: string
startingPrice: number
reservePrice?: number
currentBid: number
currentBidder: string | null
endsAt: number
}
Error codes
type EconomyErrorCode =
| 'ECONOMY_INSUFFICIENT_FUNDS'
| 'ECONOMY_LISTING_NOT_FOUND'
| 'ECONOMY_OUT_OF_STOCK'
| 'ECONOMY_BID_TOO_LOW'
| 'ECONOMY_AUCTION_ENDED'
| 'ECONOMY_SELF_BID'
File layout
gaming/narrative/economy/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/index.ts
types/common.ts
types/common.md
core/
wallet/
wallet.ts
wallet.md
wallet.types.ts
wallet.test.ts
index.ts
marketplace/
marketplace.ts
marketplace.md
marketplace.types.ts
marketplace.test.ts
index.ts
auction-house/
auction-house.ts
auction-house.md
auction-house.types.ts
auction-house.test.ts
index.ts
tests/
integration.test.ts
Impact
Downstream: @suaveplan/react-bank displays wallet state; @suaveplan/api-plugin-virtual-currency provides HTTP surface. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events.