@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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/generic.d.ts +18 -0
  3. package/dist/adapters/generic.d.ts.map +1 -0
  4. package/dist/adapters/generic.js +147 -0
  5. package/dist/adapters/generic.js.map +1 -0
  6. package/dist/adapters/index.d.ts +3 -0
  7. package/dist/adapters/index.d.ts.map +1 -0
  8. package/dist/adapters/index.js +2 -0
  9. package/dist/adapters/index.js.map +1 -0
  10. package/dist/adapters/types.d.ts +45 -0
  11. package/dist/adapters/types.d.ts.map +1 -0
  12. package/dist/adapters/types.js +8 -0
  13. package/dist/adapters/types.js.map +1 -0
  14. package/dist/auth/api-key.d.ts +37 -0
  15. package/dist/auth/api-key.d.ts.map +1 -0
  16. package/dist/auth/api-key.js +94 -0
  17. package/dist/auth/api-key.js.map +1 -0
  18. package/dist/auth/index.d.ts +8 -0
  19. package/dist/auth/index.d.ts.map +1 -0
  20. package/dist/auth/index.js +8 -0
  21. package/dist/auth/index.js.map +1 -0
  22. package/dist/auth/types.d.ts +31 -0
  23. package/dist/auth/types.d.ts.map +1 -0
  24. package/dist/auth/types.js +2 -0
  25. package/dist/auth/types.js.map +1 -0
  26. package/dist/config/costs.d.ts +61 -0
  27. package/dist/config/costs.d.ts.map +1 -0
  28. package/dist/config/costs.js +86 -0
  29. package/dist/config/costs.js.map +1 -0
  30. package/dist/config/index.d.ts +165 -0
  31. package/dist/config/index.d.ts.map +1 -0
  32. package/dist/config/index.js +286 -0
  33. package/dist/config/index.js.map +1 -0
  34. package/dist/core/deferred.d.ts +65 -0
  35. package/dist/core/deferred.d.ts.map +1 -0
  36. package/dist/core/deferred.js +72 -0
  37. package/dist/core/deferred.js.map +1 -0
  38. package/dist/core/errors.d.ts +78 -0
  39. package/dist/core/errors.d.ts.map +1 -0
  40. package/dist/core/errors.js +110 -0
  41. package/dist/core/errors.js.map +1 -0
  42. package/dist/core/index.d.ts +29 -0
  43. package/dist/core/index.d.ts.map +1 -0
  44. package/dist/core/index.js +28 -0
  45. package/dist/core/index.js.map +1 -0
  46. package/dist/core/operations.d.ts +36 -0
  47. package/dist/core/operations.d.ts.map +1 -0
  48. package/dist/core/operations.js +87 -0
  49. package/dist/core/operations.js.map +1 -0
  50. package/dist/core/types.d.ts +287 -0
  51. package/dist/core/types.d.ts.map +1 -0
  52. package/dist/core/types.js +93 -0
  53. package/dist/core/types.js.map +1 -0
  54. package/dist/index.d.ts +57 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +63 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/notifications/index.d.ts +76 -0
  59. package/dist/notifications/index.d.ts.map +1 -0
  60. package/dist/notifications/index.js +182 -0
  61. package/dist/notifications/index.js.map +1 -0
  62. package/dist/repository/index.d.ts +10 -0
  63. package/dist/repository/index.d.ts.map +1 -0
  64. package/dist/repository/index.js +11 -0
  65. package/dist/repository/index.js.map +1 -0
  66. package/dist/repository/memory/index.d.ts +70 -0
  67. package/dist/repository/memory/index.d.ts.map +1 -0
  68. package/dist/repository/memory/index.js +518 -0
  69. package/dist/repository/memory/index.js.map +1 -0
  70. package/dist/repository/types.d.ts +283 -0
  71. package/dist/repository/types.d.ts.map +1 -0
  72. package/dist/repository/types.js +39 -0
  73. package/dist/repository/types.js.map +1 -0
  74. package/dist/repository/utils.d.ts +22 -0
  75. package/dist/repository/utils.d.ts.map +1 -0
  76. package/dist/repository/utils.js +40 -0
  77. package/dist/repository/utils.js.map +1 -0
  78. package/dist/sdk/admin-client.d.ts +146 -0
  79. package/dist/sdk/admin-client.d.ts.map +1 -0
  80. package/dist/sdk/admin-client.js +196 -0
  81. package/dist/sdk/admin-client.js.map +1 -0
  82. package/dist/sdk/client.d.ts +144 -0
  83. package/dist/sdk/client.d.ts.map +1 -0
  84. package/dist/sdk/client.js +247 -0
  85. package/dist/sdk/client.js.map +1 -0
  86. package/dist/sdk/errors.d.ts +91 -0
  87. package/dist/sdk/errors.d.ts.map +1 -0
  88. package/dist/sdk/errors.js +185 -0
  89. package/dist/sdk/errors.js.map +1 -0
  90. package/dist/sdk/index.d.ts +13 -0
  91. package/dist/sdk/index.d.ts.map +1 -0
  92. package/dist/sdk/index.js +13 -0
  93. package/dist/sdk/index.js.map +1 -0
  94. package/dist/sdk/types.d.ts +110 -0
  95. package/dist/sdk/types.d.ts.map +1 -0
  96. package/dist/sdk/types.js +20 -0
  97. package/dist/sdk/types.js.map +1 -0
  98. package/dist/service/credits-service.d.ts +139 -0
  99. package/dist/service/credits-service.d.ts.map +1 -0
  100. package/dist/service/credits-service.js +372 -0
  101. package/dist/service/credits-service.js.map +1 -0
  102. package/dist/service/index.d.ts +3 -0
  103. package/dist/service/index.d.ts.map +1 -0
  104. package/dist/service/index.js +2 -0
  105. package/dist/service/index.js.map +1 -0
  106. package/dist/utils/error-utils.d.ts +48 -0
  107. package/dist/utils/error-utils.d.ts.map +1 -0
  108. package/dist/utils/error-utils.js +57 -0
  109. package/dist/utils/error-utils.js.map +1 -0
  110. package/dist/utils/index.d.ts +54 -0
  111. package/dist/utils/index.d.ts.map +1 -0
  112. package/dist/utils/index.js +72 -0
  113. package/dist/utils/index.js.map +1 -0
  114. 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,3 @@
1
+ export { CreditsService, createCreditsService, } from "./credits-service";
2
+ export type { LowBalanceNotificationCallback, SubscriptionExpiredNotificationCallback, } from "./credits-service";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,2 @@
1
+ export { CreditsService, createCreditsService, } from "./credits-service";
2
+ //# sourceMappingURL=index.js.map
@@ -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"}