@nehorai/credits 2.1.0 → 2.2.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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/index.cjs +2 -2
  3. package/dist/adapters/index.cjs.map +1 -1
  4. package/dist/adapters/index.d.cts +2 -2
  5. package/dist/adapters/index.d.ts +2 -2
  6. package/dist/adapters/index.js +2 -2
  7. package/dist/adapters/index.js.map +1 -1
  8. package/dist/core/index.cjs +102 -2
  9. package/dist/core/index.cjs.map +1 -1
  10. package/dist/core/index.d.cts +92 -5
  11. package/dist/core/index.d.ts +92 -5
  12. package/dist/core/index.js +92 -2
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/index.cjs +272 -39
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +6 -6
  17. package/dist/index.d.ts +6 -6
  18. package/dist/index.js +260 -39
  19. package/dist/index.js.map +1 -1
  20. package/dist/repository/index.cjs +247 -39
  21. package/dist/repository/index.cjs.map +1 -1
  22. package/dist/repository/index.d.cts +34 -6
  23. package/dist/repository/index.d.ts +34 -6
  24. package/dist/repository/index.js +244 -38
  25. package/dist/repository/index.js.map +1 -1
  26. package/dist/sdk/index.cjs.map +1 -1
  27. package/dist/sdk/index.d.cts +2 -2
  28. package/dist/sdk/index.d.ts +2 -2
  29. package/dist/sdk/index.js.map +1 -1
  30. package/dist/service/index.cjs +8 -4
  31. package/dist/service/index.cjs.map +1 -1
  32. package/dist/service/index.d.cts +2 -2
  33. package/dist/service/index.d.ts +2 -2
  34. package/dist/service/index.js +8 -4
  35. package/dist/service/index.js.map +1 -1
  36. package/dist/{types-BhCdep_t.d.ts → types-3c-IAwZI.d.ts} +38 -3
  37. package/dist/{types-Dzc443vy.d.cts → types-MaViK5tD.d.cts} +18 -0
  38. package/dist/{types-Dzc443vy.d.ts → types-MaViK5tD.d.ts} +18 -0
  39. package/dist/{types-wtksMa4n.d.cts → types-XSjNXO5l.d.cts} +38 -3
  40. package/package.json +70 -71
