@nehorai/credits 2.1.0 → 3.0.0
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.
- package/LICENSE +21 -0
- package/dist/adapters/index.cjs +3 -2
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +2 -2
- package/dist/adapters/index.d.ts +2 -2
- package/dist/adapters/index.js +3 -2
- package/dist/adapters/index.js.map +1 -1
- package/dist/core/index.cjs +218 -2
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +183 -5
- package/dist/core/index.d.ts +183 -5
- package/dist/core/index.js +199 -2
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +477 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +456 -59
- package/dist/index.js.map +1 -1
- package/dist/repository/index.cjs +394 -44
- package/dist/repository/index.cjs.map +1 -1
- package/dist/repository/index.d.cts +36 -7
- package/dist/repository/index.d.ts +36 -7
- package/dist/repository/index.js +391 -43
- package/dist/repository/index.js.map +1 -1
- package/dist/sdk/index.cjs +1 -0
- package/dist/sdk/index.cjs.map +1 -1
- package/dist/sdk/index.d.cts +2 -2
- package/dist/sdk/index.d.ts +2 -2
- package/dist/sdk/index.js +1 -0
- package/dist/sdk/index.js.map +1 -1
- package/dist/service/index.cjs +33 -20
- package/dist/service/index.cjs.map +1 -1
- package/dist/service/index.d.cts +2 -2
- package/dist/service/index.d.ts +2 -2
- package/dist/service/index.js +33 -20
- package/dist/service/index.js.map +1 -1
- package/dist/{types-Dzc443vy.d.cts → types-C3sxDvNb.d.cts} +43 -2
- package/dist/{types-Dzc443vy.d.ts → types-C3sxDvNb.d.ts} +43 -2
- package/dist/{types-BhCdep_t.d.ts → types-CY-oHIMA.d.ts} +55 -4
- package/dist/{types-wtksMa4n.d.cts → types-DOjguhEX.d.cts} +55 -4
- package/package.json +70 -71
package/dist/core/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { R as ReservationStatus, a as PortableReservation, P as PortableTransaction, b as CreditSource, J as JournalReferenceType } from '../types-
|
|
2
|
-
export { A as AIProviderType, B as BuiltinTier, g as CreditCheckResult, C as CreditOperationType, D as DeductCreditsResult, M as MonthlyResetResult, f as PortableJournalEntry, d as PortableUsageLog, c as PortableUserCredits, h as ResourceType, e as SubscriptionExpiryResult, S as SubscriptionTier, T as TierConfig, i as TransactionType, U as UsageHistoryEntry, j as UsageHistoryResponse, W as WithCreditsOptions, k as calculateAvailableCredits, t as toDate, l as toPortableTimestamp } from '../types-
|
|
3
|
-
import { T as TerminalReservationStatus, I as ICreditRepository } from '../types-
|
|
4
|
-
export { a as AddCreditsOutcome, C as CommitOutcome, E as ExpireOutcome, R as ReleaseOutcome,
|
|
1
|
+
import { R as ReservationStatus, a as PortableReservation, P as PortableTransaction, b as CreditSource, J as JournalReferenceType, E as ExpiredBackingFrontier } from '../types-C3sxDvNb.cjs';
|
|
2
|
+
export { A as AIProviderType, B as BuiltinTier, g as CreditCheckResult, C as CreditOperationType, D as DeductCreditsResult, M as MonthlyResetResult, f as PortableJournalEntry, d as PortableUsageLog, c as PortableUserCredits, h as ResourceType, e as SubscriptionExpiryResult, S as SubscriptionTier, T as TierConfig, i as TransactionType, U as UsageHistoryEntry, j as UsageHistoryResponse, W as WithCreditsOptions, k as calculateAvailableCredits, t as toDate, l as toPortableTimestamp } from '../types-C3sxDvNb.cjs';
|
|
3
|
+
import { T as TerminalReservationStatus, I as ICreditRepository } from '../types-DOjguhEX.cjs';
|
|
4
|
+
export { a as AddCreditsOutcome, C as CommitOutcome, E as ExpireOutcome, R as ReleaseOutcome, o as ReserveOutcome, q as isCreditedOutcome, r as isReservedOutcome, s as isWinningOutcome } from '../types-DOjguhEX.cjs';
|
|
5
5
|
export { D as DeferredExecutor, c as createDeferredExecutor, g as genericDeferred, n as noopDeferred, s as synchronousDeferred } from '../deferred-C7rrRfkm.cjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -393,6 +393,7 @@ declare function createPaymentRefConflictError(paymentRef: string, details: {
|
|
|
393
393
|
* These live in core rather than in an adapter so every repository — shipped
|
|
394
394
|
* or third-party — validates identically.
|
|
395
395
|
*/
|
|
396
|
+
|
|
396
397
|
/** `numeric(12, 2)`: 12 significant digits, 2 of them after the point. */
|
|
397
398
|
declare const CREDIT_AMOUNT_SCALE = 2;
|
|
398
399
|
/** Largest representable amount: ten integer digits plus two decimals. */
|
|
@@ -431,6 +432,40 @@ declare function toCents(value: number): number | null;
|
|
|
431
432
|
* derived result is not enough and never was.
|
|
432
433
|
*/
|
|
433
434
|
declare function sumAmounts(...values: number[]): number;
|
|
435
|
+
/**
|
|
436
|
+
* Multiply a price by a whole quantity, in cents.
|
|
437
|
+
*
|
|
438
|
+
* `1.1 * 3` is `3.3000000000000003`, which the amount validators refuse: the price
|
|
439
|
+
* was legal, and the float product blames the caller for a rounding artefact. A
|
|
440
|
+
* fractional quantity has no exact result on the grid and is refused rather than
|
|
441
|
+
* rounded, so the rounding policy stays with whoever prices fractions.
|
|
442
|
+
*/
|
|
443
|
+
declare function multiplyAmount(unit: number, quantity: number): number;
|
|
444
|
+
/**
|
|
445
|
+
* The cent-grid value a float landed next to, or the value unchanged.
|
|
446
|
+
*
|
|
447
|
+
* Documents written by older versions can hold the result of IEEE 754 additions
|
|
448
|
+
* (`FieldValue.increment` stored `0.30000000000000004`), and an application can pass
|
|
449
|
+
* a raw float difference to a ledger record. That is noise, not an amount. A value
|
|
450
|
+
* genuinely off the grid, such as `0.005`, is not noise and comes back as it went in.
|
|
451
|
+
*/
|
|
452
|
+
declare function canonicalAmount(value: number): number;
|
|
453
|
+
/**
|
|
454
|
+
* Read a stored amount the account owns as a cent-grid value, rounding down.
|
|
455
|
+
*
|
|
456
|
+
* A stored `24.999` makes every later sum off-grid, the validators then refuse the
|
|
457
|
+
* commit or release that needs it, and the holds on that account lock. Rounding down
|
|
458
|
+
* unlocks them. A value within {@link canonicalAmount}'s noise of the next cent reads as
|
|
459
|
+
* that cent, because a float cannot tell the two apart, so this can add at most that noise:
|
|
460
|
+
* 1e-6 up to 7e7, and about 1.4e-4 at the maximum. A non-finite value is left for the
|
|
461
|
+
* validators to refuse.
|
|
462
|
+
*/
|
|
463
|
+
declare function storedAmountFloor(value: number): number;
|
|
464
|
+
/**
|
|
465
|
+
* Read a stored amount the account owes as a cent-grid value, rounding up, so what
|
|
466
|
+
* stands against the balance is never understated. See {@link storedAmountFloor}.
|
|
467
|
+
*/
|
|
468
|
+
declare function storedAmountCeil(value: number): number;
|
|
434
469
|
/** Amounts that may be *moved*: strictly positive and on the cent grid. */
|
|
435
470
|
declare function isValidCreditAmount(value: unknown): value is number;
|
|
436
471
|
/**
|
|
@@ -479,6 +514,149 @@ declare function assertRepresentableFields(fields: Record<string, unknown>, cont
|
|
|
479
514
|
* fund commits, so they count toward the backing.
|
|
480
515
|
*/
|
|
481
516
|
declare function backedBalanceFloor(reserved: number, bonusCredits: number): number;
|
|
517
|
+
/**
|
|
518
|
+
* The balance left by a cap: lowered to `cap` at most, never raised, and never
|
|
519
|
+
* cut through the credits backing outstanding holds (see
|
|
520
|
+
* {@link backedBalanceFloor}). A balance already under `cap` is left alone,
|
|
521
|
+
* which is the point of computing this from the stored row inside a transaction
|
|
522
|
+
* rather than writing a value the caller read earlier.
|
|
523
|
+
*/
|
|
524
|
+
declare function cappedBalance(balance: number, cap: number, reserved: number, bonusCredits: number): number;
|
|
525
|
+
/** A balance a lowering write lands on, and how much of it only backs holds. */
|
|
526
|
+
interface PlannedBalance {
|
|
527
|
+
balance: number;
|
|
528
|
+
expiredHoldBacking: number;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* The expired credits actually held in a balance: `0 <= held <= balance`, whatever
|
|
532
|
+
* is stored. Every read of `expiredHoldBacking` goes through this. Trusting the stored
|
|
533
|
+
* value made a negative one mint credits: `balance - (-10)` read as spendable, and a
|
|
534
|
+
* close that "used" -10 of backing added 10 to the balance.
|
|
535
|
+
*/
|
|
536
|
+
declare function heldExpiredBacking(balance: number, stored?: number): number;
|
|
537
|
+
/** The part of a balance not kept only for holds. A missing field reads as 0. */
|
|
538
|
+
declare function spendableBalance(balance: number, expiredHoldBacking?: number): number;
|
|
539
|
+
/**
|
|
540
|
+
* An absolute balance write: `target`, floored at what backs outstanding holds.
|
|
541
|
+
* Whatever the floor kept above `target` is not the account's to spend.
|
|
542
|
+
*/
|
|
543
|
+
declare function planBalanceTarget(target: number, reserved: number, bonusCredits: number): PlannedBalance;
|
|
544
|
+
/**
|
|
545
|
+
* A cap on the spendable part of a balance. Like {@link cappedBalance} it never
|
|
546
|
+
* raises the balance and never cuts through what backs holds, and it records the
|
|
547
|
+
* part kept above the cap so a later release cannot hand it back. Credits already
|
|
548
|
+
* marked stay marked, so applying a cap twice changes nothing.
|
|
549
|
+
*/
|
|
550
|
+
declare function planBalanceCap(balance: number, cap: number, reserved: number, bonusCredits: number, expiredHoldBacking?: number): PlannedBalance;
|
|
551
|
+
/**
|
|
552
|
+
* What a closing hold takes out of the credits kept only for holds: its own share
|
|
553
|
+
* and nothing of another hold's. `holdBacking` comes from {@link holdExpiredBacking}.
|
|
554
|
+
*/
|
|
555
|
+
declare function settleExpiredHoldBacking(balance: number, expiredHoldBacking: number, holdBacking: number): {
|
|
556
|
+
used: number;
|
|
557
|
+
remaining: number;
|
|
558
|
+
};
|
|
559
|
+
/** The balances a commit lands on, and whether the account could pay it. */
|
|
560
|
+
interface PlannedHoldCommit {
|
|
561
|
+
covered: boolean;
|
|
562
|
+
balance: number;
|
|
563
|
+
bonusCredits: number;
|
|
564
|
+
expiredHoldBacking: number;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Pay a hold: first its own share of the marked credits, then spendable balance,
|
|
568
|
+
* then bonus. Marked credits backing other holds are never touched, so what this
|
|
569
|
+
* commit costs does not depend on which holds closed before it.
|
|
570
|
+
*/
|
|
571
|
+
declare function planHoldCommit(balance: number, bonusCredits: number, expiredHoldBacking: number, holdBacking: number, amount: number): PlannedHoldCommit;
|
|
572
|
+
/** The fields of an open hold that decide its share of the marked credits. */
|
|
573
|
+
type ExpiredBackingHold = Pick<PortableReservation, "id" | "amount" | "holdPlacedAt" | "expiredBacking">;
|
|
574
|
+
/** A hold whose stored share has to change for a new total. */
|
|
575
|
+
interface ExpiredBackingRewrite {
|
|
576
|
+
id: string;
|
|
577
|
+
expiredBacking: number;
|
|
578
|
+
}
|
|
579
|
+
/** The marked credits on an account and where they end among its open holds. */
|
|
580
|
+
interface ExpiredBackingAllocation {
|
|
581
|
+
total: number;
|
|
582
|
+
frontier: ExpiredBackingFrontier | null;
|
|
583
|
+
/** Stored shares to write with this total. Absent when none changes. */
|
|
584
|
+
rewrites?: ExpiredBackingRewrite[];
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* A frontier before every hold: no hold shares through it. It records that the account's
|
|
588
|
+
* marks are placed, so the next close does not walk the holds again.
|
|
589
|
+
*/
|
|
590
|
+
declare const NO_EXPIRED_BACKING_PREFIX: ExpiredBackingFrontier;
|
|
591
|
+
/**
|
|
592
|
+
* The share a 2.2.0 hold document recorded for itself: the order of record for that hold.
|
|
593
|
+
* Clocks are not, since the server that placed a hold may lag the one before it. Rounded
|
|
594
|
+
* up, like every amount that stands against the balance, and never above the hold. Zero
|
|
595
|
+
* means no record, which leaves the hold to the frontier.
|
|
596
|
+
*/
|
|
597
|
+
declare function recordedExpiredBacking(hold: ExpiredBackingHold): number;
|
|
598
|
+
/**
|
|
599
|
+
* Place `total` marked credits on the open holds, so that what each hold takes does not
|
|
600
|
+
* depend on the order they close in.
|
|
601
|
+
*
|
|
602
|
+
* Shares a 2.2.0 hold document stored stand as recorded: they need not be a prefix of
|
|
603
|
+
* anything, and they sit on documents that already exist. The rest goes to the other
|
|
604
|
+
* holds oldest first, each up to its amount. That part is a prefix, so one
|
|
605
|
+
* {@link ExpiredBackingFrontier} on the account describes it and no hold document changes
|
|
606
|
+
* however many there are. A new hold carries no share, so no record is ever added.
|
|
607
|
+
*
|
|
608
|
+
* A total below the records trims them newest first, and only when every other hold has
|
|
609
|
+
* nothing, and a total the other holds cannot take fills what the records left free. Those
|
|
610
|
+
* are the only hold writes: `rewrites`, at most one per record, each record being an open
|
|
611
|
+
* hold from before the upgrade.
|
|
612
|
+
*
|
|
613
|
+
* Marks no hold can carry stay marked. They were out of reach before this ran, and
|
|
614
|
+
* dropping them would make them spendable; the next absolute balance write replaces
|
|
615
|
+
* them. A row without `holdPlacedAt` is a record, not a hold, and a row with an unusable
|
|
616
|
+
* amount cannot close, so neither takes a share.
|
|
617
|
+
*/
|
|
618
|
+
declare function allocateExpiredBacking(holds: readonly ExpiredBackingHold[], total: number): ExpiredBackingAllocation;
|
|
619
|
+
/**
|
|
620
|
+
* This hold's share of the marked credits: its own record if it has one, otherwise where
|
|
621
|
+
* it falls against the account's frontier. Pass the `rewrites` of an allocation made in
|
|
622
|
+
* this same step, since the document does not hold them yet.
|
|
623
|
+
*/
|
|
624
|
+
declare function holdExpiredBacking(hold: ExpiredBackingHold, frontier?: ExpiredBackingFrontier | null, rewrites?: readonly ExpiredBackingRewrite[]): number;
|
|
625
|
+
/**
|
|
626
|
+
* When a new hold counts as placed: `now`, or just after the frontier if this server's
|
|
627
|
+
* clock is behind the server that placed it. A hold that sorted before the frontier
|
|
628
|
+
* would read as fully marked, taking expired credits that belong to older holds.
|
|
629
|
+
*/
|
|
630
|
+
declare function holdPlacedAtAfter(now: string, frontier?: ExpiredBackingFrontier | null): string;
|
|
631
|
+
/** The account fields that hold the marked credits. */
|
|
632
|
+
interface ExpiredBackingFields {
|
|
633
|
+
expiredHoldBacking?: number;
|
|
634
|
+
expiredBackingFrontier?: ExpiredBackingFrontier | null;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* The fields to write for a new marked total: nothing when it was and stays 0, so an
|
|
638
|
+
* account that never had any keeps a document without the fields. A stored value that
|
|
639
|
+
* is not the new one is overwritten, which is what repairs a bad one. A `frontier` left
|
|
640
|
+
* undefined keeps the stored one.
|
|
641
|
+
*/
|
|
642
|
+
declare function expiredBackingPatch(next: {
|
|
643
|
+
total: number;
|
|
644
|
+
frontier?: ExpiredBackingFrontier | null;
|
|
645
|
+
}, stored: ExpiredBackingFields): ExpiredBackingFields;
|
|
646
|
+
/**
|
|
647
|
+
* Validate a balance target supplied by the application: a cap or the result of
|
|
648
|
+
* a monthly-reset decision. It must be storable and cannot be negative.
|
|
649
|
+
*/
|
|
650
|
+
declare function assertValidBalanceTarget(value: unknown, field: string, context?: Record<string, unknown>): asserts value is number;
|
|
651
|
+
/**
|
|
652
|
+
* Validate how a tier write changes the balance. `balance` sets a value the
|
|
653
|
+
* caller computed and `balanceCap` lowers the stored value, so naming both has
|
|
654
|
+
* no single meaning and is refused.
|
|
655
|
+
*/
|
|
656
|
+
declare function assertValidTierBalanceInput(input: {
|
|
657
|
+
balance?: number;
|
|
658
|
+
balanceCap?: number;
|
|
659
|
+
}, context?: Record<string, unknown>): void;
|
|
482
660
|
/**
|
|
483
661
|
* Validate a limit derived from tier configuration.
|
|
484
662
|
*
|
|
@@ -694,4 +872,4 @@ declare function releaseReservationWithJournal(repository: ICreditRepository, us
|
|
|
694
872
|
*/
|
|
695
873
|
declare function reserveCreditsForOperation(repository: ICreditRepository, userId: string, amount: number, operationType: string, expiryMs?: number, idempotencyKey?: string): Promise<PortableReservation>;
|
|
696
874
|
|
|
697
|
-
export { CREDIT_AMOUNT_MAX, CREDIT_AMOUNT_SCALE, CreditError, CreditErrorCode, type CreditErrorCodeType, CreditSource, JournalReferenceType, type PaymentEventPayload, PortableReservation, PortableTransaction, RESERVATION_STATUSES, RESERVED_JOURNAL_KEY_PREFIX, type ReservationIntegrityView, ReservationStatus, type StoredPaymentEvent, TERMINAL_RESERVATION_STATUSES, TerminalReservationStatus, assertDirectStatusWriteAllowed, assertHoldPlaced, assertKnownReservationStatus, assertPublicJournalKey, assertRepresentableAmount, assertRepresentableFields, assertRepresentableTierAmount, assertUnkeyedDirectReservation, assertUnreferencedDirectTransaction, assertValidCreditAmount, assertValidIdempotencyKey, assertValidStoredAmount, assertValidStoredAmountRaw, backedBalanceFloor, classifyDatabaseError, commitReservationWithJournal, createIdempotencyConflictError, createInsufficientCreditsError, createInvalidAmountError, createInvalidOperationTypeError, createPaymentRefConflictError, createReservationAlreadyProcessedError, createReservationExpiredError, createReservationNotFoundError, createTransientError, createUnsupportedOperationError, createUserNotFoundError, describePaymentMismatch, getSqlState, hasPlacedHold, isCreditError, isIdempotencyConflictError, isInsufficientCreditsError, isRepresentableAmount, isReservationStatus, isReservedJournalKey, isTerminalReservationStatus, isTransientDatabaseError, isTransientError, isUniqueViolation, isValidCreditAmount, isValidIdempotencyKey, normalizePaymentRef, numericToCents, releaseReservationWithJournal, reserveCreditsForOperation, sameAmount, storedMonthlyLimit, sumAmounts, terminalStatusOf, toCents };
|
|
875
|
+
export { CREDIT_AMOUNT_MAX, CREDIT_AMOUNT_SCALE, CreditError, CreditErrorCode, type CreditErrorCodeType, CreditSource, type ExpiredBackingAllocation, type ExpiredBackingFields, ExpiredBackingFrontier, type ExpiredBackingHold, type ExpiredBackingRewrite, JournalReferenceType, NO_EXPIRED_BACKING_PREFIX, type PaymentEventPayload, PortableReservation, PortableTransaction, RESERVATION_STATUSES, RESERVED_JOURNAL_KEY_PREFIX, type ReservationIntegrityView, ReservationStatus, type StoredPaymentEvent, TERMINAL_RESERVATION_STATUSES, TerminalReservationStatus, allocateExpiredBacking, assertDirectStatusWriteAllowed, assertHoldPlaced, assertKnownReservationStatus, assertPublicJournalKey, assertRepresentableAmount, assertRepresentableFields, assertRepresentableTierAmount, assertUnkeyedDirectReservation, assertUnreferencedDirectTransaction, assertValidBalanceTarget, assertValidCreditAmount, assertValidIdempotencyKey, assertValidStoredAmount, assertValidStoredAmountRaw, assertValidTierBalanceInput, backedBalanceFloor, canonicalAmount, cappedBalance, classifyDatabaseError, commitReservationWithJournal, createIdempotencyConflictError, createInsufficientCreditsError, createInvalidAmountError, createInvalidOperationTypeError, createPaymentRefConflictError, createReservationAlreadyProcessedError, createReservationExpiredError, createReservationNotFoundError, createTransientError, createUnsupportedOperationError, createUserNotFoundError, describePaymentMismatch, expiredBackingPatch, getSqlState, hasPlacedHold, heldExpiredBacking, holdExpiredBacking, holdPlacedAtAfter, isCreditError, isIdempotencyConflictError, isInsufficientCreditsError, isRepresentableAmount, isReservationStatus, isReservedJournalKey, isTerminalReservationStatus, isTransientDatabaseError, isTransientError, isUniqueViolation, isValidCreditAmount, isValidIdempotencyKey, multiplyAmount, normalizePaymentRef, numericToCents, planBalanceCap, planBalanceTarget, planHoldCommit, recordedExpiredBacking, releaseReservationWithJournal, reserveCreditsForOperation, sameAmount, settleExpiredHoldBacking, spendableBalance, storedAmountCeil, storedAmountFloor, storedMonthlyLimit, sumAmounts, terminalStatusOf, toCents };
|
package/dist/core/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { R as ReservationStatus, a as PortableReservation, P as PortableTransaction, b as CreditSource, J as JournalReferenceType } from '../types-
|
|
2
|
-
export { A as AIProviderType, B as BuiltinTier, g as CreditCheckResult, C as CreditOperationType, D as DeductCreditsResult, M as MonthlyResetResult, f as PortableJournalEntry, d as PortableUsageLog, c as PortableUserCredits, h as ResourceType, e as SubscriptionExpiryResult, S as SubscriptionTier, T as TierConfig, i as TransactionType, U as UsageHistoryEntry, j as UsageHistoryResponse, W as WithCreditsOptions, k as calculateAvailableCredits, t as toDate, l as toPortableTimestamp } from '../types-
|
|
3
|
-
import { T as TerminalReservationStatus, I as ICreditRepository } from '../types-
|
|
4
|
-
export { a as AddCreditsOutcome, C as CommitOutcome, E as ExpireOutcome, R as ReleaseOutcome,
|
|
1
|
+
import { R as ReservationStatus, a as PortableReservation, P as PortableTransaction, b as CreditSource, J as JournalReferenceType, E as ExpiredBackingFrontier } from '../types-C3sxDvNb.js';
|
|
2
|
+
export { A as AIProviderType, B as BuiltinTier, g as CreditCheckResult, C as CreditOperationType, D as DeductCreditsResult, M as MonthlyResetResult, f as PortableJournalEntry, d as PortableUsageLog, c as PortableUserCredits, h as ResourceType, e as SubscriptionExpiryResult, S as SubscriptionTier, T as TierConfig, i as TransactionType, U as UsageHistoryEntry, j as UsageHistoryResponse, W as WithCreditsOptions, k as calculateAvailableCredits, t as toDate, l as toPortableTimestamp } from '../types-C3sxDvNb.js';
|
|
3
|
+
import { T as TerminalReservationStatus, I as ICreditRepository } from '../types-CY-oHIMA.js';
|
|
4
|
+
export { a as AddCreditsOutcome, C as CommitOutcome, E as ExpireOutcome, R as ReleaseOutcome, o as ReserveOutcome, q as isCreditedOutcome, r as isReservedOutcome, s as isWinningOutcome } from '../types-CY-oHIMA.js';
|
|
5
5
|
export { D as DeferredExecutor, c as createDeferredExecutor, g as genericDeferred, n as noopDeferred, s as synchronousDeferred } from '../deferred-C7rrRfkm.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -393,6 +393,7 @@ declare function createPaymentRefConflictError(paymentRef: string, details: {
|
|
|
393
393
|
* These live in core rather than in an adapter so every repository — shipped
|
|
394
394
|
* or third-party — validates identically.
|
|
395
395
|
*/
|
|
396
|
+
|
|
396
397
|
/** `numeric(12, 2)`: 12 significant digits, 2 of them after the point. */
|
|
397
398
|
declare const CREDIT_AMOUNT_SCALE = 2;
|
|
398
399
|
/** Largest representable amount: ten integer digits plus two decimals. */
|
|
@@ -431,6 +432,40 @@ declare function toCents(value: number): number | null;
|
|
|
431
432
|
* derived result is not enough and never was.
|
|
432
433
|
*/
|
|
433
434
|
declare function sumAmounts(...values: number[]): number;
|
|
435
|
+
/**
|
|
436
|
+
* Multiply a price by a whole quantity, in cents.
|
|
437
|
+
*
|
|
438
|
+
* `1.1 * 3` is `3.3000000000000003`, which the amount validators refuse: the price
|
|
439
|
+
* was legal, and the float product blames the caller for a rounding artefact. A
|
|
440
|
+
* fractional quantity has no exact result on the grid and is refused rather than
|
|
441
|
+
* rounded, so the rounding policy stays with whoever prices fractions.
|
|
442
|
+
*/
|
|
443
|
+
declare function multiplyAmount(unit: number, quantity: number): number;
|
|
444
|
+
/**
|
|
445
|
+
* The cent-grid value a float landed next to, or the value unchanged.
|
|
446
|
+
*
|
|
447
|
+
* Documents written by older versions can hold the result of IEEE 754 additions
|
|
448
|
+
* (`FieldValue.increment` stored `0.30000000000000004`), and an application can pass
|
|
449
|
+
* a raw float difference to a ledger record. That is noise, not an amount. A value
|
|
450
|
+
* genuinely off the grid, such as `0.005`, is not noise and comes back as it went in.
|
|
451
|
+
*/
|
|
452
|
+
declare function canonicalAmount(value: number): number;
|
|
453
|
+
/**
|
|
454
|
+
* Read a stored amount the account owns as a cent-grid value, rounding down.
|
|
455
|
+
*
|
|
456
|
+
* A stored `24.999` makes every later sum off-grid, the validators then refuse the
|
|
457
|
+
* commit or release that needs it, and the holds on that account lock. Rounding down
|
|
458
|
+
* unlocks them. A value within {@link canonicalAmount}'s noise of the next cent reads as
|
|
459
|
+
* that cent, because a float cannot tell the two apart, so this can add at most that noise:
|
|
460
|
+
* 1e-6 up to 7e7, and about 1.4e-4 at the maximum. A non-finite value is left for the
|
|
461
|
+
* validators to refuse.
|
|
462
|
+
*/
|
|
463
|
+
declare function storedAmountFloor(value: number): number;
|
|
464
|
+
/**
|
|
465
|
+
* Read a stored amount the account owes as a cent-grid value, rounding up, so what
|
|
466
|
+
* stands against the balance is never understated. See {@link storedAmountFloor}.
|
|
467
|
+
*/
|
|
468
|
+
declare function storedAmountCeil(value: number): number;
|
|
434
469
|
/** Amounts that may be *moved*: strictly positive and on the cent grid. */
|
|
435
470
|
declare function isValidCreditAmount(value: unknown): value is number;
|
|
436
471
|
/**
|
|
@@ -479,6 +514,149 @@ declare function assertRepresentableFields(fields: Record<string, unknown>, cont
|
|
|
479
514
|
* fund commits, so they count toward the backing.
|
|
480
515
|
*/
|
|
481
516
|
declare function backedBalanceFloor(reserved: number, bonusCredits: number): number;
|
|
517
|
+
/**
|
|
518
|
+
* The balance left by a cap: lowered to `cap` at most, never raised, and never
|
|
519
|
+
* cut through the credits backing outstanding holds (see
|
|
520
|
+
* {@link backedBalanceFloor}). A balance already under `cap` is left alone,
|
|
521
|
+
* which is the point of computing this from the stored row inside a transaction
|
|
522
|
+
* rather than writing a value the caller read earlier.
|
|
523
|
+
*/
|
|
524
|
+
declare function cappedBalance(balance: number, cap: number, reserved: number, bonusCredits: number): number;
|
|
525
|
+
/** A balance a lowering write lands on, and how much of it only backs holds. */
|
|
526
|
+
interface PlannedBalance {
|
|
527
|
+
balance: number;
|
|
528
|
+
expiredHoldBacking: number;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* The expired credits actually held in a balance: `0 <= held <= balance`, whatever
|
|
532
|
+
* is stored. Every read of `expiredHoldBacking` goes through this. Trusting the stored
|
|
533
|
+
* value made a negative one mint credits: `balance - (-10)` read as spendable, and a
|
|
534
|
+
* close that "used" -10 of backing added 10 to the balance.
|
|
535
|
+
*/
|
|
536
|
+
declare function heldExpiredBacking(balance: number, stored?: number): number;
|
|
537
|
+
/** The part of a balance not kept only for holds. A missing field reads as 0. */
|
|
538
|
+
declare function spendableBalance(balance: number, expiredHoldBacking?: number): number;
|
|
539
|
+
/**
|
|
540
|
+
* An absolute balance write: `target`, floored at what backs outstanding holds.
|
|
541
|
+
* Whatever the floor kept above `target` is not the account's to spend.
|
|
542
|
+
*/
|
|
543
|
+
declare function planBalanceTarget(target: number, reserved: number, bonusCredits: number): PlannedBalance;
|
|
544
|
+
/**
|
|
545
|
+
* A cap on the spendable part of a balance. Like {@link cappedBalance} it never
|
|
546
|
+
* raises the balance and never cuts through what backs holds, and it records the
|
|
547
|
+
* part kept above the cap so a later release cannot hand it back. Credits already
|
|
548
|
+
* marked stay marked, so applying a cap twice changes nothing.
|
|
549
|
+
*/
|
|
550
|
+
declare function planBalanceCap(balance: number, cap: number, reserved: number, bonusCredits: number, expiredHoldBacking?: number): PlannedBalance;
|
|
551
|
+
/**
|
|
552
|
+
* What a closing hold takes out of the credits kept only for holds: its own share
|
|
553
|
+
* and nothing of another hold's. `holdBacking` comes from {@link holdExpiredBacking}.
|
|
554
|
+
*/
|
|
555
|
+
declare function settleExpiredHoldBacking(balance: number, expiredHoldBacking: number, holdBacking: number): {
|
|
556
|
+
used: number;
|
|
557
|
+
remaining: number;
|
|
558
|
+
};
|
|
559
|
+
/** The balances a commit lands on, and whether the account could pay it. */
|
|
560
|
+
interface PlannedHoldCommit {
|
|
561
|
+
covered: boolean;
|
|
562
|
+
balance: number;
|
|
563
|
+
bonusCredits: number;
|
|
564
|
+
expiredHoldBacking: number;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Pay a hold: first its own share of the marked credits, then spendable balance,
|
|
568
|
+
* then bonus. Marked credits backing other holds are never touched, so what this
|
|
569
|
+
* commit costs does not depend on which holds closed before it.
|
|
570
|
+
*/
|
|
571
|
+
declare function planHoldCommit(balance: number, bonusCredits: number, expiredHoldBacking: number, holdBacking: number, amount: number): PlannedHoldCommit;
|
|
572
|
+
/** The fields of an open hold that decide its share of the marked credits. */
|
|
573
|
+
type ExpiredBackingHold = Pick<PortableReservation, "id" | "amount" | "holdPlacedAt" | "expiredBacking">;
|
|
574
|
+
/** A hold whose stored share has to change for a new total. */
|
|
575
|
+
interface ExpiredBackingRewrite {
|
|
576
|
+
id: string;
|
|
577
|
+
expiredBacking: number;
|
|
578
|
+
}
|
|
579
|
+
/** The marked credits on an account and where they end among its open holds. */
|
|
580
|
+
interface ExpiredBackingAllocation {
|
|
581
|
+
total: number;
|
|
582
|
+
frontier: ExpiredBackingFrontier | null;
|
|
583
|
+
/** Stored shares to write with this total. Absent when none changes. */
|
|
584
|
+
rewrites?: ExpiredBackingRewrite[];
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* A frontier before every hold: no hold shares through it. It records that the account's
|
|
588
|
+
* marks are placed, so the next close does not walk the holds again.
|
|
589
|
+
*/
|
|
590
|
+
declare const NO_EXPIRED_BACKING_PREFIX: ExpiredBackingFrontier;
|
|
591
|
+
/**
|
|
592
|
+
* The share a 2.2.0 hold document recorded for itself: the order of record for that hold.
|
|
593
|
+
* Clocks are not, since the server that placed a hold may lag the one before it. Rounded
|
|
594
|
+
* up, like every amount that stands against the balance, and never above the hold. Zero
|
|
595
|
+
* means no record, which leaves the hold to the frontier.
|
|
596
|
+
*/
|
|
597
|
+
declare function recordedExpiredBacking(hold: ExpiredBackingHold): number;
|
|
598
|
+
/**
|
|
599
|
+
* Place `total` marked credits on the open holds, so that what each hold takes does not
|
|
600
|
+
* depend on the order they close in.
|
|
601
|
+
*
|
|
602
|
+
* Shares a 2.2.0 hold document stored stand as recorded: they need not be a prefix of
|
|
603
|
+
* anything, and they sit on documents that already exist. The rest goes to the other
|
|
604
|
+
* holds oldest first, each up to its amount. That part is a prefix, so one
|
|
605
|
+
* {@link ExpiredBackingFrontier} on the account describes it and no hold document changes
|
|
606
|
+
* however many there are. A new hold carries no share, so no record is ever added.
|
|
607
|
+
*
|
|
608
|
+
* A total below the records trims them newest first, and only when every other hold has
|
|
609
|
+
* nothing, and a total the other holds cannot take fills what the records left free. Those
|
|
610
|
+
* are the only hold writes: `rewrites`, at most one per record, each record being an open
|
|
611
|
+
* hold from before the upgrade.
|
|
612
|
+
*
|
|
613
|
+
* Marks no hold can carry stay marked. They were out of reach before this ran, and
|
|
614
|
+
* dropping them would make them spendable; the next absolute balance write replaces
|
|
615
|
+
* them. A row without `holdPlacedAt` is a record, not a hold, and a row with an unusable
|
|
616
|
+
* amount cannot close, so neither takes a share.
|
|
617
|
+
*/
|
|
618
|
+
declare function allocateExpiredBacking(holds: readonly ExpiredBackingHold[], total: number): ExpiredBackingAllocation;
|
|
619
|
+
/**
|
|
620
|
+
* This hold's share of the marked credits: its own record if it has one, otherwise where
|
|
621
|
+
* it falls against the account's frontier. Pass the `rewrites` of an allocation made in
|
|
622
|
+
* this same step, since the document does not hold them yet.
|
|
623
|
+
*/
|
|
624
|
+
declare function holdExpiredBacking(hold: ExpiredBackingHold, frontier?: ExpiredBackingFrontier | null, rewrites?: readonly ExpiredBackingRewrite[]): number;
|
|
625
|
+
/**
|
|
626
|
+
* When a new hold counts as placed: `now`, or just after the frontier if this server's
|
|
627
|
+
* clock is behind the server that placed it. A hold that sorted before the frontier
|
|
628
|
+
* would read as fully marked, taking expired credits that belong to older holds.
|
|
629
|
+
*/
|
|
630
|
+
declare function holdPlacedAtAfter(now: string, frontier?: ExpiredBackingFrontier | null): string;
|
|
631
|
+
/** The account fields that hold the marked credits. */
|
|
632
|
+
interface ExpiredBackingFields {
|
|
633
|
+
expiredHoldBacking?: number;
|
|
634
|
+
expiredBackingFrontier?: ExpiredBackingFrontier | null;
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* The fields to write for a new marked total: nothing when it was and stays 0, so an
|
|
638
|
+
* account that never had any keeps a document without the fields. A stored value that
|
|
639
|
+
* is not the new one is overwritten, which is what repairs a bad one. A `frontier` left
|
|
640
|
+
* undefined keeps the stored one.
|
|
641
|
+
*/
|
|
642
|
+
declare function expiredBackingPatch(next: {
|
|
643
|
+
total: number;
|
|
644
|
+
frontier?: ExpiredBackingFrontier | null;
|
|
645
|
+
}, stored: ExpiredBackingFields): ExpiredBackingFields;
|
|
646
|
+
/**
|
|
647
|
+
* Validate a balance target supplied by the application: a cap or the result of
|
|
648
|
+
* a monthly-reset decision. It must be storable and cannot be negative.
|
|
649
|
+
*/
|
|
650
|
+
declare function assertValidBalanceTarget(value: unknown, field: string, context?: Record<string, unknown>): asserts value is number;
|
|
651
|
+
/**
|
|
652
|
+
* Validate how a tier write changes the balance. `balance` sets a value the
|
|
653
|
+
* caller computed and `balanceCap` lowers the stored value, so naming both has
|
|
654
|
+
* no single meaning and is refused.
|
|
655
|
+
*/
|
|
656
|
+
declare function assertValidTierBalanceInput(input: {
|
|
657
|
+
balance?: number;
|
|
658
|
+
balanceCap?: number;
|
|
659
|
+
}, context?: Record<string, unknown>): void;
|
|
482
660
|
/**
|
|
483
661
|
* Validate a limit derived from tier configuration.
|
|
484
662
|
*
|
|
@@ -694,4 +872,4 @@ declare function releaseReservationWithJournal(repository: ICreditRepository, us
|
|
|
694
872
|
*/
|
|
695
873
|
declare function reserveCreditsForOperation(repository: ICreditRepository, userId: string, amount: number, operationType: string, expiryMs?: number, idempotencyKey?: string): Promise<PortableReservation>;
|
|
696
874
|
|
|
697
|
-
export { CREDIT_AMOUNT_MAX, CREDIT_AMOUNT_SCALE, CreditError, CreditErrorCode, type CreditErrorCodeType, CreditSource, JournalReferenceType, type PaymentEventPayload, PortableReservation, PortableTransaction, RESERVATION_STATUSES, RESERVED_JOURNAL_KEY_PREFIX, type ReservationIntegrityView, ReservationStatus, type StoredPaymentEvent, TERMINAL_RESERVATION_STATUSES, TerminalReservationStatus, assertDirectStatusWriteAllowed, assertHoldPlaced, assertKnownReservationStatus, assertPublicJournalKey, assertRepresentableAmount, assertRepresentableFields, assertRepresentableTierAmount, assertUnkeyedDirectReservation, assertUnreferencedDirectTransaction, assertValidCreditAmount, assertValidIdempotencyKey, assertValidStoredAmount, assertValidStoredAmountRaw, backedBalanceFloor, classifyDatabaseError, commitReservationWithJournal, createIdempotencyConflictError, createInsufficientCreditsError, createInvalidAmountError, createInvalidOperationTypeError, createPaymentRefConflictError, createReservationAlreadyProcessedError, createReservationExpiredError, createReservationNotFoundError, createTransientError, createUnsupportedOperationError, createUserNotFoundError, describePaymentMismatch, getSqlState, hasPlacedHold, isCreditError, isIdempotencyConflictError, isInsufficientCreditsError, isRepresentableAmount, isReservationStatus, isReservedJournalKey, isTerminalReservationStatus, isTransientDatabaseError, isTransientError, isUniqueViolation, isValidCreditAmount, isValidIdempotencyKey, normalizePaymentRef, numericToCents, releaseReservationWithJournal, reserveCreditsForOperation, sameAmount, storedMonthlyLimit, sumAmounts, terminalStatusOf, toCents };
|
|
875
|
+
export { CREDIT_AMOUNT_MAX, CREDIT_AMOUNT_SCALE, CreditError, CreditErrorCode, type CreditErrorCodeType, CreditSource, type ExpiredBackingAllocation, type ExpiredBackingFields, ExpiredBackingFrontier, type ExpiredBackingHold, type ExpiredBackingRewrite, JournalReferenceType, NO_EXPIRED_BACKING_PREFIX, type PaymentEventPayload, PortableReservation, PortableTransaction, RESERVATION_STATUSES, RESERVED_JOURNAL_KEY_PREFIX, type ReservationIntegrityView, ReservationStatus, type StoredPaymentEvent, TERMINAL_RESERVATION_STATUSES, TerminalReservationStatus, allocateExpiredBacking, assertDirectStatusWriteAllowed, assertHoldPlaced, assertKnownReservationStatus, assertPublicJournalKey, assertRepresentableAmount, assertRepresentableFields, assertRepresentableTierAmount, assertUnkeyedDirectReservation, assertUnreferencedDirectTransaction, assertValidBalanceTarget, assertValidCreditAmount, assertValidIdempotencyKey, assertValidStoredAmount, assertValidStoredAmountRaw, assertValidTierBalanceInput, backedBalanceFloor, canonicalAmount, cappedBalance, classifyDatabaseError, commitReservationWithJournal, createIdempotencyConflictError, createInsufficientCreditsError, createInvalidAmountError, createInvalidOperationTypeError, createPaymentRefConflictError, createReservationAlreadyProcessedError, createReservationExpiredError, createReservationNotFoundError, createTransientError, createUnsupportedOperationError, createUserNotFoundError, describePaymentMismatch, expiredBackingPatch, getSqlState, hasPlacedHold, heldExpiredBacking, holdExpiredBacking, holdPlacedAtAfter, isCreditError, isIdempotencyConflictError, isInsufficientCreditsError, isRepresentableAmount, isReservationStatus, isReservedJournalKey, isTerminalReservationStatus, isTransientDatabaseError, isTransientError, isUniqueViolation, isValidCreditAmount, isValidIdempotencyKey, multiplyAmount, normalizePaymentRef, numericToCents, planBalanceCap, planBalanceTarget, planHoldCommit, recordedExpiredBacking, releaseReservationWithJournal, reserveCreditsForOperation, sameAmount, settleExpiredHoldBacking, spendableBalance, storedAmountCeil, storedAmountFloor, storedMonthlyLimit, sumAmounts, terminalStatusOf, toCents };
|