@nehorai/credits 3.0.0 → 3.1.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/dist/adapters/index.cjs +169 -156
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +169 -156
- package/dist/adapters/index.js.map +1 -1
- package/dist/core/index.cjs +9 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index-BrogG0Gc.d.cts +277 -0
- package/dist/index-DOvJES2m.d.ts +277 -0
- package/dist/index.cjs +78 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +76 -10
- package/dist/index.js.map +1 -1
- package/dist/repository/index.cjs +71 -8
- package/dist/repository/index.cjs.map +1 -1
- package/dist/repository/index.d.cts +3 -255
- package/dist/repository/index.d.ts +3 -255
- package/dist/repository/index.js +71 -8
- package/dist/repository/index.js.map +1 -1
- package/dist/service/index.cjs +17 -3
- package/dist/service/index.cjs.map +1 -1
- package/dist/service/index.d.cts +1 -1
- package/dist/service/index.d.ts +1 -1
- package/dist/service/index.js +17 -3
- package/dist/service/index.js.map +1 -1
- package/dist/{types-CY-oHIMA.d.ts → types-BL3YqxUD.d.ts} +9 -3
- package/dist/{types-DOjguhEX.d.cts → types-YLAB8bFk.d.cts} +9 -3
- package/package.json +1 -1
|
@@ -631,17 +631,25 @@ var DEFAULT_CONFIG = {
|
|
|
631
631
|
}
|
|
632
632
|
};
|
|
633
633
|
var currentConfig = DEFAULT_CONFIG;
|
|
634
|
+
function isOperationCostMap(value) {
|
|
635
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
636
|
+
return Object.values(value).every((cost) => isRepresentableAmount(cost) && cost >= 0);
|
|
637
|
+
}
|
|
634
638
|
function loadConfigFromEnv() {
|
|
635
639
|
const envConfig = {};
|
|
636
640
|
const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;
|
|
637
641
|
if (operationCostsEnv) {
|
|
638
642
|
try {
|
|
639
643
|
const parsed = JSON.parse(operationCostsEnv);
|
|
640
|
-
if (
|
|
644
|
+
if (isOperationCostMap(parsed)) {
|
|
641
645
|
envConfig.operationCosts = {
|
|
642
646
|
...DEFAULT_CONFIG.operationCosts,
|
|
643
647
|
...parsed
|
|
644
648
|
};
|
|
649
|
+
} else {
|
|
650
|
+
console.warn(
|
|
651
|
+
"[Credits Config] Ignoring CREDITS_OPERATION_COSTS: it must be a JSON object whose costs are numbers >= 0, exact to the cent"
|
|
652
|
+
);
|
|
645
653
|
}
|
|
646
654
|
} catch {
|
|
647
655
|
console.warn("[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON");
|
|
@@ -986,6 +994,50 @@ function assertValidBalanceUpdate(current, updates) {
|
|
|
986
994
|
throw new Error(`Invalid balance update: ${result.errors.join("; ")}`);
|
|
987
995
|
}
|
|
988
996
|
}
|
|
997
|
+
function signupGrantJournalInput(userId, tier, grant) {
|
|
998
|
+
if (grant <= 0) return null;
|
|
999
|
+
return {
|
|
1000
|
+
userId,
|
|
1001
|
+
entryType: "credit",
|
|
1002
|
+
amount: grant,
|
|
1003
|
+
balanceBefore: 0,
|
|
1004
|
+
balanceAfter: grant,
|
|
1005
|
+
source: "bonus",
|
|
1006
|
+
referenceId: `signup-${userId}`,
|
|
1007
|
+
referenceType: "adjustment",
|
|
1008
|
+
description: `Signup grant for ${tier} tier.`,
|
|
1009
|
+
metadata: { tier }
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
function tierChangeJournalInput(input) {
|
|
1013
|
+
const { userId, previousTier, newTier, previousBalance, newBalance, bonusCredits } = input;
|
|
1014
|
+
const change = sumAmounts(newBalance, -previousBalance);
|
|
1015
|
+
if (change === 0) return null;
|
|
1016
|
+
const raised = change > 0;
|
|
1017
|
+
const amount = raised ? newBalance : 0;
|
|
1018
|
+
const balanceBefore = sumAmounts(previousBalance, bonusCredits);
|
|
1019
|
+
const balanceAfter = sumAmounts(newBalance, bonusCredits);
|
|
1020
|
+
assertRepresentableFields(
|
|
1021
|
+
{
|
|
1022
|
+
"journal amount": amount,
|
|
1023
|
+
"journal balanceBefore": balanceBefore,
|
|
1024
|
+
"journal balanceAfter": balanceAfter
|
|
1025
|
+
},
|
|
1026
|
+
{ userId, operation: "updateUserTier" }
|
|
1027
|
+
);
|
|
1028
|
+
return {
|
|
1029
|
+
userId,
|
|
1030
|
+
entryType: raised ? "credit" : "debit",
|
|
1031
|
+
amount,
|
|
1032
|
+
balanceBefore,
|
|
1033
|
+
balanceAfter,
|
|
1034
|
+
source: raised ? "subscription_upgrade" : "subscription_downgrade",
|
|
1035
|
+
referenceId: `tier-${Date.now()}`,
|
|
1036
|
+
referenceType: "subscription",
|
|
1037
|
+
description: `Tier changed from ${previousTier} to ${newTier}.`,
|
|
1038
|
+
metadata: { previousTier, newTier, previousBalance, newBalance }
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
989
1041
|
|
|
990
1042
|
// src/repository/memory/snapshot.ts
|
|
991
1043
|
function copyRecord(record) {
|
|
@@ -1630,6 +1682,8 @@ var InMemoryCreditRepository = class {
|
|
|
1630
1682
|
updatedAt: now
|
|
1631
1683
|
};
|
|
1632
1684
|
this.store.users.set(userId, credits);
|
|
1685
|
+
const grantLine = signupGrantJournalInput(userId, tier, initialBalance);
|
|
1686
|
+
if (grantLine) this.recordJournalEntry(grantLine);
|
|
1633
1687
|
return credits;
|
|
1634
1688
|
}
|
|
1635
1689
|
async updateUserCredits(userId, updates) {
|
|
@@ -1710,21 +1764,29 @@ var InMemoryCreditRepository = class {
|
|
|
1710
1764
|
if (!credits) {
|
|
1711
1765
|
throw new Error(`User ${userId} not found`);
|
|
1712
1766
|
}
|
|
1713
|
-
|
|
1714
|
-
|
|
1767
|
+
let planned;
|
|
1768
|
+
if (input.balance !== void 0) {
|
|
1769
|
+
planned = planBalanceTarget(input.balance, credits.reserved, credits.bonusCredits);
|
|
1770
|
+
} else if (input.balanceCap !== void 0 && !isFreeTier(credits.tier)) {
|
|
1771
|
+
planned = planBalanceCap(
|
|
1715
1772
|
credits.balance,
|
|
1716
1773
|
input.balanceCap,
|
|
1717
1774
|
credits.reserved,
|
|
1718
1775
|
credits.bonusCredits,
|
|
1719
1776
|
credits.expiredHoldBacking
|
|
1720
1777
|
);
|
|
1721
|
-
credits.balance = capped.balance;
|
|
1722
|
-
writeExpiredHoldBacking(this.store, credits, capped.expiredHoldBacking);
|
|
1723
1778
|
}
|
|
1779
|
+
const journalLine = planned ? tierChangeJournalInput({
|
|
1780
|
+
userId,
|
|
1781
|
+
previousTier: credits.tier,
|
|
1782
|
+
newTier: input.tier,
|
|
1783
|
+
previousBalance: credits.balance,
|
|
1784
|
+
newBalance: planned.balance,
|
|
1785
|
+
bonusCredits: credits.bonusCredits
|
|
1786
|
+
}) : null;
|
|
1724
1787
|
credits.tier = input.tier;
|
|
1725
1788
|
credits.monthlyLimit = input.monthlyLimit;
|
|
1726
|
-
if (
|
|
1727
|
-
const planned = planBalanceTarget(input.balance, credits.reserved, credits.bonusCredits);
|
|
1789
|
+
if (planned) {
|
|
1728
1790
|
credits.balance = planned.balance;
|
|
1729
1791
|
writeExpiredHoldBacking(this.store, credits, planned.expiredHoldBacking);
|
|
1730
1792
|
}
|
|
@@ -1743,6 +1805,7 @@ var InMemoryCreditRepository = class {
|
|
|
1743
1805
|
}
|
|
1744
1806
|
credits.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1745
1807
|
this.store.users.set(userId, credits);
|
|
1808
|
+
if (journalLine) this.recordJournalEntry(journalLine);
|
|
1746
1809
|
}
|
|
1747
1810
|
// ==================== Reservations ====================
|
|
1748
1811
|
async createReservation(input) {
|
|
@@ -2364,7 +2427,7 @@ var InMemoryCreditRepository = class {
|
|
|
2364
2427
|
userId,
|
|
2365
2428
|
entryType: "debit",
|
|
2366
2429
|
amount: 0,
|
|
2367
|
-
//
|
|
2430
|
+
// 0, however far the cap lowered the balance: the totals record the move
|
|
2368
2431
|
balanceBefore: sumAmounts(previousBalance, credits.bonusCredits),
|
|
2369
2432
|
balanceAfter,
|
|
2370
2433
|
source: "subscription_downgrade",
|