@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.
@@ -186,6 +186,11 @@ function creditAmountReason(value) {
186
186
  }
187
187
  return `Credit amount ${value} is not exact to ${CREDIT_AMOUNT_SCALE} decimal places`;
188
188
  }
189
+ function isRepresentableAmount(value) {
190
+ if (typeof value !== "number") return false;
191
+ const cents = toCents(value);
192
+ return cents !== null && Math.abs(cents) <= MAX_CENTS;
193
+ }
189
194
  function storedMonthlyLimit(limit) {
190
195
  return Number.isFinite(limit) ? limit : CREDIT_AMOUNT_MAX;
191
196
  }
@@ -363,17 +368,25 @@ var DEFAULT_CONFIG = {
363
368
  }
364
369
  };
365
370
  var currentConfig = DEFAULT_CONFIG;
371
+ function isOperationCostMap(value) {
372
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
373
+ return Object.values(value).every((cost) => isRepresentableAmount(cost) && cost >= 0);
374
+ }
366
375
  function loadConfigFromEnv() {
367
376
  const envConfig = {};
368
377
  const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;
369
378
  if (operationCostsEnv) {
370
379
  try {
371
380
  const parsed = JSON.parse(operationCostsEnv);
372
- if (typeof parsed === "object" && parsed !== null) {
381
+ if (isOperationCostMap(parsed)) {
373
382
  envConfig.operationCosts = {
374
383
  ...DEFAULT_CONFIG.operationCosts,
375
384
  ...parsed
376
385
  };
386
+ } else {
387
+ console.warn(
388
+ "[Credits Config] Ignoring CREDITS_OPERATION_COSTS: it must be a JSON object whose costs are numbers >= 0, exact to the cent"
389
+ );
377
390
  }
378
391
  } catch {
379
392
  console.warn("[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON");
@@ -724,7 +737,7 @@ var CreditsService = class {
724
737
  userId,
725
738
  entryType: "debit",
726
739
  amount: 0,
727
- // No credits deducted, just tier change
740
+ // 0, however far the cap lowered the balance: the totals record the move
728
741
  balanceBefore: sumAmounts(data.balance, data.bonusCredits),
729
742
  // The ledger balance, matching every other journal writer: the V2
730
743
  // transitions and add/deduct all record `balance + bonusCredits`, so
@@ -1116,7 +1129,8 @@ var CreditsService = class {
1116
1129
  ]);
1117
1130
  const historyEntries = entries.map((entry) => {
1118
1131
  const type = this.mapSourceToHistoryType(entry.source);
1119
- const creditsChange = entry.entryType === "credit" ? entry.amount : -entry.amount;
1132
+ const signedAmount = entry.entryType === "credit" ? entry.amount : -entry.amount;
1133
+ const creditsChange = entry.balanceBefore !== void 0 ? sumAmounts(entry.balanceAfter, -entry.balanceBefore) : signedAmount;
1120
1134
  return {
1121
1135
  id: entry.id,
1122
1136
  type,