@stacksjs/commerce 0.70.380 → 0.71.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ import type { BidRow } from '../types';
2
+ /**
3
+ * The bid currently winning a lot, or null when nobody has bid.
4
+ *
5
+ * There is exactly one `leading` row per open lot; `placeBid` maintains that
6
+ * invariant inside a transaction. `orderBy` is still spelled out so a stray
7
+ * second row (a hand-edited record, a restored backup) resolves to the highest
8
+ * one rather than to whatever the storage engine returns first.
9
+ */
10
+ export declare function leadingBid(itemId: number): Promise<BidRow | null>;
11
+ /** Every bid on a lot, newest first - the lot's activity feed. */
12
+ export declare function bidsForItem(itemId: number): Promise<BidRow[]>;
13
+ /** Every bid in an auction, oldest first - the settlement input. */
14
+ export declare function bidsForAuction(auctionId: number): Promise<BidRow[]>;
15
+ /**
16
+ * One bidder's bids across an auction, for the "your bids" panel a parent
17
+ * refreshes all night and for addressing outbid notices.
18
+ */
19
+ export declare function bidsByBidder(auctionId: number, bidderEmail: string): Promise<BidRow[]>;
20
+ /** Distinct bidders in an auction, for audience resolution on notifications. */
21
+ export declare function biddersFor(auctionId: number): Promise<{ name: string, email: string }[]>;
@@ -0,0 +1,2 @@
1
+ export { bidsByBidder, bidsForAuction, bidsForItem, biddersFor, leadingBid } from './fetch';
2
+ export { placeBid } from './place';
@@ -0,0 +1,15 @@
1
+ import type { BidRequest, PlacedBid } from '../types';
2
+ /**
3
+ * Place a bid.
4
+ *
5
+ * The decision of what should happen is the pure engine's
6
+ * (`resolveBid`); everything here is the consequences: which rows change, what
7
+ * the lot's close time becomes, and who needs to be told. Keeping the two apart
8
+ * is what lets a bidding war be tested without a database and a database write
9
+ * be reviewed without re-deriving the auction rules.
10
+ *
11
+ * Every write runs in one transaction. A silent auction's worst failure mode is
12
+ * two `leading` bids on one lot - two people are told they won the same
13
+ * vacation package, and someone has to call one of them back.
14
+ */
15
+ export declare function placeBid(request: BidRequest): Promise<PlacedBid>;
@@ -0,0 +1,22 @@
1
+ import type { AuctionItemRow, AuctionRules } from '../types';
2
+ /**
3
+ * Whether a bid landing at `now` should push this lot's close out, and to when.
4
+ *
5
+ * Sniping is the reason paper bid sheets get guarded by a parent with a
6
+ * stopwatch: a bid dropped in the last four seconds wins not because it was the
7
+ * highest anyone would pay, but because nobody could answer it. Extending the
8
+ * close whenever a bid lands inside the window converts the last minute back
9
+ * into an auction - the lot ends when bidding actually stops.
10
+ *
11
+ * `max_extensions` is the counterweight. Without it two determined bidders can
12
+ * hold one lot open indefinitely, and the gala staff cannot go home. Once a lot
13
+ * has been extended that many times it closes on schedule, and the bidding war
14
+ * is settled by whoever was ahead.
15
+ *
16
+ * Returns null when nothing should change.
17
+ */
18
+ export declare function extendedCloseAt(item: Pick<AuctionItemRow, 'closes_at' | 'extension_count'>, closesAt: Date, rules: Pick<AuctionRules, 'antiSnipeMinutes' | 'extendOnBidWindowMinutes' | 'maxExtensions'>, now: Date): Date | null;
19
+ /**
20
+ * The close time in force for a lot: its own override, else the auction's.
21
+ */
22
+ export declare function effectiveCloseAt(item: Pick<AuctionItemRow, 'closes_at'>, auctionClosesAt: string | Date): Date;
@@ -0,0 +1,29 @@
1
+ import type { AuctionItemRow, IncrementTier } from '../types';
2
+ /**
3
+ * The increment that applies at `amount`.
4
+ *
5
+ * A lot's own `min_increment` overrides the ladder entirely - that is the
6
+ * escape hatch for the lot that wants round hundreds regardless of where the
7
+ * bidding starts.
8
+ */
9
+ export declare function incrementFor(amount: number, ladder?: IncrementTier[], itemIncrement?: number | null): number;
10
+ /**
11
+ * The smallest bid that can be placed on this lot right now.
12
+ *
13
+ * With no bids yet that is the starting bid itself, not the starting bid plus
14
+ * an increment: the first bidder should be able to type the number printed on
15
+ * the bid sheet.
16
+ */
17
+ export declare function nextMinimumBid(item: Pick<AuctionItemRow, 'starting_bid' | 'min_increment'>, leadingAmount: number | null, ladder?: IncrementTier[]): number;
18
+ /**
19
+ * The default increment ladder, in cents.
20
+ *
21
+ * A flat increment is wrong at both ends of a gala catalogue: $5 steps turn a
22
+ * $4,000 vacation package into a hundred-bid slog, and $100 steps price
23
+ * everyone out of the $60 class art project. The ladder is what a live
24
+ * auctioneer does by instinct - bigger money moves in bigger steps.
25
+ *
26
+ * Tiers are read in order and the first whose `upTo` exceeds the current
27
+ * amount wins, so they must stay sorted ascending with a single `null` last.
28
+ */
29
+ export declare const DEFAULT_INCREMENT_LADDER: IncrementTier[];
@@ -0,0 +1,14 @@
1
+ export type { IncomingBid, LeadingBid } from './proxy';
2
+ /**
3
+ * The pure auction engine.
4
+ *
5
+ * Nothing in here reads a clock, a config file or a database - every input is
6
+ * an argument. That is what lets the interesting rules (a proxy-bid war, a tie,
7
+ * an anti-snipe extension at the boundary, a reserve that was not met) be
8
+ * tested exhaustively in milliseconds, and it is why the persistence layer in
9
+ * `../bids` and `../auctions` stays as thin as it does.
10
+ */
11
+ export { effectiveCloseAt, extendedCloseAt } from './anti-snipe';
12
+ export { DEFAULT_INCREMENT_LADDER, incrementFor, nextMinimumBid } from './increments';
13
+ export { resolveBid } from './proxy';
14
+ export { determineWinner, settle } from './winners';
@@ -0,0 +1,27 @@
1
+ import type { AuctionItemRow, BidResolution, BidRow, IncrementTier } from '../types';
2
+ /**
3
+ * Resolve one incoming bid against the standing leader.
4
+ *
5
+ * This is proxy bidding, the model every online auction has converged on: a
6
+ * bidder states a ceiling, and the house bids on their behalf in increments
7
+ * only as far as it must. The visible number is therefore almost never the
8
+ * ceiling - it is one increment above whatever the losing side was willing to
9
+ * pay, which is exactly the price discovery a paper bid sheet cannot do.
10
+ *
11
+ * The function is pure. It reads no clock, touches no database, and returns
12
+ * what *should* happen; `placeBid` is what makes it so. That split is what
13
+ * makes the interesting cases - a ceiling war, a tie, a bidder raising their
14
+ * own maximum - testable without a gala.
15
+ */
16
+ export declare function resolveBid(item: Pick<AuctionItemRow, 'starting_bid' | 'min_increment' | 'buy_now_price'>, leader: LeadingBid, incoming: IncomingBid, ladder?: IncrementTier[]): BidResolution;
17
+ /**
18
+ * The bid a challenger is offering, reduced to what the engine needs.
19
+ */
20
+ export declare interface IncomingBid {
21
+ bidderEmail: string
22
+ bidderName: string
23
+ amount: number
24
+ maxAmount?: number | null
25
+ }
26
+ /** The current leader, or null when nobody has bid on the lot yet. */
27
+ export type LeadingBid = Pick<BidRow, 'bidder_email' | 'bidder_name' | 'amount' | 'max_amount'> | null;
@@ -0,0 +1,22 @@
1
+ import type { AuctionItemRow, AuctionSettlement, BidRow, ItemOutcome, PledgeRow } from '../types';
2
+ /**
3
+ * Decide a single lot's outcome from its bids.
4
+ *
5
+ * Ordering is explicit rather than inherited from whatever the query returned:
6
+ * highest amount wins, and the earlier bid wins a tie. Two bids can legitimately
7
+ * share an amount when one bidder's proxy stepped up to exactly another's
8
+ * ceiling, and "first to commit" is the rule every bid sheet has always used.
9
+ */
10
+ export declare function determineWinner(item: Pick<AuctionItemRow, 'id' | 'lot_number' | 'title' | 'reserve_price'>, bids: BidRow[]): ItemOutcome;
11
+ /**
12
+ * Roll a set of lot outcomes and pledges into the number the school actually
13
+ * cares about.
14
+ *
15
+ * `valueDelta` compares winning bids against fair market value. It is the
16
+ * honest read on a catalogue: a positive delta means donors paid over the value
17
+ * of what they took home, which is the point of a benefit auction; a negative
18
+ * one means the room got bargains and the procurement committee has a
19
+ * conversation to have. Lots with no stated value are excluded rather than
20
+ * counted as zero, which would make every catalogue look like a loss.
21
+ */
22
+ export declare function settle(items: Pick<AuctionItemRow, 'id' | 'fair_market_value'>[], outcomes: ItemOutcome[], pledges: PledgeRow[], opts: { auctionId: number, currency: string, goalAmount?: number | null }): AuctionSettlement;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Auctions.
3
+ *
4
+ * A benefit auction is commerce with the prices taken out: lots instead of
5
+ * products, bidders instead of customers, and a hard stop time instead of a
6
+ * checkout. It sits inside commerce because everything downstream of the
7
+ * hammer - what was sold, for how much, who owes it, what gets receipted - is
8
+ * ordinary commerce, and splitting the two would mean two vocabularies for one
9
+ * night's money.
10
+ *
11
+ * Three layers, deliberately separable:
12
+ *
13
+ * engine/ pure rules - increments, proxy bidding, anti-snipe, winners
14
+ * lots/ bids/ pledges/ persistence on top of those rules
15
+ * realtime, notifications how the room and the bidders find out
16
+ *
17
+ * Everything is in integer cents. See `./types` for the row shapes and why they
18
+ * are declared here rather than imported from the generated ORM types.
19
+ *
20
+ * Both spellings work, since a file that only places bids should not have to
21
+ * reach through two namespaces to do it:
22
+ *
23
+ * commerce.auctions.placeBid(...) flat, like every other commerce module
24
+ * commerce.auctions.bids.placeBid(...) grouped, when a file touches several layers
25
+ */
26
+ export * from './bids/index';
27
+ export * as bids from './bids/index';
28
+ export * from './engine/index';
29
+ export * as engine from './engine/index';
30
+ export * from './lots/index';
31
+ export * as lots from './lots/index';
32
+ export * from './notifications';
33
+ export * from './pledges/index';
34
+ export * as pledges from './pledges/index';
35
+ export * from './realtime';
36
+ export * from './rules';
37
+ export * from './types';
@@ -0,0 +1,38 @@
1
+ import { nextMinimumBid } from '../engine/increments';
2
+ import type { AuctionItemRow, AuctionRow } from '../types';
3
+ export declare function fetchAuction(id: number): Promise<AuctionRow | null>;
4
+ /** The auction attached to an event, which is how the public page finds it. */
5
+ export declare function fetchAuctionForEvent(eventId: number): Promise<AuctionRow | null>;
6
+ export declare function fetchItems(auctionId: number): Promise<AuctionItemRow[]>;
7
+ /**
8
+ * The catalogue as a bidder sees it: every lot with its current price, how
9
+ * contested it is, and the number to beat.
10
+ *
11
+ * One grouped query rather than a query per lot - a gala catalogue is a hundred
12
+ * lots and the page is opened by a few hundred phones at once, so the N+1 here
13
+ * would be the whole evening's load.
14
+ *
15
+ * Proxy ceilings are not selected. They must never reach a template.
16
+ */
17
+ export declare function fetchCatalogue(auctionId: number): Promise<ItemWithBidState[]>;
18
+ /**
19
+ * The live numbers behind the gala monitor: what the room has committed so far,
20
+ * against the goal.
21
+ */
22
+ export declare function auctionTotals(auctionId: number): Promise<AuctionTotals>;
23
+ export declare interface ItemWithBidState extends AuctionItemRow {
24
+ currentBid: number
25
+ bidCount: number
26
+ nextMinimumBid: number
27
+ leaderName: string | null
28
+ }
29
+ export declare interface AuctionTotals {
30
+ currentBidTotal: number
31
+ pledgeTotal: number
32
+ raised: number
33
+ goalAmount: number | null
34
+ bidCount: number
35
+ bidderCount: number
36
+ lotsOffered: number
37
+ lotsWithBids: number
38
+ }
@@ -0,0 +1,3 @@
1
+ export type { AuctionTotals, ItemWithBidState } from './fetch';
2
+ export { auctionTotals, fetchAuction, fetchAuctionForEvent, fetchCatalogue, fetchItems } from './fetch';
3
+ export { closeAuction, closeDueItems, closeItem, closingSoon, openAuction, settleAuction } from './lifecycle';
@@ -0,0 +1,42 @@
1
+ import type { AuctionItemRow, AuctionRow, AuctionSettlement, ItemOutcome } from '../types';
2
+ /**
3
+ * Open an auction for bidding.
4
+ *
5
+ * Lots go open with it. A lot held back deliberately (a live-auction headline
6
+ * item that is only listed for display) keeps whatever status it already has if
7
+ * it is not `draft`.
8
+ */
9
+ export declare function openAuction(auctionId: number, now?: Date): Promise<AuctionRow | null>;
10
+ /**
11
+ * Close one lot and decide it.
12
+ *
13
+ * Returns the outcome, or null when the lot was already resolved - which makes
14
+ * the function safe to call from a per-minute job that may overlap with an
15
+ * organizer clicking "close now" on the same lot.
16
+ */
17
+ export declare function closeItem(item: AuctionItemRow, now?: Date): Promise<ItemOutcome | null>;
18
+ /**
19
+ * Close every lot whose time is up, honouring anti-snipe extensions.
20
+ *
21
+ * This is what the per-minute job calls. It reads each lot's own close time
22
+ * (which a late bid may have pushed out seconds ago) rather than the auction's,
23
+ * so an extension always wins over the schedule.
24
+ */
25
+ export declare function closeDueItems(auctionId: number, now?: Date): Promise<ItemOutcome[]>;
26
+ /**
27
+ * Close the auction itself: resolve every remaining lot, then mark it closed.
28
+ */
29
+ export declare function closeAuction(auctionId: number, now?: Date): Promise<ItemOutcome[]>;
30
+ /**
31
+ * The settlement sheet: what every lot did, what the night raised, and how it
32
+ * measured against the goal and against fair market value.
33
+ *
34
+ * Read-only by default. Pass `{ markSettled: true }` once the school has
35
+ * actually invoiced, which is a decision a person makes, not a job.
36
+ */
37
+ export declare function settleAuction(auctionId: number, opts?: { markSettled?: boolean, now?: Date }): Promise<AuctionSettlement | null>;
38
+ /**
39
+ * Lots that close within `withinMinutes`, for the "closing soon" notice and for
40
+ * the organizer's watchlist of items still short of their fair market value.
41
+ */
42
+ export declare function closingSoon(auctionId: number, withinMinutes: number, now?: Date): Promise<AuctionItemRow[]>;
@@ -0,0 +1,34 @@
1
+ import type { AuctionItemRow, ItemOutcome, PlacedBid } from './types';
2
+ /**
3
+ * Told to the bidder who just lost the lead.
4
+ *
5
+ * It names the lot and the number to beat, because the entire purpose of the
6
+ * message is to let someone re-bid from their phone in one tap. A notice that
7
+ * only says "you have been outbid" makes them go find the lot themselves, and
8
+ * most of them do not.
9
+ */
10
+ export declare function outbidNotification(result: PlacedBid, currency?: string): AuctionNotification | null;
11
+ /** Told to the winner of a lot once it closes. */
12
+ export declare function winnerNotification(outcome: ItemOutcome, currency?: string): AuctionNotification | null;
13
+ /**
14
+ * Told to everyone still leading a lot that is about to close, and to bidders
15
+ * watching one. Sent by the app's scheduler rather than inline, because "soon"
16
+ * is a decision about the school's evening, not about this bid.
17
+ */
18
+ export declare function closingSoonNotification(item: AuctionItemRow, to: { name: string, email: string }, currentBid: number, currency?: string): AuctionNotification;
19
+ /**
20
+ * Notification payloads for the three things a bidder must be told.
21
+ *
22
+ * These are builders, not senders. The auction package has no opinion about
23
+ * whether a school reaches parents by email, SMS or a row in their dashboard
24
+ * inbox - it knows what happened and phrases it; `@stacksjs/notifications`
25
+ * decides where it goes. That split is also what lets an app schedule a notice
26
+ * for later (the closing-soon nudge) rather than sending it inline.
27
+ */
28
+ export declare interface AuctionNotification {
29
+ to: { name: string, email: string }
30
+ subject: string
31
+ body: string
32
+ type: 'outbid' | 'winner' | 'closing_soon'
33
+ data: Record<string, unknown>
34
+ }
@@ -0,0 +1,27 @@
1
+ import type { PledgeRow, PledgeStatus } from '../types';
2
+ /**
3
+ * Record a fund-a-need pledge.
4
+ *
5
+ * Fund-a-need is the part of a benefit auction that is not an auction at all:
6
+ * the room is asked to give at fixed levels and nobody competes for anything.
7
+ * It shares the auction only for the tally board, which is why it lives beside
8
+ * bidding but never passes through the bidding engine.
9
+ */
10
+ export declare function makePledge(request: PledgeRequest): Promise<PledgeRow>;
11
+ /** Confirmed pledge money for an auction, in cents. */
12
+ export declare function pledgeTotal(auctionId: number): Promise<number>;
13
+ export declare function fetchPledges(auctionId: number): Promise<PledgeRow[]>;
14
+ /**
15
+ * The tally board: how much each level has raised and how many gave at it.
16
+ * Levels are reported in descending gift size, which is the order they are read
17
+ * out in the room.
18
+ */
19
+ export declare function pledgeLevels(auctionId: number): Promise<{ level: string, amount: number, count: number }[]>;
20
+ export declare interface PledgeRequest {
21
+ auctionId: number
22
+ donorName: string
23
+ donorEmail: string
24
+ amount: number
25
+ level?: string | null
26
+ status?: PledgeStatus
27
+ }
@@ -0,0 +1,20 @@
1
+ import type { PlacedBid, PledgeRow } from './types';
2
+ /**
3
+ * The channel an auction's live updates are published on. One channel per
4
+ * auction rather than per lot: a gala's bidding page shows the whole catalogue
5
+ * at once, and a phone in a gym should hold one socket, not forty.
6
+ */
7
+ export declare function auctionChannel(auctionId: number): string;
8
+ /**
9
+ * Publish a bid to everyone watching the auction.
10
+ *
11
+ * The payload carries the new price and the next minimum rather than a
12
+ * "refresh" signal, so a phone that is already on the lot updates in place. It
13
+ * deliberately does NOT carry proxy ceilings: the hidden maximum is the one
14
+ * number that must never leave the server, or the whole mechanism collapses.
15
+ */
16
+ export declare function broadcastBid(result: PlacedBid): Promise<void>;
17
+ /** Publish a fund-a-need pledge, for the running total on the tally board. */
18
+ export declare function broadcastPledge(pledge: PledgeRow, runningTotal: number): Promise<void>;
19
+ /** Publish a lot closing, so open bid sheets stop accepting input. */
20
+ export declare function broadcastItemClosed(auctionId: number, itemId: number, outcome: { status: string, amount: number, winnerName?: string }): Promise<void>;
@@ -0,0 +1,17 @@
1
+ import type { AuctionRow, AuctionRules } from './types';
2
+ /**
3
+ * The rules in force for one auction: the app's `config/auction.ts` defaults,
4
+ * with the auction row's own columns winning where they are set.
5
+ *
6
+ * Per-auction overrides matter because one school runs both a two-week online
7
+ * catalogue (long extensions, forgiving windows) and a ninety-minute in-room
8
+ * gala (two-minute extensions, or none at all) in the same season, and neither
9
+ * should have to be the global default.
10
+ */
11
+ export declare function rulesFor(auction?: Pick<AuctionRow, 'anti_snipe_minutes' | 'extend_on_bid_window_minutes' | 'max_extensions'> | null): AuctionRules;
12
+ /**
13
+ * The auction's currency, falling back to the storefront's. An auction
14
+ * inherits the currency the rest of commerce already runs in unless it says
15
+ * otherwise - a school does not raise money in a different one than it bills in.
16
+ */
17
+ export declare function currencyFor(auction?: Pick<AuctionRow, 'currency'> | null): string;
@@ -0,0 +1,168 @@
1
+ export declare interface AuctionRow {
2
+ id: number
3
+ uuid?: string
4
+ event_id: number
5
+ title: string
6
+ description?: string | null
7
+ status: AuctionStatus
8
+ currency: string
9
+ goal_amount?: number | null
10
+ opens_at: string | Date
11
+ closes_at: string | Date
12
+ anti_snipe_minutes: number
13
+ extend_on_bid_window_minutes: number
14
+ max_extensions: number
15
+ }
16
+ export declare interface AuctionItemRow {
17
+ id: number
18
+ uuid?: string
19
+ auction_id: number
20
+ lot_number: number
21
+ title: string
22
+ description?: string | null
23
+ image_url?: string | null
24
+ category?: string | null
25
+ donor_name?: string | null
26
+ fair_market_value?: number | null
27
+ starting_bid: number
28
+ min_increment?: number | null
29
+ buy_now_price?: number | null
30
+ reserve_price?: number | null
31
+ status: AuctionItemStatus
32
+ closes_at?: string | Date | null
33
+ extension_count: number
34
+ }
35
+ export declare interface BidRow {
36
+ id: number
37
+ uuid?: string
38
+ auction_item_id: number
39
+ auction_id: number
40
+ bidder_name: string
41
+ bidder_email: string
42
+ amount: number
43
+ max_amount?: number | null
44
+ status: BidStatus
45
+ placed_at: string | Date
46
+ }
47
+ export declare interface PledgeRow {
48
+ id: number
49
+ uuid?: string
50
+ auction_id: number
51
+ donor_name: string
52
+ donor_email: string
53
+ amount: number
54
+ level?: string | null
55
+ status: PledgeStatus
56
+ created_at?: string | Date
57
+ }
58
+ /**
59
+ * One rung of the increment ladder: below `upTo` cents, bids step by `step`
60
+ * cents. The last rung carries `upTo: null` and applies to everything above.
61
+ */
62
+ export declare interface IncrementTier {
63
+ upTo: number | null
64
+ step: number
65
+ }
66
+ export declare interface AuctionRules {
67
+ increments: IncrementTier[]
68
+ antiSnipeMinutes: number
69
+ extendOnBidWindowMinutes: number
70
+ maxExtensions: number
71
+ }
72
+ /** What a caller passes to `placeBid`. */
73
+ export declare interface BidRequest {
74
+ itemId: number
75
+ bidderName: string
76
+ bidderEmail: string
77
+ amount: number
78
+ maxAmount?: number | null
79
+ now?: Date
80
+ }
81
+ /**
82
+ * The result of resolving one bid against the current leader. This is what the
83
+ * pure engine returns; the persistence layer turns it into row writes and the
84
+ * caller turns it into notifications.
85
+ */
86
+ export declare interface BidResolution {
87
+ accepted: boolean
88
+ reason?: BidRejectionReason
89
+ message?: string
90
+ leader?: {
91
+ bidderEmail: string
92
+ bidderName: string
93
+ amount: number
94
+ maxAmount: number | null
95
+ isChallenger: boolean
96
+ }
97
+ challengerAmount?: number
98
+ outbid?: {
99
+ bidderEmail: string
100
+ bidderName: string
101
+ amount: number
102
+ }
103
+ buyNow?: boolean
104
+ nextMinimumBid: number
105
+ }
106
+ export declare interface PlacedBid extends BidResolution {
107
+ bid?: BidRow
108
+ item?: AuctionItemRow
109
+ extendedTo?: Date
110
+ }
111
+ export declare interface ItemOutcome {
112
+ itemId: number
113
+ lotNumber: number
114
+ title: string
115
+ status: AuctionItemStatus
116
+ winningBidId?: number
117
+ winnerName?: string
118
+ winnerEmail?: string
119
+ amount: number
120
+ passedReason?: 'no_bids' | 'reserve_not_met'
121
+ }
122
+ export declare interface AuctionSettlement {
123
+ auctionId: number
124
+ currency: string
125
+ totalRaised: number
126
+ bidRevenue: number
127
+ pledgeRevenue: number
128
+ goalAmount: number | null
129
+ itemsSold: number
130
+ itemsPassed: number
131
+ sellThrough: number
132
+ valueDelta: number
133
+ outcomes: ItemOutcome[]
134
+ }
135
+ /**
136
+ * Auction types.
137
+ *
138
+ * Money is integer minor units (cents) everywhere - amounts, increments,
139
+ * ceilings, goals and totals alike. A silent auction adds thousands of bids
140
+ * together and then reports the number to a board of trustees; floats lose that
141
+ * argument. Formatting happens at the edge, never in this package.
142
+ *
143
+ * The row shapes below are declared here rather than imported from
144
+ * `@stacksjs/orm`'s generated model types on purpose. This package has to build
145
+ * and run against an app whose ORM types were generated before the auction
146
+ * models existed - which is every app that installs the published framework
147
+ * before the next release. The models under
148
+ * `storage/framework/defaults/app/Models/commerce/` are the source of the
149
+ * columns; these interfaces are the contract the engine reads them through.
150
+ */
151
+ /** Where an auction is in its life. */
152
+ export type AuctionStatus = 'draft' | 'preview' | 'open' | 'closed' | 'settled';
153
+ /** Where a single lot is in its life. */
154
+ export type AuctionItemStatus = 'open' | 'closed' | 'sold' | 'passed';
155
+ /**
156
+ * Bid state. `leading` is the one bid per item that currently wins; every
157
+ * other live bid is `outbid`. Both resolve to `won` / `lost` at close.
158
+ * `invalid` is a bid an organizer retracted (a mis-keyed amount, a guest who
159
+ * bid on the wrong lot), kept for the audit trail rather than deleted.
160
+ */
161
+ export type BidStatus = 'leading' | 'outbid' | 'won' | 'lost' | 'invalid';
162
+ export type PledgeStatus = 'pending' | 'confirmed' | 'cancelled';
163
+ export type BidRejectionReason = | 'auction_not_open'
164
+ | 'item_not_open'
165
+ | 'below_minimum'
166
+ | 'max_below_amount'
167
+ | 'already_leading'
168
+ | 'item_not_found';
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as auctions from './auctions/index';
1
2
  import * as coupons from './coupons/index';
2
3
  import * as customers from './customers/index';
3
4
  import * as devices from './devices/index';
@@ -13,6 +14,7 @@ import * as tax from './tax/index';
13
14
  import * as waitlists from './waitlists/index';
14
15
  export declare const commerce: CommerceNamespace;
15
16
  export declare interface CommerceNamespace {
17
+ auctions: AuctionsModule
16
18
  coupons: CouponsModule
17
19
  customers: CustomersModule
18
20
  errors: ErrorsModule
@@ -27,6 +29,7 @@ export declare interface CommerceNamespace {
27
29
  devices: DevicesModule
28
30
  receipts: ReceiptsModule
29
31
  }
32
+ declare type AuctionsModule = typeof auctions;
30
33
  declare type CouponsModule = typeof coupons;
31
34
  declare type CustomersModule = typeof customers;
32
35
  declare type ErrorsModule = typeof errors;
@@ -41,6 +44,7 @@ declare type WaitlistsModule = typeof waitlists;
41
44
  declare type DevicesModule = typeof devices;
42
45
  declare type ReceiptsModule = typeof receipts;
43
46
  export {
47
+ auctions,
44
48
  coupons,
45
49
  customers,
46
50
  devices,