@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.
- package/LICENSE +21 -0
- package/dist/adapters/index.cjs +2 -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 +2 -2
- package/dist/adapters/index.js.map +1 -1
- package/dist/core/index.cjs +102 -2
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +92 -5
- package/dist/core/index.d.ts +92 -5
- package/dist/core/index.js +92 -2
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +272 -39
- 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 +260 -39
- package/dist/index.js.map +1 -1
- package/dist/repository/index.cjs +247 -39
- package/dist/repository/index.cjs.map +1 -1
- package/dist/repository/index.d.cts +34 -6
- package/dist/repository/index.d.ts +34 -6
- package/dist/repository/index.js +244 -38
- package/dist/repository/index.js.map +1 -1
- 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.map +1 -1
- package/dist/service/index.cjs +8 -4
- 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 +8 -4
- package/dist/service/index.js.map +1 -1
- package/dist/{types-BhCdep_t.d.ts → types-3c-IAwZI.d.ts} +38 -3
- package/dist/{types-Dzc443vy.d.cts → types-MaViK5tD.d.cts} +18 -0
- package/dist/{types-Dzc443vy.d.ts → types-MaViK5tD.d.ts} +18 -0
- package/dist/{types-wtksMa4n.d.cts → types-XSjNXO5l.d.cts} +38 -3
- package/package.json +70 -71
package/dist/core/index.cjs
CHANGED
|
@@ -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().
|
|
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,
|