@@ -36,12 +36,16 @@ __export(core_exports, {
36
36
  assertRepresentableTierAmount: () => assertRepresentableTierAmount,
37
37
  assertUnkeyedDirectReservation: () => assertUnkeyedDirectReservation,
38
38
  assertUnreferencedDirectTransaction: () => assertUnreferencedDirectTransaction,
39
+ assertValidBalanceTarget: () => assertValidBalanceTarget,
39
40
  assertValidCreditAmount: () => assertValidCreditAmount,
40
41
  assertValidIdempotencyKey: () => assertValidIdempotencyKey,
41
42
  assertValidStoredAmount: () => assertValidStoredAmount,
42
43
  assertValidStoredAmountRaw: () => assertValidStoredAmountRaw,
44
+ assertValidTierBalanceInput: () => assertValidTierBalanceInput,
45
+ attributeExpiredBacking: () => attributeExpiredBacking,
43
46
  backedBalanceFloor: () => backedBalanceFloor,
44
47
  calculateAvailableCredits: () => calculateAvailableCredits,
48
+ cappedBalance: () => cappedBalance,
45
49
  classifyDatabaseError: () => classifyDatabaseError,
46
50
  commitReservationWithJournal: () => commitReservationWithJournal,
47
51
  createDeferredExecutor: () => createDeferredExecutor,
@@ -57,6 +61,7 @@ __export(core_exports, {
57
61
  createUnsupportedOperationError: () => createUnsupportedOperationError,
58
62
  createUserNotFoundError: () => createUserNotFoundError,
59
63
  describePaymentMismatch: () => describePaymentMismatch,
64
+ expiredHoldBackingPatch: () => expiredHoldBackingPatch,
60
65
  genericDeferred: () => genericDeferred,
61
66
  getSqlState: () => getSqlState,
62
67
  hasPlacedHold: () => hasPlacedHold,
@@ -78,9 +83,14 @@ __export(core_exports, {
78
83
  noopDeferred: () => noopDeferred,
79
84
  normalizePaymentRef: () => normalizePaymentRef,
80
85
  numericToCents: () => numericToCents,
86
+ planBalanceCap: () => planBalanceCap,
87
+ planBalanceTarget: () => planBalanceTarget,
88
+ planHoldCommit: () => planHoldCommit,
81
89
  releaseReservationWithJournal: () => releaseReservationWithJournal,
82
90
  reserveCreditsForOperation: () => reserveCreditsForOperation,
83
91
  sameAmount: () => sameAmount,
92
+ settleExpiredHoldBacking: () => settleExpiredHoldBacking,
93
+ spendableBalance: () => spendableBalance,
84
94
  storedMonthlyLimit: () => storedMonthlyLimit,
85
95
  sumAmounts: () => sumAmounts,
86
96
  synchronousDeferred: () => synchronousDeferred,
@@ -312,6 +322,86 @@ function backedBalanceFloor(reserved, bonusCredits) {
312
322
  assertRepresentableAmount(floor, "backedBalanceFloor", { reserved, bonusCredits });
313
323
  return floor;
314
324
  }
325
+ function cappedBalance(balance, cap, reserved, bonusCredits) {
326
+ return Math.min(balance, Math.max(cap, backedBalanceFloor(reserved, bonusCredits)));
327
+ }
328
+ function spendableBalance(balance, expiredHoldBacking = 0) {
329
+ return sumAmounts(balance, -Math.min(expiredHoldBacking, balance));
330
+ }
331
+ function planBalanceTarget(target, reserved, bonusCredits) {
332
+ const balance = Math.max(target, backedBalanceFloor(reserved, bonusCredits));
333
+ return { balance, expiredHoldBacking: sumAmounts(balance, -target) };
334
+ }
335
+ function planBalanceCap(balance, cap, reserved, bonusCredits, expiredHoldBacking = 0) {
336
+ const spendable = spendableBalance(balance, expiredHoldBacking);
337
+ const target = Math.min(spendable, cap);
338
+ const kept = Math.max(
339
+ sumAmounts(balance, -spendable),
340
+ sumAmounts(backedBalanceFloor(reserved, bonusCredits), -target)
341
+ );
342
+ const next = Math.min(balance, sumAmounts(target, kept));
343
+ return { balance: next, expiredHoldBacking: Math.max(0, sumAmounts(next, -target)) };
344
+ }
345
+ function settleExpiredHoldBacking(balance, expiredHoldBacking, holdBacking) {
346
+ const held = Math.min(expiredHoldBacking, balance);
347
+ const used = Math.min(held, holdBacking);
348
+ return { used, remaining: sumAmounts(held, -used) };
349
+ }
350
+ function planHoldCommit(balance, bonusCredits, expiredHoldBacking, holdBacking, amount) {
351
+ const backing = settleExpiredHoldBacking(balance, expiredHoldBacking, holdBacking);
352
+ const rest = sumAmounts(amount, -backing.used);
353
+ const fromSpendable = Math.min(spendableBalance(balance, expiredHoldBacking), rest);
354
+ const fromBonus = sumAmounts(rest, -fromSpendable);
355
+ return {
356
+ covered: fromBonus <= bonusCredits,
357
+ balance: sumAmounts(balance, -backing.used, -fromSpendable),
358
+ bonusCredits: sumAmounts(bonusCredits, -fromBonus),
359
+ expiredHoldBacking: backing.remaining
360
+ };
361
+ }
362
+ function attributeExpiredBacking(holds, total) {
363
+ const placed = holds.filter((hold) => hold.holdPlacedAt).sort(
364
+ (a, b) => a.holdPlacedAt === b.holdPlacedAt ? a.id < b.id ? -1 : 1 : a.holdPlacedAt < b.holdPlacedAt ? -1 : 1
365
+ );
366
+ const shares = /* @__PURE__ */ new Map();
367
+ let left = total;
368
+ for (const hold of placed) {
369
+ const kept = Math.min(hold.expiredBacking ?? 0, hold.amount, left);
370
+ shares.set(hold.id, kept);
371
+ left = sumAmounts(left, -kept);
372
+ }
373
+ for (const hold of placed) {
374
+ const share = shares.get(hold.id);
375
+ const added = Math.min(sumAmounts(hold.amount, -share), left);
376
+ shares.set(hold.id, sumAmounts(share, added));
377
+ left = sumAmounts(left, -added);
378
+ }
379
+ return shares;
380
+ }
381
+ function expiredHoldBackingPatch(next, stored = 0) {
382
+ return next > 0 || stored > 0 ? { expiredHoldBacking: next } : {};
383
+ }
384
+ function assertValidBalanceTarget(value, field, context) {
385
+ assertRepresentableAmount(value, field, context);
386
+ if (value < 0) {
387
+ throw new CreditError(
388
+ `${field} cannot be negative (got ${value})`,
389
+ CreditErrorCode.INVALID_AMOUNT,
390
+ { field, value, ...context }
391
+ );
392
+ }
393
+ }
394
+ function assertValidTierBalanceInput(input, context) {
395
+ if (input.balanceCap === void 0) return;
396
+ if (input.balance !== void 0) {
397
+ throw new CreditError(
398
+ "A tier update cannot set both balance and balanceCap",
399
+ CreditErrorCode.INVALID_AMOUNT,
400
+ { balance: input.balance, balanceCap: input.balanceCap, ...context }
401
+ );
402
+ }
403
+ assertValidBalanceTarget(input.balanceCap, "balanceCap", context);
404
+ }
315
405
  function assertRepresentableTierAmount(value, field, context) {
316
406
  if (value === Number.POSITIVE_INFINITY) return;
317
407
  assertRepresentableAmount(value, field, context);
@@ -714,9 +804,9 @@ var creditSystemConfigSchema = import_zod.z.object({
714
804
  */
715
805
  reservationExpiryMs: import_zod.z.number().positive().default(5 * 60 * 1e3),
716
806
  /**
717
- * Default credits for new users (free tier)
807
+ * Default credits for new users (free tier). 0 means no signup grant.
718
808
  */
719
- defaultFreeCredits: import_zod.z.number().positive().default(25),
809
+ defaultFreeCredits: import_zod.z.number().min(0).default(25),
720
810
  /**
721
811
  * Grace period in days after subscription expires before downgrade
722
812
  */
@@ -1100,12 +1190,16 @@ async function reserveCreditsForOperation(repository, userId, amount, operationT
1100
1190
  assertRepresentableTierAmount,
1101
1191
  assertUnkeyedDirectReservation,
1102
1192
  assertUnreferencedDirectTransaction,
1193
+ assertValidBalanceTarget,
1103
1194
  assertValidCreditAmount,
1104
1195
  assertValidIdempotencyKey,
1105
1196
  assertValidStoredAmount,
1106
1197
  assertValidStoredAmountRaw,
1198
+ assertValidTierBalanceInput,
1199
+ attributeExpiredBacking,
1107
1200
  backedBalanceFloor,
1108
1201
  calculateAvailableCredits,
1202
+ cappedBalance,
1109
1203
  classifyDatabaseError,
1110
1204
  commitReservationWithJournal,
1111
1205
  createDeferredExecutor,
@@ -1121,6 +1215,7 @@ async function reserveCreditsForOperation(repository, userId, amount, operationT
1121
1215
  createUnsupportedOperationError,
1122
1216
  createUserNotFoundError,
1123
1217
  describePaymentMismatch,
1218
+ expiredHoldBackingPatch,
1124
1219
  genericDeferred,
1125
1220
  getSqlState,
1126
1221
  hasPlacedHold,
@@ -1142,9 +1237,14 @@ async function reserveCreditsForOperation(repository, userId, amount, operationT
1142
1237
  noopDeferred,
1143
1238
  normalizePaymentRef,
1144
1239
  numericToCents,
1240
+ planBalanceCap,
1241
+ planBalanceTarget,
1242
+ planHoldCommit,
1145
1243
  releaseReservationWithJournal,
1146
1244
  reserveCreditsForOperation,
1147
1245
  sameAmount,
1246
+ settleExpiredHoldBacking,
1247
+ spendableBalance,
1148
1248
  storedMonthlyLimit,
1149
1249
  sumAmounts,
1150
1250
  synchronousDeferred,