@nehorai/credits 1.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/generic.d.ts +18 -0
- package/dist/adapters/generic.d.ts.map +1 -0
- package/dist/adapters/generic.js +147 -0
- package/dist/adapters/generic.js.map +1 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/types.d.ts +45 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +8 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/auth/api-key.d.ts +37 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +94 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/types.d.ts +31 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/config/costs.d.ts +61 -0
- package/dist/config/costs.d.ts.map +1 -0
- package/dist/config/costs.js +86 -0
- package/dist/config/costs.js.map +1 -0
- package/dist/config/index.d.ts +165 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +286 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/deferred.d.ts +65 -0
- package/dist/core/deferred.d.ts.map +1 -0
- package/dist/core/deferred.js +72 -0
- package/dist/core/deferred.js.map +1 -0
- package/dist/core/errors.d.ts +78 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +110 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +29 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +28 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/operations.d.ts +36 -0
- package/dist/core/operations.d.ts.map +1 -0
- package/dist/core/operations.js +87 -0
- package/dist/core/operations.js.map +1 -0
- package/dist/core/types.d.ts +287 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +93 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/notifications/index.d.ts +76 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +182 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/repository/index.d.ts +10 -0
- package/dist/repository/index.d.ts.map +1 -0
- package/dist/repository/index.js +11 -0
- package/dist/repository/index.js.map +1 -0
- package/dist/repository/memory/index.d.ts +70 -0
- package/dist/repository/memory/index.d.ts.map +1 -0
- package/dist/repository/memory/index.js +518 -0
- package/dist/repository/memory/index.js.map +1 -0
- package/dist/repository/types.d.ts +283 -0
- package/dist/repository/types.d.ts.map +1 -0
- package/dist/repository/types.js +39 -0
- package/dist/repository/types.js.map +1 -0
- package/dist/repository/utils.d.ts +22 -0
- package/dist/repository/utils.d.ts.map +1 -0
- package/dist/repository/utils.js +40 -0
- package/dist/repository/utils.js.map +1 -0
- package/dist/sdk/admin-client.d.ts +146 -0
- package/dist/sdk/admin-client.d.ts.map +1 -0
- package/dist/sdk/admin-client.js +196 -0
- package/dist/sdk/admin-client.js.map +1 -0
- package/dist/sdk/client.d.ts +144 -0
- package/dist/sdk/client.d.ts.map +1 -0
- package/dist/sdk/client.js +247 -0
- package/dist/sdk/client.js.map +1 -0
- package/dist/sdk/errors.d.ts +91 -0
- package/dist/sdk/errors.d.ts.map +1 -0
- package/dist/sdk/errors.js +185 -0
- package/dist/sdk/errors.js.map +1 -0
- package/dist/sdk/index.d.ts +13 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +13 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/sdk/types.d.ts +110 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +20 -0
- package/dist/sdk/types.js.map +1 -0
- package/dist/service/credits-service.d.ts +139 -0
- package/dist/service/credits-service.d.ts.map +1 -0
- package/dist/service/credits-service.js +372 -0
- package/dist/service/credits-service.js.map +1 -0
- package/dist/service/index.d.ts +3 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +2 -0
- package/dist/service/index.js.map +1 -0
- package/dist/utils/error-utils.d.ts +48 -0
- package/dist/utils/error-utils.d.ts.map +1 -0
- package/dist/utils/error-utils.js +57 -0
- package/dist/utils/error-utils.js.map +1 -0
- package/dist/utils/index.d.ts +54 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { toDate } from "../core/types";
|
|
2
|
+
import { toClientUserCredits } from "../repository/types";
|
|
3
|
+
import { DEFAULT_FREE_CREDITS, RESERVATION_EXPIRY_MS, getMonthlyLimit } from "../config/costs";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a date is past the monthly reset date
|
|
6
|
+
*/
|
|
7
|
+
function isPastMonthlyReset(resetAt) {
|
|
8
|
+
if (!resetAt)
|
|
9
|
+
return false;
|
|
10
|
+
const resetDate = toDate(resetAt);
|
|
11
|
+
return new Date() >= resetDate;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Default grace period for subscription expiry (in days)
|
|
15
|
+
*/
|
|
16
|
+
const DEFAULT_GRACE_PERIOD_DAYS = 3;
|
|
17
|
+
/**
|
|
18
|
+
* Credits service with dependency injection for repository
|
|
19
|
+
*
|
|
20
|
+
* Provides business logic for credit operations, delegating
|
|
21
|
+
* database operations to the injected repository.
|
|
22
|
+
*/
|
|
23
|
+
export class CreditsService {
|
|
24
|
+
repository;
|
|
25
|
+
lowBalanceCallback;
|
|
26
|
+
subscriptionExpiredCallback;
|
|
27
|
+
constructor(repository) {
|
|
28
|
+
this.repository = repository;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Set callback for low balance notifications
|
|
32
|
+
*/
|
|
33
|
+
setLowBalanceCallback(callback) {
|
|
34
|
+
this.lowBalanceCallback = callback;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Set callback for subscription expired notifications
|
|
38
|
+
*/
|
|
39
|
+
setSubscriptionExpiredCallback(callback) {
|
|
40
|
+
this.subscriptionExpiredCallback = callback;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Get user credits, performing monthly reset and subscription expiry checks if needed
|
|
44
|
+
*
|
|
45
|
+
* This method uses atomic operations to prevent race conditions:
|
|
46
|
+
* 1. Checks subscription expiry with grace period
|
|
47
|
+
* 2. Atomically performs monthly reset if needed (with optimistic locking)
|
|
48
|
+
*
|
|
49
|
+
* @param userId - User ID
|
|
50
|
+
* @returns User credits or null if not found
|
|
51
|
+
*/
|
|
52
|
+
async getUserCredits(userId) {
|
|
53
|
+
let data = await this.repository.getUserCredits(userId);
|
|
54
|
+
if (!data) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
// Step 1: Check subscription expiry (for non-free tiers)
|
|
58
|
+
if (data.tier !== "free" && data.subscriptionExpiresAt) {
|
|
59
|
+
const expiryResult = await this.repository.checkAndHandleSubscriptionExpiry(userId, DEFAULT_GRACE_PERIOD_DAYS);
|
|
60
|
+
if (expiryResult.wasDowngraded) {
|
|
61
|
+
// Create journal entry for downgrade
|
|
62
|
+
await this.repository.createJournalEntry({
|
|
63
|
+
userId,
|
|
64
|
+
entryType: "debit",
|
|
65
|
+
amount: 0, // No credits deducted, just tier change
|
|
66
|
+
balanceAfter: expiryResult.credits.balance,
|
|
67
|
+
source: "subscription_downgrade",
|
|
68
|
+
referenceId: `downgrade-${Date.now()}`,
|
|
69
|
+
referenceType: "subscription",
|
|
70
|
+
description: `Subscription expired. Downgraded from ${data.tier} to free tier.`,
|
|
71
|
+
metadata: {
|
|
72
|
+
previousTier: data.tier,
|
|
73
|
+
previousBalance: data.balance,
|
|
74
|
+
newBalance: expiryResult.credits.balance,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
// Trigger subscription expired notification (non-blocking)
|
|
78
|
+
if (this.subscriptionExpiredCallback) {
|
|
79
|
+
this.subscriptionExpiredCallback(userId, true).catch((error) => {
|
|
80
|
+
console.error("[Credits] Failed to send subscription expired notification:", error);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Use the potentially updated credits
|
|
85
|
+
data = expiryResult.credits;
|
|
86
|
+
}
|
|
87
|
+
// Step 2: Check if monthly reset is needed (use atomic operation)
|
|
88
|
+
if (isPastMonthlyReset(data.monthlyResetAt)) {
|
|
89
|
+
// Convert monthlyResetAt to a compatible type (Date or string)
|
|
90
|
+
const expectedResetAt = toDate(data.monthlyResetAt);
|
|
91
|
+
const resetResult = await this.repository.atomicMonthlyReset(userId, data.tier, expectedResetAt);
|
|
92
|
+
if (resetResult.wasReset) {
|
|
93
|
+
// Create journal entry for monthly reset
|
|
94
|
+
const balanceChange = resetResult.credits.balance - data.balance;
|
|
95
|
+
if (balanceChange !== 0) {
|
|
96
|
+
await this.repository.createJournalEntry({
|
|
97
|
+
userId,
|
|
98
|
+
entryType: balanceChange > 0 ? "credit" : "debit",
|
|
99
|
+
amount: Math.abs(balanceChange),
|
|
100
|
+
balanceAfter: resetResult.credits.balance,
|
|
101
|
+
source: "monthly_reset",
|
|
102
|
+
referenceId: `reset-${Date.now()}`,
|
|
103
|
+
referenceType: "reset",
|
|
104
|
+
description: `Monthly credit reset for ${data.tier} tier.`,
|
|
105
|
+
metadata: {
|
|
106
|
+
tier: data.tier,
|
|
107
|
+
previousBalance: data.balance,
|
|
108
|
+
newBalance: resetResult.credits.balance,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Use the potentially updated credits
|
|
114
|
+
data = resetResult.credits;
|
|
115
|
+
}
|
|
116
|
+
return toClientUserCredits(data);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Initialize credits for a new user with free tier
|
|
120
|
+
* @param userId - User ID
|
|
121
|
+
* @returns Initialized user credits
|
|
122
|
+
*/
|
|
123
|
+
async initializeUserCredits(userId) {
|
|
124
|
+
const credits = await this.repository.initializeUserCredits(userId, "free", DEFAULT_FREE_CREDITS);
|
|
125
|
+
return toClientUserCredits(credits);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get or create user credits
|
|
129
|
+
* Initializes with free tier if not exists
|
|
130
|
+
* @param userId - User ID
|
|
131
|
+
* @returns User credits
|
|
132
|
+
*/
|
|
133
|
+
async getOrCreateUserCredits(userId) {
|
|
134
|
+
const existing = await this.getUserCredits(userId);
|
|
135
|
+
if (existing) {
|
|
136
|
+
return existing;
|
|
137
|
+
}
|
|
138
|
+
return this.initializeUserCredits(userId);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if user has sufficient credits for an operation
|
|
142
|
+
* @param userId - User ID
|
|
143
|
+
* @param requiredCredits - Credits required
|
|
144
|
+
* @returns Credit check result
|
|
145
|
+
*/
|
|
146
|
+
async checkCredits(userId, requiredCredits) {
|
|
147
|
+
const credits = await this.getOrCreateUserCredits(userId);
|
|
148
|
+
// Available = balance + bonusCredits - reserved
|
|
149
|
+
const totalBalance = credits.balance + credits.bonusCredits;
|
|
150
|
+
const available = totalBalance - credits.reserved;
|
|
151
|
+
const hasCredits = available >= requiredCredits;
|
|
152
|
+
return {
|
|
153
|
+
hasCredits,
|
|
154
|
+
balance: totalBalance,
|
|
155
|
+
required: requiredCredits,
|
|
156
|
+
shortfall: hasCredits ? 0 : requiredCredits - available,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Reserve credits for an operation (phase 1 of two-phase commit)
|
|
161
|
+
* Creates a reservation and locks the credits
|
|
162
|
+
* @param userId - User ID
|
|
163
|
+
* @param amount - Credits to reserve
|
|
164
|
+
* @param operationType - Operation type for tracking
|
|
165
|
+
* @returns Reservation object
|
|
166
|
+
* @throws Error if insufficient credits
|
|
167
|
+
*/
|
|
168
|
+
async reserveCredits(userId, amount, operationType) {
|
|
169
|
+
const expiresAt = new Date(Date.now() + RESERVATION_EXPIRY_MS);
|
|
170
|
+
return this.repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Commit a reservation (phase 2 of two-phase commit - success)
|
|
174
|
+
* Deducts credits and marks reservation as committed
|
|
175
|
+
* Also triggers low balance notifications if balance drops below threshold
|
|
176
|
+
*/
|
|
177
|
+
async commitCredits(userId, reservationId) {
|
|
178
|
+
// Get the reservation to know the amount
|
|
179
|
+
const reservation = await this.repository.getReservation(userId, reservationId);
|
|
180
|
+
if (!reservation) {
|
|
181
|
+
throw new Error(`Reservation ${reservationId} not found`);
|
|
182
|
+
}
|
|
183
|
+
// Commit the reservation atomically
|
|
184
|
+
await this.repository.commitReservationAtomic(userId, reservationId);
|
|
185
|
+
// Create journal entry
|
|
186
|
+
const credits = await this.repository.getUserCredits(userId);
|
|
187
|
+
if (credits) {
|
|
188
|
+
await this.repository.createJournalEntry({
|
|
189
|
+
userId,
|
|
190
|
+
entryType: "debit",
|
|
191
|
+
amount: reservation.amount,
|
|
192
|
+
balanceAfter: credits.balance,
|
|
193
|
+
source: "operation_commit",
|
|
194
|
+
referenceId: reservationId,
|
|
195
|
+
referenceType: "reservation",
|
|
196
|
+
description: `Committed ${reservation.amount} credits for ${reservation.operationType}`,
|
|
197
|
+
metadata: {
|
|
198
|
+
operationType: reservation.operationType,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
// Trigger low balance notification (non-blocking)
|
|
202
|
+
if (this.lowBalanceCallback) {
|
|
203
|
+
this.lowBalanceCallback(userId, credits.balance).catch((error) => {
|
|
204
|
+
console.error("[Credits] Failed to send low balance notification:", error);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Release a reservation (phase 2 of two-phase commit - failure)
|
|
211
|
+
* Returns reserved credits and marks reservation as released
|
|
212
|
+
*/
|
|
213
|
+
async releaseCredits(userId, reservationId) {
|
|
214
|
+
// Get the reservation to check its state
|
|
215
|
+
const reservation = await this.repository.getReservation(userId, reservationId);
|
|
216
|
+
// Release the reservation atomically
|
|
217
|
+
await this.repository.releaseReservationAtomic(userId, reservationId);
|
|
218
|
+
// Create journal entry only if reservation was in reserved state
|
|
219
|
+
if (reservation?.status === "reserved") {
|
|
220
|
+
const credits = await this.repository.getUserCredits(userId);
|
|
221
|
+
if (credits) {
|
|
222
|
+
await this.repository.createJournalEntry({
|
|
223
|
+
userId,
|
|
224
|
+
entryType: "credit",
|
|
225
|
+
amount: 0, // No actual credits returned (they were reserved, not spent)
|
|
226
|
+
balanceAfter: credits.balance,
|
|
227
|
+
source: "operation_release",
|
|
228
|
+
referenceId: reservationId,
|
|
229
|
+
referenceType: "reservation",
|
|
230
|
+
description: `Released ${reservation.amount} reserved credits for ${reservation.operationType}`,
|
|
231
|
+
metadata: {
|
|
232
|
+
operationType: reservation.operationType,
|
|
233
|
+
amount: reservation.amount,
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Log usage for audit trail
|
|
241
|
+
* @param log - Usage log data
|
|
242
|
+
*/
|
|
243
|
+
async logUsage(log) {
|
|
244
|
+
await this.repository.logUsage(log);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Add credits to user account (for purchases, bonuses, etc.)
|
|
248
|
+
* @param userId - User ID
|
|
249
|
+
* @param amount - Credits to add
|
|
250
|
+
* @param description - Transaction description
|
|
251
|
+
* @param paymentRef - Optional payment reference
|
|
252
|
+
*/
|
|
253
|
+
async addCredits(userId, amount, description, paymentRef) {
|
|
254
|
+
return this.repository.addCreditsAtomic(userId, amount, description, paymentRef);
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Update user subscription tier
|
|
258
|
+
* @param userId - User ID
|
|
259
|
+
* @param tier - New subscription tier
|
|
260
|
+
* @param expiresAt - Subscription expiry date (optional)
|
|
261
|
+
*/
|
|
262
|
+
async updateTier(userId, tier, expiresAt) {
|
|
263
|
+
const monthlyLimit = getMonthlyLimit(tier);
|
|
264
|
+
await this.repository.updateUserTier(userId, {
|
|
265
|
+
tier,
|
|
266
|
+
monthlyLimit: monthlyLimit === Infinity ? 0 : monthlyLimit,
|
|
267
|
+
// Reset balance to new tier limit if upgrading
|
|
268
|
+
balance: tier !== "free"
|
|
269
|
+
? (monthlyLimit === Infinity ? 999999 : monthlyLimit)
|
|
270
|
+
: undefined,
|
|
271
|
+
monthlyUsed: tier !== "free" ? 0 : undefined,
|
|
272
|
+
subscriptionExpiresAt: expiresAt ? expiresAt.toISOString() : null,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get usage logs with optional filtering
|
|
277
|
+
* @param userId - Optional user ID filter
|
|
278
|
+
* @param limit - Max results
|
|
279
|
+
* @param offset - Skip results
|
|
280
|
+
* @returns List of usage logs
|
|
281
|
+
*/
|
|
282
|
+
async getUsageLogs(userId, limit = 50, offset = 0) {
|
|
283
|
+
return this.repository.getUsageLogs({ userId, limit, offset });
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get user-friendly usage history
|
|
287
|
+
* Combines journal entries into a user-facing format
|
|
288
|
+
*
|
|
289
|
+
* @param userId - User ID
|
|
290
|
+
* @param limit - Max results per page
|
|
291
|
+
* @param offset - Skip results for pagination
|
|
292
|
+
* @returns Paginated usage history response
|
|
293
|
+
*/
|
|
294
|
+
async getUsageHistory(userId, limit = 20, offset = 0) {
|
|
295
|
+
// Get journal entries
|
|
296
|
+
const [entries, total] = await Promise.all([
|
|
297
|
+
this.repository.getJournalEntries({ userId, limit, offset }),
|
|
298
|
+
this.repository.getJournalEntriesCount({ userId }),
|
|
299
|
+
]);
|
|
300
|
+
// Convert journal entries to user-friendly format
|
|
301
|
+
const historyEntries = entries.map((entry) => {
|
|
302
|
+
const type = this.mapSourceToHistoryType(entry.source);
|
|
303
|
+
const creditsChange = entry.entryType === "credit" ? entry.amount : -entry.amount;
|
|
304
|
+
return {
|
|
305
|
+
id: entry.id,
|
|
306
|
+
type,
|
|
307
|
+
creditsChange,
|
|
308
|
+
balanceAfter: entry.balanceAfter,
|
|
309
|
+
description: entry.description,
|
|
310
|
+
createdAt: typeof entry.createdAt === "string"
|
|
311
|
+
? entry.createdAt
|
|
312
|
+
: toDate(entry.createdAt).toISOString(),
|
|
313
|
+
};
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
entries: historyEntries,
|
|
317
|
+
pagination: {
|
|
318
|
+
total,
|
|
319
|
+
limit,
|
|
320
|
+
offset,
|
|
321
|
+
hasMore: offset + entries.length < total,
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Map journal entry source to user-friendly history type
|
|
327
|
+
*/
|
|
328
|
+
mapSourceToHistoryType(source) {
|
|
329
|
+
switch (source) {
|
|
330
|
+
case "operation_commit":
|
|
331
|
+
case "reservation_expired":
|
|
332
|
+
return "usage";
|
|
333
|
+
case "purchase":
|
|
334
|
+
return "purchase";
|
|
335
|
+
case "subscription_grant":
|
|
336
|
+
case "subscription_upgrade":
|
|
337
|
+
case "bonus":
|
|
338
|
+
return "bonus";
|
|
339
|
+
case "monthly_reset":
|
|
340
|
+
return "reset";
|
|
341
|
+
case "refund":
|
|
342
|
+
case "operation_release":
|
|
343
|
+
return "refund";
|
|
344
|
+
case "admin_adjustment":
|
|
345
|
+
case "subscription_downgrade":
|
|
346
|
+
case "expiry":
|
|
347
|
+
default:
|
|
348
|
+
return "adjustment";
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Get journal entries directly (for admin or debugging)
|
|
353
|
+
* @param query - Journal entry query parameters
|
|
354
|
+
* @returns List of journal entries
|
|
355
|
+
*/
|
|
356
|
+
async getJournalEntries(query) {
|
|
357
|
+
return this.repository.getJournalEntries(query);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get the underlying repository (for advanced use cases)
|
|
361
|
+
*/
|
|
362
|
+
getRepository() {
|
|
363
|
+
return this.repository;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Create a credits service with a repository
|
|
368
|
+
*/
|
|
369
|
+
export function createCreditsService(repository) {
|
|
370
|
+
return new CreditsService(repository);
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=credits-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credits-service.js","sourceRoot":"","sources":["../../src/service/credits-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,8BAA8B,EAC9B,uCAAuC,GACxC,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/service/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit error utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to parse and identify credit-related errors
|
|
5
|
+
* for consistent error handling across the application.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parsed information from a credit error message
|
|
9
|
+
*/
|
|
10
|
+
export interface CreditErrorInfo {
|
|
11
|
+
/** Credits currently available */
|
|
12
|
+
available: number;
|
|
13
|
+
/** Credits required for the operation */
|
|
14
|
+
required: number;
|
|
15
|
+
/** Difference between required and available */
|
|
16
|
+
shortfall: number;
|
|
17
|
+
/** Original error message */
|
|
18
|
+
rawMessage: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Parse a credit error message to extract available/required credits
|
|
22
|
+
*
|
|
23
|
+
* @param error - Error message string
|
|
24
|
+
* @returns Parsed credit info or null if not a valid credit error format
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const error = "Insufficient credits. Available: 3, Required: 10";
|
|
29
|
+
* const info = parseCreditError(error);
|
|
30
|
+
* // { available: 3, required: 10, shortfall: 7, rawMessage: "..." }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseCreditError(error: string): CreditErrorInfo | null;
|
|
34
|
+
/**
|
|
35
|
+
* Check if an error message is a credit-related error
|
|
36
|
+
*
|
|
37
|
+
* @param error - Error message string
|
|
38
|
+
* @returns true if the error is credit-related
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* if (isCreditError(error)) {
|
|
43
|
+
* showCreditErrorDialog(parseCreditError(error));
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function isCreditErrorMessage(error: string): boolean;
|
|
48
|
+
//# sourceMappingURL=error-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-utils.d.ts","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAsBtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAM3D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit error utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to parse and identify credit-related errors
|
|
5
|
+
* for consistent error handling across the application.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Parse a credit error message to extract available/required credits
|
|
9
|
+
*
|
|
10
|
+
* @param error - Error message string
|
|
11
|
+
* @returns Parsed credit info or null if not a valid credit error format
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const error = "Insufficient credits. Available: 3, Required: 10";
|
|
16
|
+
* const info = parseCreditError(error);
|
|
17
|
+
* // { available: 3, required: 10, shortfall: 7, rawMessage: "..." }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function parseCreditError(error) {
|
|
21
|
+
// Pattern: Available: X, Required: Y (case insensitive, flexible whitespace)
|
|
22
|
+
const match = error.match(/Available:\s*(\d+)\s*,\s*Required:\s*(\d+)/i);
|
|
23
|
+
if (!match) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const available = parseInt(match[1], 10);
|
|
27
|
+
const required = parseInt(match[2], 10);
|
|
28
|
+
// Validate parsed numbers
|
|
29
|
+
if (isNaN(available) || isNaN(required)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
available,
|
|
34
|
+
required,
|
|
35
|
+
shortfall: required - available,
|
|
36
|
+
rawMessage: error,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check if an error message is a credit-related error
|
|
41
|
+
*
|
|
42
|
+
* @param error - Error message string
|
|
43
|
+
* @returns true if the error is credit-related
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* if (isCreditError(error)) {
|
|
48
|
+
* showCreditErrorDialog(parseCreditError(error));
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function isCreditErrorMessage(error) {
|
|
53
|
+
const lowerError = error.toLowerCase();
|
|
54
|
+
return (lowerError.includes("insufficient credits") ||
|
|
55
|
+
lowerError.includes("not enough credits"));
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=error-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-utils.js","sourceRoot":"","sources":["../../src/utils/error-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,6EAA6E;IAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAEzE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAExC,0BAA0B;IAC1B,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,SAAS;QACT,QAAQ;QACR,SAAS,EAAE,QAAQ,GAAG,SAAS;QAC/B,UAAU,EAAE,KAAK;KAClB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,OAAO,CACL,UAAU,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QAC3C,UAAU,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAC1C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit utility functions
|
|
3
|
+
*
|
|
4
|
+
* Common helper functions for credit calculations
|
|
5
|
+
*/
|
|
6
|
+
import type { PortableReservation } from "../core";
|
|
7
|
+
export { parseCreditError, isCreditErrorMessage, type CreditErrorInfo, } from "./error-utils";
|
|
8
|
+
/**
|
|
9
|
+
* Calculate total available credits from balance and bonus credits
|
|
10
|
+
*
|
|
11
|
+
* @param credits - Object containing balance and bonusCredits properties
|
|
12
|
+
* @returns Total credits (balance + bonusCredits)
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const total = getTotalCredits({ balance: 100, bonusCredits: 50 });
|
|
17
|
+
* // Returns 150
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function getTotalCredits(credits: {
|
|
21
|
+
balance: number;
|
|
22
|
+
bonusCredits: number;
|
|
23
|
+
}): number;
|
|
24
|
+
/**
|
|
25
|
+
* Generate a unique request ID
|
|
26
|
+
*
|
|
27
|
+
* Format: req_{timestamp}_{randomPart}
|
|
28
|
+
* Example: req_1706234567890_a1b2c3d4e5f6
|
|
29
|
+
*
|
|
30
|
+
* @returns URL-safe request ID string
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateRequestId(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Check if the input data indicates preview mode
|
|
35
|
+
*
|
|
36
|
+
* Preview mode skips actual credit deduction - used for dry runs
|
|
37
|
+
* and UI previews where the user wants to see results without committing.
|
|
38
|
+
*
|
|
39
|
+
* @param data - The input data to check
|
|
40
|
+
* @returns true if data has preview: true
|
|
41
|
+
*/
|
|
42
|
+
export declare function isPreviewMode(data: unknown): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Create a dummy reservation for preview mode
|
|
45
|
+
*
|
|
46
|
+
* Used when `isPreviewMode(data)` returns true to provide
|
|
47
|
+
* a valid reservation object without actual credit operations.
|
|
48
|
+
*
|
|
49
|
+
* @param userId - The user ID
|
|
50
|
+
* @param operationType - The operation type
|
|
51
|
+
* @returns A dummy PortableReservation with id "preview-mode"
|
|
52
|
+
*/
|
|
53
|
+
export declare function createDummyReservation(userId: string, operationType: string): PortableReservation;
|
|
54
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB,GAAG,MAAM,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAI1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAOpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,mBAAmB,CAWrB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit utility functions
|
|
3
|
+
*
|
|
4
|
+
* Common helper functions for credit calculations
|
|
5
|
+
*/
|
|
6
|
+
export { parseCreditError, isCreditErrorMessage, } from "./error-utils";
|
|
7
|
+
/**
|
|
8
|
+
* Calculate total available credits from balance and bonus credits
|
|
9
|
+
*
|
|
10
|
+
* @param credits - Object containing balance and bonusCredits properties
|
|
11
|
+
* @returns Total credits (balance + bonusCredits)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const total = getTotalCredits({ balance: 100, bonusCredits: 50 });
|
|
16
|
+
* // Returns 150
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function getTotalCredits(credits) {
|
|
20
|
+
return credits.balance + credits.bonusCredits;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate a unique request ID
|
|
24
|
+
*
|
|
25
|
+
* Format: req_{timestamp}_{randomPart}
|
|
26
|
+
* Example: req_1706234567890_a1b2c3d4e5f6
|
|
27
|
+
*
|
|
28
|
+
* @returns URL-safe request ID string
|
|
29
|
+
*/
|
|
30
|
+
export function generateRequestId() {
|
|
31
|
+
const timestamp = Date.now();
|
|
32
|
+
const randomPart = Math.random().toString(36).substring(2, 14);
|
|
33
|
+
return `req_${timestamp}_${randomPart}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check if the input data indicates preview mode
|
|
37
|
+
*
|
|
38
|
+
* Preview mode skips actual credit deduction - used for dry runs
|
|
39
|
+
* and UI previews where the user wants to see results without committing.
|
|
40
|
+
*
|
|
41
|
+
* @param data - The input data to check
|
|
42
|
+
* @returns true if data has preview: true
|
|
43
|
+
*/
|
|
44
|
+
export function isPreviewMode(data) {
|
|
45
|
+
return (typeof data === "object" &&
|
|
46
|
+
data !== null &&
|
|
47
|
+
"preview" in data &&
|
|
48
|
+
data.preview === true);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Create a dummy reservation for preview mode
|
|
52
|
+
*
|
|
53
|
+
* Used when `isPreviewMode(data)` returns true to provide
|
|
54
|
+
* a valid reservation object without actual credit operations.
|
|
55
|
+
*
|
|
56
|
+
* @param userId - The user ID
|
|
57
|
+
* @param operationType - The operation type
|
|
58
|
+
* @returns A dummy PortableReservation with id "preview-mode"
|
|
59
|
+
*/
|
|
60
|
+
export function createDummyReservation(userId, operationType) {
|
|
61
|
+
const now = new Date().toISOString();
|
|
62
|
+
return {
|
|
63
|
+
id: "preview-mode",
|
|
64
|
+
userId,
|
|
65
|
+
amount: 0,
|
|
66
|
+
operationType,
|
|
67
|
+
status: "released",
|
|
68
|
+
createdAt: now,
|
|
69
|
+
expiresAt: now,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GAErB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,OAG/B;IACC,OAAO,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC;AAChD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/D,OAAO,OAAO,SAAS,IAAI,UAAU,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,KAAK,IAAI;QACb,SAAS,IAAI,IAAI;QAChB,IAAgC,CAAC,OAAO,KAAK,IAAI,CACnD,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,aAAqB;IAErB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,OAAO;QACL,EAAE,EAAE,cAAc;QAClB,MAAM;QACN,MAAM,EAAE,CAAC;QACT,aAAa;QACb,MAAM,EAAE,UAAU;QAClB,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,GAAG;KACf,CAAC;AACJ,CAAC"}
|