@nehorai/credits 1.0.2 → 1.3.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 (134) hide show
  1. package/dist/adapters/index.cjs +463 -0
  2. package/dist/adapters/index.cjs.map +1 -0
  3. package/dist/adapters/index.d.cts +66 -0
  4. package/dist/adapters/index.d.ts +66 -3
  5. package/dist/adapters/index.js +435 -1
  6. package/dist/adapters/index.js.map +1 -1
  7. package/dist/auth/index.cjs +96 -0
  8. package/dist/auth/index.cjs.map +1 -0
  9. package/dist/auth/{api-key.d.ts → index.d.cts} +9 -6
  10. package/dist/auth/index.d.ts +37 -5
  11. package/dist/auth/index.js +65 -7
  12. package/dist/auth/index.js.map +1 -1
  13. package/dist/core/index.cjs +522 -0
  14. package/dist/core/index.cjs.map +1 -0
  15. package/dist/core/index.d.cts +119 -0
  16. package/dist/core/index.d.ts +115 -25
  17. package/dist/core/index.js +475 -27
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/{core/deferred.d.ts → deferred-C7rrRfkm.d.cts} +7 -6
  20. package/dist/{core/deferred.js → deferred-C7rrRfkm.d.ts} +23 -29
  21. package/dist/index.cjs +2506 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.cts +437 -0
  24. package/dist/index.d.ts +429 -49
  25. package/dist/index.js +2399 -62
  26. package/dist/index.js.map +1 -1
  27. package/dist/repository/index.cjs +816 -0
  28. package/dist/repository/index.cjs.map +1 -0
  29. package/dist/repository/{memory/index.d.ts → index.d.cts} +35 -5
  30. package/dist/repository/index.d.ts +97 -7
  31. package/dist/repository/index.js +783 -10
  32. package/dist/repository/index.js.map +1 -1
  33. package/dist/sdk/index.cjs +608 -0
  34. package/dist/sdk/index.cjs.map +1 -0
  35. package/dist/sdk/index.d.cts +495 -0
  36. package/dist/sdk/index.d.ts +491 -9
  37. package/dist/sdk/index.js +568 -12
  38. package/dist/sdk/index.js.map +1 -1
  39. package/dist/service/index.cjs +670 -0
  40. package/dist/service/index.cjs.map +1 -0
  41. package/dist/service/{credits-service.d.ts → index.d.cts} +9 -7
  42. package/dist/service/index.d.ts +141 -3
  43. package/dist/service/index.js +641 -1
  44. package/dist/service/index.js.map +1 -1
  45. package/dist/{core/types.d.ts → types-BBxIWHK2.d.cts} +25 -35
  46. package/dist/types-BBxIWHK2.d.ts +277 -0
  47. package/dist/types-D1vdeBcA.d.cts +305 -0
  48. package/dist/{auth/types.d.ts → types-OZU3I5OM.d.cts} +5 -4
  49. package/dist/types-OZU3I5OM.d.ts +32 -0
  50. package/dist/{repository/types.d.ts → types-aJsyv8df.d.ts} +35 -13
  51. package/package.json +14 -13
  52. package/dist/adapters/generic.d.ts +0 -18
  53. package/dist/adapters/generic.d.ts.map +0 -1
  54. package/dist/adapters/generic.js +0 -147
  55. package/dist/adapters/generic.js.map +0 -1
  56. package/dist/adapters/index.d.ts.map +0 -1
  57. package/dist/adapters/types.d.ts +0 -45
  58. package/dist/adapters/types.d.ts.map +0 -1
  59. package/dist/adapters/types.js +0 -8
  60. package/dist/adapters/types.js.map +0 -1
  61. package/dist/auth/api-key.d.ts.map +0 -1
  62. package/dist/auth/api-key.js +0 -94
  63. package/dist/auth/api-key.js.map +0 -1
  64. package/dist/auth/index.d.ts.map +0 -1
  65. package/dist/auth/types.d.ts.map +0 -1
  66. package/dist/auth/types.js +0 -2
  67. package/dist/auth/types.js.map +0 -1
  68. package/dist/config/costs.d.ts +0 -61
  69. package/dist/config/costs.d.ts.map +0 -1
  70. package/dist/config/costs.js +0 -86
  71. package/dist/config/costs.js.map +0 -1
  72. package/dist/config/index.d.ts +0 -165
  73. package/dist/config/index.d.ts.map +0 -1
  74. package/dist/config/index.js +0 -286
  75. package/dist/config/index.js.map +0 -1
  76. package/dist/core/deferred.d.ts.map +0 -1
  77. package/dist/core/deferred.js.map +0 -1
  78. package/dist/core/errors.d.ts +0 -78
  79. package/dist/core/errors.d.ts.map +0 -1
  80. package/dist/core/errors.js +0 -110
  81. package/dist/core/errors.js.map +0 -1
  82. package/dist/core/index.d.ts.map +0 -1
  83. package/dist/core/operations.d.ts +0 -36
  84. package/dist/core/operations.d.ts.map +0 -1
  85. package/dist/core/operations.js +0 -87
  86. package/dist/core/operations.js.map +0 -1
  87. package/dist/core/types.d.ts.map +0 -1
  88. package/dist/core/types.js +0 -93
  89. package/dist/core/types.js.map +0 -1
  90. package/dist/index.d.ts.map +0 -1
  91. package/dist/notifications/index.d.ts +0 -76
  92. package/dist/notifications/index.d.ts.map +0 -1
  93. package/dist/notifications/index.js +0 -182
  94. package/dist/notifications/index.js.map +0 -1
  95. package/dist/repository/index.d.ts.map +0 -1
  96. package/dist/repository/memory/index.d.ts.map +0 -1
  97. package/dist/repository/memory/index.js +0 -518
  98. package/dist/repository/memory/index.js.map +0 -1
  99. package/dist/repository/types.d.ts.map +0 -1
  100. package/dist/repository/types.js +0 -39
  101. package/dist/repository/types.js.map +0 -1
  102. package/dist/repository/utils.d.ts +0 -22
  103. package/dist/repository/utils.d.ts.map +0 -1
  104. package/dist/repository/utils.js +0 -40
  105. package/dist/repository/utils.js.map +0 -1
  106. package/dist/sdk/admin-client.d.ts +0 -146
  107. package/dist/sdk/admin-client.d.ts.map +0 -1
  108. package/dist/sdk/admin-client.js +0 -196
  109. package/dist/sdk/admin-client.js.map +0 -1
  110. package/dist/sdk/client.d.ts +0 -144
  111. package/dist/sdk/client.d.ts.map +0 -1
  112. package/dist/sdk/client.js +0 -247
  113. package/dist/sdk/client.js.map +0 -1
  114. package/dist/sdk/errors.d.ts +0 -91
  115. package/dist/sdk/errors.d.ts.map +0 -1
  116. package/dist/sdk/errors.js +0 -185
  117. package/dist/sdk/errors.js.map +0 -1
  118. package/dist/sdk/index.d.ts.map +0 -1
  119. package/dist/sdk/types.d.ts +0 -110
  120. package/dist/sdk/types.d.ts.map +0 -1
  121. package/dist/sdk/types.js +0 -20
  122. package/dist/sdk/types.js.map +0 -1
  123. package/dist/service/credits-service.d.ts.map +0 -1
  124. package/dist/service/credits-service.js +0 -372
  125. package/dist/service/credits-service.js.map +0 -1
  126. package/dist/service/index.d.ts.map +0 -1
  127. package/dist/utils/error-utils.d.ts +0 -48
  128. package/dist/utils/error-utils.d.ts.map +0 -1
  129. package/dist/utils/error-utils.js +0 -57
  130. package/dist/utils/error-utils.js.map +0 -1
  131. package/dist/utils/index.d.ts +0 -54
  132. package/dist/utils/index.d.ts.map +0 -1
  133. package/dist/utils/index.js +0 -72
  134. package/dist/utils/index.js.map +0 -1
package/dist/sdk/index.js CHANGED
@@ -1,13 +1,569 @@
1
- /**
2
- * Credits SDK - barrel exports
3
- *
4
- * Provides TypeScript SDK for consuming the credits REST API
5
- * from external services or other applications.
6
- */
7
- // Clients
8
- export { CreditsClient } from "./client.js";
9
- export { AdminCreditsClient } from "./admin-client.js";
10
- export { SDKErrorCode } from "./types.js";
11
- // Errors
12
- export { CreditsSDKError, NetworkError, AuthenticationError, AuthorizationError, InsufficientCreditsError, ReservationNotFoundError, ReservationExpiredError, ValidationError, ServerError, parseApiError, } from "./errors.js";
1
+ // src/sdk/types.ts
2
+ var SDKErrorCode = {
3
+ NETWORK_ERROR: "NETWORK_ERROR",
4
+ AUTHENTICATION_ERROR: "AUTHENTICATION_ERROR",
5
+ AUTHORIZATION_ERROR: "AUTHORIZATION_ERROR",
6
+ INSUFFICIENT_CREDITS: "INSUFFICIENT_CREDITS",
7
+ RESERVATION_NOT_FOUND: "RESERVATION_NOT_FOUND",
8
+ RESERVATION_EXPIRED: "RESERVATION_EXPIRED",
9
+ VALIDATION_ERROR: "VALIDATION_ERROR",
10
+ SERVER_ERROR: "SERVER_ERROR",
11
+ UNKNOWN_ERROR: "UNKNOWN_ERROR"
12
+ };
13
+
14
+ // src/sdk/errors.ts
15
+ var CreditsSDKError = class extends Error {
16
+ code;
17
+ details;
18
+ statusCode;
19
+ constructor(message, code, options) {
20
+ super(message, { cause: options?.cause });
21
+ this.name = "CreditsSDKError";
22
+ this.code = code;
23
+ this.details = options?.details;
24
+ this.statusCode = options?.statusCode;
25
+ }
26
+ /**
27
+ * Check if this is a specific error type
28
+ */
29
+ is(code) {
30
+ return this.code === code;
31
+ }
32
+ /**
33
+ * Create a JSON representation for logging
34
+ */
35
+ toJSON() {
36
+ return {
37
+ name: this.name,
38
+ message: this.message,
39
+ code: this.code,
40
+ details: this.details,
41
+ statusCode: this.statusCode
42
+ };
43
+ }
44
+ };
45
+ var NetworkError = class extends CreditsSDKError {
46
+ constructor(message, cause) {
47
+ super(message, SDKErrorCode.NETWORK_ERROR, { cause });
48
+ this.name = "NetworkError";
49
+ }
50
+ };
51
+ var AuthenticationError = class extends CreditsSDKError {
52
+ constructor(message = "Authentication required") {
53
+ super(message, SDKErrorCode.AUTHENTICATION_ERROR, { statusCode: 401 });
54
+ this.name = "AuthenticationError";
55
+ }
56
+ };
57
+ var AuthorizationError = class extends CreditsSDKError {
58
+ constructor(message = "Access denied") {
59
+ super(message, SDKErrorCode.AUTHORIZATION_ERROR, { statusCode: 403 });
60
+ this.name = "AuthorizationError";
61
+ }
62
+ };
63
+ var InsufficientCreditsError = class extends CreditsSDKError {
64
+ available;
65
+ required;
66
+ constructor(available, required) {
67
+ super(
68
+ `Insufficient credits: available ${available}, required ${required}`,
69
+ SDKErrorCode.INSUFFICIENT_CREDITS,
70
+ {
71
+ statusCode: 402,
72
+ details: { available, required }
73
+ }
74
+ );
75
+ this.name = "InsufficientCreditsError";
76
+ this.available = available;
77
+ this.required = required;
78
+ }
79
+ };
80
+ var ReservationNotFoundError = class extends CreditsSDKError {
81
+ reservationId;
82
+ constructor(reservationId) {
83
+ super(
84
+ `Reservation not found: ${reservationId}`,
85
+ SDKErrorCode.RESERVATION_NOT_FOUND,
86
+ {
87
+ statusCode: 404,
88
+ details: { reservationId }
89
+ }
90
+ );
91
+ this.name = "ReservationNotFoundError";
92
+ this.reservationId = reservationId;
93
+ }
94
+ };
95
+ var ReservationExpiredError = class extends CreditsSDKError {
96
+ reservationId;
97
+ constructor(reservationId) {
98
+ super(
99
+ `Reservation expired: ${reservationId}`,
100
+ SDKErrorCode.RESERVATION_EXPIRED,
101
+ {
102
+ statusCode: 410,
103
+ details: { reservationId }
104
+ }
105
+ );
106
+ this.name = "ReservationExpiredError";
107
+ this.reservationId = reservationId;
108
+ }
109
+ };
110
+ var ValidationError = class extends CreditsSDKError {
111
+ field;
112
+ constructor(message, field) {
113
+ super(message, SDKErrorCode.VALIDATION_ERROR, {
114
+ statusCode: 400,
115
+ details: field ? { field } : void 0
116
+ });
117
+ this.name = "ValidationError";
118
+ this.field = field;
119
+ }
120
+ };
121
+ var ServerError = class extends CreditsSDKError {
122
+ constructor(message = "Internal server error") {
123
+ super(message, SDKErrorCode.SERVER_ERROR, { statusCode: 500 });
124
+ this.name = "ServerError";
125
+ }
126
+ };
127
+ function parseApiError(statusCode, body) {
128
+ const errorCode = body.error?.code || "";
129
+ const errorMessage = body.error?.message || "An error occurred";
130
+ const details = body.error?.details;
131
+ switch (statusCode) {
132
+ case 401:
133
+ return new AuthenticationError(errorMessage);
134
+ case 403:
135
+ return new AuthorizationError(errorMessage);
136
+ case 402:
137
+ if (details?.available !== void 0 && details?.required !== void 0) {
138
+ return new InsufficientCreditsError(
139
+ details.available,
140
+ details.required
141
+ );
142
+ }
143
+ return new CreditsSDKError(errorMessage, SDKErrorCode.INSUFFICIENT_CREDITS, {
144
+ statusCode,
145
+ details
146
+ });
147
+ case 404:
148
+ if (errorCode === "RESERVATION_NOT_FOUND" && details?.reservationId) {
149
+ return new ReservationNotFoundError(details.reservationId);
150
+ }
151
+ return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_NOT_FOUND, {
152
+ statusCode,
153
+ details
154
+ });
155
+ case 410:
156
+ if (errorCode === "RESERVATION_EXPIRED" && details?.reservationId) {
157
+ return new ReservationExpiredError(details.reservationId);
158
+ }
159
+ return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_EXPIRED, {
160
+ statusCode,
161
+ details
162
+ });
163
+ case 400:
164
+ case 422:
165
+ return new ValidationError(errorMessage, details?.field);
166
+ case 500:
167
+ default:
168
+ if (statusCode >= 500) {
169
+ return new ServerError(errorMessage);
170
+ }
171
+ return new CreditsSDKError(errorMessage, SDKErrorCode.UNKNOWN_ERROR, {
172
+ statusCode,
173
+ details
174
+ });
175
+ }
176
+ }
177
+
178
+ // src/sdk/client.ts
179
+ var CreditsClient = class {
180
+ baseUrl;
181
+ getAuthToken;
182
+ fetchFn;
183
+ constructor(config) {
184
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
185
+ this.getAuthToken = config.getAuthToken;
186
+ this.fetchFn = config.fetch ?? fetch;
187
+ }
188
+ /**
189
+ * Make an authenticated request to the API
190
+ */
191
+ async request(path, options = {}) {
192
+ const { method = "GET", body, params } = options;
193
+ let url = `${this.baseUrl}${path}`;
194
+ if (params) {
195
+ const searchParams = new URLSearchParams();
196
+ for (const [key, value] of Object.entries(params)) {
197
+ if (value !== void 0) {
198
+ searchParams.set(key, String(value));
199
+ }
200
+ }
201
+ const queryString = searchParams.toString();
202
+ if (queryString) {
203
+ url += `?${queryString}`;
204
+ }
205
+ }
206
+ const headers = {
207
+ "Content-Type": "application/json"
208
+ };
209
+ if (this.getAuthToken) {
210
+ try {
211
+ const token = await this.getAuthToken();
212
+ if (token) {
213
+ headers["Authorization"] = `Bearer ${token}`;
214
+ }
215
+ } catch {
216
+ }
217
+ }
218
+ let response;
219
+ try {
220
+ response = await this.fetchFn(url, {
221
+ method,
222
+ headers,
223
+ body: body ? JSON.stringify(body) : void 0
224
+ });
225
+ } catch (error) {
226
+ throw new NetworkError(
227
+ error instanceof Error ? error.message : "Network request failed",
228
+ error instanceof Error ? error : void 0
229
+ );
230
+ }
231
+ const responseBody = await response.json();
232
+ if (!response.ok) {
233
+ throw parseApiError(response.status, responseBody);
234
+ }
235
+ return responseBody.data;
236
+ }
237
+ /**
238
+ * Get the current user's credit balance
239
+ *
240
+ * @returns User's credit information
241
+ * @throws AuthenticationError if not authenticated
242
+ */
243
+ async getBalance() {
244
+ return this.request("/api/v1/credits/balance");
245
+ }
246
+ /**
247
+ * Check if the user has sufficient credits for an operation
248
+ *
249
+ * @param operationType - Type of operation to check
250
+ * @param customCost - Optional custom cost to check against
251
+ * @returns Credit availability status
252
+ * @throws AuthenticationError if not authenticated
253
+ */
254
+ async checkAvailability(operationType, customCost) {
255
+ return this.request("/api/v1/credits/availability", {
256
+ params: {
257
+ operationType,
258
+ customCost: customCost?.toString()
259
+ }
260
+ });
261
+ }
262
+ /**
263
+ * Reserve credits for an operation
264
+ *
265
+ * This is the first step in the two-phase commit pattern.
266
+ * Reserved credits are held for 5 minutes before expiring.
267
+ *
268
+ * @param operationType - Type of operation
269
+ * @param options - Optional reservation options
270
+ * @returns Reservation details including ID and expiry
271
+ * @throws InsufficientCreditsError if not enough credits
272
+ * @throws AuthenticationError if not authenticated
273
+ */
274
+ async reserve(operationType, options) {
275
+ return this.request("/api/v1/credits/reserve", {
276
+ method: "POST",
277
+ body: {
278
+ operationType,
279
+ ...options
280
+ }
281
+ });
282
+ }
283
+ /**
284
+ * Commit a reservation (deduct credits)
285
+ *
286
+ * This is the second step in the two-phase commit pattern.
287
+ * Call this after your operation succeeds.
288
+ *
289
+ * @param reservationId - ID of the reservation to commit
290
+ * @throws ReservationNotFoundError if reservation doesn't exist
291
+ * @throws ReservationExpiredError if reservation has expired
292
+ * @throws AuthenticationError if not authenticated
293
+ */
294
+ async commit(reservationId) {
295
+ await this.request("/api/v1/credits/commit", {
296
+ method: "POST",
297
+ body: { reservationId }
298
+ });
299
+ }
300
+ /**
301
+ * Release a reservation (refund credits)
302
+ *
303
+ * Call this if your operation fails after reserving credits.
304
+ *
305
+ * @param reservationId - ID of the reservation to release
306
+ * @throws ReservationNotFoundError if reservation doesn't exist
307
+ * @throws AuthenticationError if not authenticated
308
+ */
309
+ async release(reservationId) {
310
+ await this.request("/api/v1/credits/release", {
311
+ method: "POST",
312
+ body: { reservationId }
313
+ });
314
+ }
315
+ /**
316
+ * Get usage history for the current user
317
+ *
318
+ * @param options - Optional pagination options
319
+ * @returns Paginated list of usage entries
320
+ * @throws AuthenticationError if not authenticated
321
+ */
322
+ async getHistory(options) {
323
+ return this.request("/api/v1/credits/history", {
324
+ params: {
325
+ page: options?.page,
326
+ limit: options?.limit
327
+ }
328
+ });
329
+ }
330
+ /**
331
+ * Get the available credits (balance + bonus - reserved)
332
+ *
333
+ * Convenience method that calculates available credits from balance.
334
+ *
335
+ * @returns Number of available credits
336
+ * @throws AuthenticationError if not authenticated
337
+ */
338
+ async getAvailableCredits() {
339
+ const balance = await this.getBalance();
340
+ return balance.balance + balance.bonusCredits - balance.reserved;
341
+ }
342
+ /**
343
+ * Execute an operation with automatic credit handling
344
+ *
345
+ * This is a convenience method that handles the full two-phase commit
346
+ * pattern automatically.
347
+ *
348
+ * @param operationType - Type of operation
349
+ * @param operation - The operation to execute
350
+ * @param options - Optional reservation options
351
+ * @returns Result of the operation
352
+ * @throws InsufficientCreditsError if not enough credits
353
+ * @throws AuthenticationError if not authenticated
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * const result = await client.withCredits(
358
+ * "story_generation",
359
+ * async () => {
360
+ * return generateStory(data);
361
+ * }
362
+ * );
363
+ * ```
364
+ */
365
+ async withCredits(operationType, operation, options) {
366
+ const reservation = await this.reserve(operationType, options);
367
+ try {
368
+ const result = await operation();
369
+ await this.commit(reservation.reservationId);
370
+ return result;
371
+ } catch (error) {
372
+ try {
373
+ await this.release(reservation.reservationId);
374
+ } catch {
375
+ }
376
+ throw error;
377
+ }
378
+ }
379
+ };
380
+
381
+ // src/sdk/admin-client.ts
382
+ var AdminCreditsClient = class {
383
+ baseUrl;
384
+ apiKey;
385
+ fetchFn;
386
+ constructor(config) {
387
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
388
+ this.apiKey = config.apiKey;
389
+ this.fetchFn = config.fetch ?? fetch;
390
+ }
391
+ /**
392
+ * Make an authenticated admin request to the API
393
+ */
394
+ async request(path, options = {}) {
395
+ const { method = "GET", body, params } = options;
396
+ let url = `${this.baseUrl}${path}`;
397
+ if (params) {
398
+ const searchParams = new URLSearchParams();
399
+ for (const [key, value] of Object.entries(params)) {
400
+ if (value !== void 0) {
401
+ searchParams.set(key, String(value));
402
+ }
403
+ }
404
+ const queryString = searchParams.toString();
405
+ if (queryString) {
406
+ url += `?${queryString}`;
407
+ }
408
+ }
409
+ const headers = {
410
+ "Content-Type": "application/json",
411
+ "x-api-key": this.apiKey
412
+ };
413
+ let response;
414
+ try {
415
+ response = await this.fetchFn(url, {
416
+ method,
417
+ headers,
418
+ body: body ? JSON.stringify(body) : void 0
419
+ });
420
+ } catch (error) {
421
+ throw new NetworkError(
422
+ error instanceof Error ? error.message : "Network request failed",
423
+ error instanceof Error ? error : void 0
424
+ );
425
+ }
426
+ const responseBody = await response.json();
427
+ if (!response.ok) {
428
+ throw parseApiError(response.status, responseBody);
429
+ }
430
+ return responseBody.data;
431
+ }
432
+ /**
433
+ * Get a specific user's credits
434
+ *
435
+ * @param userId - ID of the user
436
+ * @returns User's credit information
437
+ * @throws AuthorizationError if API key is invalid
438
+ */
439
+ async getUserCredits(userId) {
440
+ return this.request(`/api/admin/credits/users/${userId}`);
441
+ }
442
+ /**
443
+ * Add credits to a user's account
444
+ *
445
+ * Credits are added to bonusCredits, which don't expire on monthly reset.
446
+ *
447
+ * @param userId - ID of the user
448
+ * @param amount - Amount of credits to add
449
+ * @param description - Reason for adding credits
450
+ * @throws AuthorizationError if API key is invalid
451
+ */
452
+ async addCredits(userId, amount, description) {
453
+ await this.request(
454
+ `/api/admin/credits/users/${userId}/add`,
455
+ {
456
+ method: "POST",
457
+ body: { amount, description }
458
+ }
459
+ );
460
+ }
461
+ /**
462
+ * Update a user's subscription tier
463
+ *
464
+ * @param userId - ID of the user
465
+ * @param tier - New subscription tier
466
+ * @param expiresAt - Optional expiration date (ISO 8601)
467
+ * @throws AuthorizationError if API key is invalid
468
+ */
469
+ async updateSubscription(userId, tier, expiresAt) {
470
+ const body = { tier };
471
+ if (expiresAt) {
472
+ body.expiresAt = expiresAt;
473
+ }
474
+ await this.request(
475
+ `/api/admin/credits/users/${userId}/subscription`,
476
+ {
477
+ method: "PUT",
478
+ body
479
+ }
480
+ );
481
+ }
482
+ /**
483
+ * Get the current credits configuration
484
+ *
485
+ * @returns Credits system configuration
486
+ * @throws AuthorizationError if API key is invalid
487
+ */
488
+ async getConfig() {
489
+ return this.request("/api/admin/credits/config");
490
+ }
491
+ /**
492
+ * Update operation costs
493
+ *
494
+ * @param costs - New operation costs (operation type -> cost)
495
+ * @throws AuthorizationError if API key is invalid
496
+ */
497
+ async updateCosts(costs) {
498
+ await this.request("/api/admin/credits/config/costs", {
499
+ method: "PUT",
500
+ body: { operationCosts: costs }
501
+ });
502
+ }
503
+ /**
504
+ * List all users with their credits
505
+ *
506
+ * @param options - Pagination and filtering options
507
+ * @returns Paginated list of users
508
+ * @throws AuthorizationError if API key is invalid
509
+ */
510
+ async listUsers(options) {
511
+ return this.request("/api/admin/credits/users", {
512
+ params: {
513
+ page: options?.page,
514
+ limit: options?.limit,
515
+ tier: options?.tier,
516
+ search: options?.search
517
+ }
518
+ });
519
+ }
520
+ /**
521
+ * Deduct credits from a user's account
522
+ *
523
+ * @param userId - ID of the user
524
+ * @param amount - Amount of credits to deduct
525
+ * @param description - Reason for deducting credits
526
+ * @throws AuthorizationError if API key is invalid
527
+ */
528
+ async deductCredits(userId, amount, description) {
529
+ await this.request(
530
+ `/api/admin/credits/users/${userId}/deduct`,
531
+ {
532
+ method: "POST",
533
+ body: { amount, description }
534
+ }
535
+ );
536
+ }
537
+ /**
538
+ * Get usage statistics for a user
539
+ *
540
+ * @param userId - ID of the user
541
+ * @param options - Optional date range
542
+ * @returns Usage statistics
543
+ * @throws AuthorizationError if API key is invalid
544
+ */
545
+ async getUserStats(userId, options) {
546
+ return this.request(`/api/admin/credits/users/${userId}/stats`, {
547
+ params: {
548
+ startDate: options?.startDate,
549
+ endDate: options?.endDate
550
+ }
551
+ });
552
+ }
553
+ };
554
+ export {
555
+ AdminCreditsClient,
556
+ AuthenticationError,
557
+ AuthorizationError,
558
+ CreditsClient,
559
+ CreditsSDKError,
560
+ InsufficientCreditsError,
561
+ NetworkError,
562
+ ReservationExpiredError,
563
+ ReservationNotFoundError,
564
+ SDKErrorCode,
565
+ ServerError,
566
+ ValidationError,
567
+ parseApiError
568
+ };
13
569
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,UAAU;AACV,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAkBvD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,SAAS;AACT,OAAO,EACL,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACvB,eAAe,EACf,WAAW,EACX,aAAa,GACd,MAAM,aAAa,CAAC"}
1
+ {"version":3,"sources":["../../src/sdk/types.ts","../../src/sdk/errors.ts","../../src/sdk/client.ts","../../src/sdk/admin-client.ts"],"sourcesContent":["/**\n * SDK types for the credits system\n *\n * These types are used by the client SDKs for REST API consumption.\n */\n\n// Re-export core types for SDK consumers\nexport type {\n PortableUserCredits as UserCredits,\n PortableReservation as CreditReservation,\n CreditCheckResult,\n SubscriptionTier,\n} from \"../core/types.js\";\n\n/**\n * Configuration for CreditsClient\n */\nexport interface CreditsClientConfig {\n /** Base URL of the credits API (e.g., \"https://api.example.com\") */\n baseUrl: string;\n /** Function to get the auth token for session-based auth */\n getAuthToken?: () => Promise<string>;\n /** Optional custom fetch function for testing */\n fetch?: typeof fetch;\n}\n\n/**\n * Configuration for AdminCreditsClient\n */\nexport interface AdminCreditsClientConfig {\n /** Base URL of the credits API */\n baseUrl: string;\n /** Admin API key */\n apiKey: string;\n /** Optional custom fetch function for testing */\n fetch?: typeof fetch;\n}\n\n/**\n * Result of a credit reservation\n */\nexport interface ReservationResult {\n /** The reservation ID */\n reservationId: string;\n /** Amount of credits reserved */\n amount: number;\n /** When the reservation expires (ISO 8601) */\n expiresAt: string;\n}\n\n/**\n * Pagination options for API requests\n */\nexport interface PaginationOptions {\n /** Page number (1-indexed) */\n page?: number;\n /** Number of items per page */\n limit?: number;\n}\n\n/**\n * Usage history entry\n */\nexport interface UsageHistoryEntry {\n /** Unique ID of the usage log */\n id: string;\n /** Type of operation */\n operationType: string;\n /** AI provider used */\n provider: string;\n /** Credits consumed */\n creditsUsed: number;\n /** Whether the operation was successful */\n success: boolean;\n /** Error message if unsuccessful */\n errorMessage?: string;\n /** ID of the related resource */\n resourceId?: string;\n /** Type of the related resource */\n resourceType?: string;\n /** When the operation occurred (ISO 8601) */\n createdAt: string;\n}\n\n/**\n * Response from usage history endpoint\n */\nexport interface UsageHistoryResponse {\n /** List of usage entries */\n entries: UsageHistoryEntry[];\n /** Whether there are more results */\n hasMore: boolean;\n /** Total count (if available) */\n total?: number;\n}\n\n/**\n * Generic API response wrapper\n */\nexport interface ApiResponse<T> {\n success: boolean;\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\n/**\n * SDK error codes\n */\nexport const SDKErrorCode = {\n NETWORK_ERROR: \"NETWORK_ERROR\",\n AUTHENTICATION_ERROR: \"AUTHENTICATION_ERROR\",\n AUTHORIZATION_ERROR: \"AUTHORIZATION_ERROR\",\n INSUFFICIENT_CREDITS: \"INSUFFICIENT_CREDITS\",\n RESERVATION_NOT_FOUND: \"RESERVATION_NOT_FOUND\",\n RESERVATION_EXPIRED: \"RESERVATION_EXPIRED\",\n VALIDATION_ERROR: \"VALIDATION_ERROR\",\n SERVER_ERROR: \"SERVER_ERROR\",\n UNKNOWN_ERROR: \"UNKNOWN_ERROR\",\n} as const;\n\nexport type SDKErrorCodeType = (typeof SDKErrorCode)[keyof typeof SDKErrorCode];\n","/**\n * SDK error classes\n *\n * Provides typed error handling for SDK operations.\n */\n\nimport { SDKErrorCode, type SDKErrorCodeType } from \"./types.js\";\n\n/**\n * Base error class for SDK errors\n */\nexport class CreditsSDKError extends Error {\n public readonly code: SDKErrorCodeType;\n public readonly details?: Record<string, unknown>;\n public readonly statusCode?: number;\n\n constructor(\n message: string,\n code: SDKErrorCodeType,\n options?: {\n details?: Record<string, unknown>;\n statusCode?: number;\n cause?: Error;\n }\n ) {\n super(message, { cause: options?.cause });\n this.name = \"CreditsSDKError\";\n this.code = code;\n this.details = options?.details;\n this.statusCode = options?.statusCode;\n }\n\n /**\n * Check if this is a specific error type\n */\n is(code: SDKErrorCodeType): boolean {\n return this.code === code;\n }\n\n /**\n * Create a JSON representation for logging\n */\n toJSON(): Record<string, unknown> {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n details: this.details,\n statusCode: this.statusCode,\n };\n }\n}\n\n/**\n * Error thrown when a network request fails\n */\nexport class NetworkError extends CreditsSDKError {\n constructor(message: string, cause?: Error) {\n super(message, SDKErrorCode.NETWORK_ERROR, { cause });\n this.name = \"NetworkError\";\n }\n}\n\n/**\n * Error thrown when authentication fails\n */\nexport class AuthenticationError extends CreditsSDKError {\n constructor(message: string = \"Authentication required\") {\n super(message, SDKErrorCode.AUTHENTICATION_ERROR, { statusCode: 401 });\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Error thrown when authorization fails\n */\nexport class AuthorizationError extends CreditsSDKError {\n constructor(message: string = \"Access denied\") {\n super(message, SDKErrorCode.AUTHORIZATION_ERROR, { statusCode: 403 });\n this.name = \"AuthorizationError\";\n }\n}\n\n/**\n * Error thrown when credits are insufficient\n */\nexport class InsufficientCreditsError extends CreditsSDKError {\n public readonly available: number;\n public readonly required: number;\n\n constructor(available: number, required: number) {\n super(\n `Insufficient credits: available ${available}, required ${required}`,\n SDKErrorCode.INSUFFICIENT_CREDITS,\n {\n statusCode: 402,\n details: { available, required },\n }\n );\n this.name = \"InsufficientCreditsError\";\n this.available = available;\n this.required = required;\n }\n}\n\n/**\n * Error thrown when a reservation is not found\n */\nexport class ReservationNotFoundError extends CreditsSDKError {\n public readonly reservationId: string;\n\n constructor(reservationId: string) {\n super(\n `Reservation not found: ${reservationId}`,\n SDKErrorCode.RESERVATION_NOT_FOUND,\n {\n statusCode: 404,\n details: { reservationId },\n }\n );\n this.name = \"ReservationNotFoundError\";\n this.reservationId = reservationId;\n }\n}\n\n/**\n * Error thrown when a reservation has expired\n */\nexport class ReservationExpiredError extends CreditsSDKError {\n public readonly reservationId: string;\n\n constructor(reservationId: string) {\n super(\n `Reservation expired: ${reservationId}`,\n SDKErrorCode.RESERVATION_EXPIRED,\n {\n statusCode: 410,\n details: { reservationId },\n }\n );\n this.name = \"ReservationExpiredError\";\n this.reservationId = reservationId;\n }\n}\n\n/**\n * Error thrown for validation failures\n */\nexport class ValidationError extends CreditsSDKError {\n public readonly field?: string;\n\n constructor(message: string, field?: string) {\n super(message, SDKErrorCode.VALIDATION_ERROR, {\n statusCode: 400,\n details: field ? { field } : undefined,\n });\n this.name = \"ValidationError\";\n this.field = field;\n }\n}\n\n/**\n * Error thrown for server errors\n */\nexport class ServerError extends CreditsSDKError {\n constructor(message: string = \"Internal server error\") {\n super(message, SDKErrorCode.SERVER_ERROR, { statusCode: 500 });\n this.name = \"ServerError\";\n }\n}\n\n/**\n * Parse an API error response into the appropriate SDK error\n */\nexport function parseApiError(\n statusCode: number,\n body: { error?: { code?: string; message?: string; details?: unknown } }\n): CreditsSDKError {\n const errorCode = body.error?.code || \"\";\n const errorMessage = body.error?.message || \"An error occurred\";\n const details = body.error?.details as Record<string, unknown> | undefined;\n\n switch (statusCode) {\n case 401:\n return new AuthenticationError(errorMessage);\n case 403:\n return new AuthorizationError(errorMessage);\n case 402:\n // Parse insufficient credits details\n if (details?.available !== undefined && details?.required !== undefined) {\n return new InsufficientCreditsError(\n details.available as number,\n details.required as number\n );\n }\n return new CreditsSDKError(errorMessage, SDKErrorCode.INSUFFICIENT_CREDITS, {\n statusCode,\n details,\n });\n case 404:\n if (errorCode === \"RESERVATION_NOT_FOUND\" && details?.reservationId) {\n return new ReservationNotFoundError(details.reservationId as string);\n }\n return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_NOT_FOUND, {\n statusCode,\n details,\n });\n case 410:\n if (errorCode === \"RESERVATION_EXPIRED\" && details?.reservationId) {\n return new ReservationExpiredError(details.reservationId as string);\n }\n return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_EXPIRED, {\n statusCode,\n details,\n });\n case 400:\n case 422:\n return new ValidationError(errorMessage, details?.field as string | undefined);\n case 500:\n default:\n if (statusCode >= 500) {\n return new ServerError(errorMessage);\n }\n return new CreditsSDKError(errorMessage, SDKErrorCode.UNKNOWN_ERROR, {\n statusCode,\n details,\n });\n }\n}\n","/**\n * CreditsClient - REST API client for the credits system\n *\n * Provides a type-safe interface for consuming credits API endpoints\n * from external services or other applications.\n *\n * @example\n * ```typescript\n * const client = new CreditsClient({\n * baseUrl: process.env.CREDITS_API_URL,\n * getAuthToken: async () => session.accessToken,\n * });\n *\n * // Check if user has sufficient credits\n * const check = await client.checkAvailability(\"story_generation\");\n * if (!check.hasCredits) {\n * console.log(\"Insufficient credits\");\n * return;\n * }\n *\n * // Two-phase commit pattern\n * const reservation = await client.reserve(\"story_generation\");\n * try {\n * const result = await doExpensiveWork();\n * await client.commit(reservation.reservationId);\n * return result;\n * } catch (error) {\n * await client.release(reservation.reservationId);\n * throw error;\n * }\n * ```\n */\n\nimport type {\n CreditsClientConfig,\n UserCredits,\n CreditCheckResult,\n ReservationResult,\n UsageHistoryResponse,\n PaginationOptions,\n} from \"./types.js\";\nimport { parseApiError, NetworkError } from \"./errors.js\";\n\nexport class CreditsClient {\n private readonly baseUrl: string;\n private readonly getAuthToken?: () => Promise<string>;\n private readonly fetchFn: typeof fetch;\n\n constructor(config: CreditsClientConfig) {\n // Ensure baseUrl doesn't have trailing slash\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n this.getAuthToken = config.getAuthToken;\n this.fetchFn = config.fetch ?? fetch;\n }\n\n /**\n * Make an authenticated request to the API\n */\n private async request<T>(\n path: string,\n options: {\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n params?: Record<string, string | number | undefined>;\n } = {}\n ): Promise<T> {\n const { method = \"GET\", body, params } = options;\n\n // Build URL with query params\n let url = `${this.baseUrl}${path}`;\n if (params) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n searchParams.set(key, String(value));\n }\n }\n const queryString = searchParams.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n }\n\n // Build headers\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n // Add auth token if available\n if (this.getAuthToken) {\n try {\n const token = await this.getAuthToken();\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n } catch {\n // Auth token not available, proceed without it\n }\n }\n\n // Make the request\n let response: Response;\n try {\n response = await this.fetchFn(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (error) {\n throw new NetworkError(\n error instanceof Error ? error.message : \"Network request failed\",\n error instanceof Error ? error : undefined\n );\n }\n\n // Parse response\n const responseBody = (await response.json()) as {\n data?: T;\n error?: { code?: string; message?: string; details?: unknown };\n };\n\n // Handle errors\n if (!response.ok) {\n throw parseApiError(response.status, responseBody);\n }\n\n // Return data\n return responseBody.data as T;\n }\n\n /**\n * Get the current user's credit balance\n *\n * @returns User's credit information\n * @throws AuthenticationError if not authenticated\n */\n async getBalance(): Promise<UserCredits> {\n return this.request<UserCredits>(\"/api/v1/credits/balance\");\n }\n\n /**\n * Check if the user has sufficient credits for an operation\n *\n * @param operationType - Type of operation to check\n * @param customCost - Optional custom cost to check against\n * @returns Credit availability status\n * @throws AuthenticationError if not authenticated\n */\n async checkAvailability(\n operationType: string,\n customCost?: number\n ): Promise<CreditCheckResult> {\n return this.request<CreditCheckResult>(\"/api/v1/credits/availability\", {\n params: {\n operationType,\n customCost: customCost?.toString(),\n },\n });\n }\n\n /**\n * Reserve credits for an operation\n *\n * This is the first step in the two-phase commit pattern.\n * Reserved credits are held for 5 minutes before expiring.\n *\n * @param operationType - Type of operation\n * @param options - Optional reservation options\n * @returns Reservation details including ID and expiry\n * @throws InsufficientCreditsError if not enough credits\n * @throws AuthenticationError if not authenticated\n */\n async reserve(\n operationType: string,\n options?: {\n customCost?: number;\n resourceId?: string;\n resourceType?: string;\n }\n ): Promise<ReservationResult> {\n return this.request<ReservationResult>(\"/api/v1/credits/reserve\", {\n method: \"POST\",\n body: {\n operationType,\n ...options,\n },\n });\n }\n\n /**\n * Commit a reservation (deduct credits)\n *\n * This is the second step in the two-phase commit pattern.\n * Call this after your operation succeeds.\n *\n * @param reservationId - ID of the reservation to commit\n * @throws ReservationNotFoundError if reservation doesn't exist\n * @throws ReservationExpiredError if reservation has expired\n * @throws AuthenticationError if not authenticated\n */\n async commit(reservationId: string): Promise<void> {\n await this.request<{ message: string }>(\"/api/v1/credits/commit\", {\n method: \"POST\",\n body: { reservationId },\n });\n }\n\n /**\n * Release a reservation (refund credits)\n *\n * Call this if your operation fails after reserving credits.\n *\n * @param reservationId - ID of the reservation to release\n * @throws ReservationNotFoundError if reservation doesn't exist\n * @throws AuthenticationError if not authenticated\n */\n async release(reservationId: string): Promise<void> {\n await this.request<{ message: string }>(\"/api/v1/credits/release\", {\n method: \"POST\",\n body: { reservationId },\n });\n }\n\n /**\n * Get usage history for the current user\n *\n * @param options - Optional pagination options\n * @returns Paginated list of usage entries\n * @throws AuthenticationError if not authenticated\n */\n async getHistory(options?: PaginationOptions): Promise<UsageHistoryResponse> {\n return this.request<UsageHistoryResponse>(\"/api/v1/credits/history\", {\n params: {\n page: options?.page,\n limit: options?.limit,\n },\n });\n }\n\n /**\n * Get the available credits (balance + bonus - reserved)\n *\n * Convenience method that calculates available credits from balance.\n *\n * @returns Number of available credits\n * @throws AuthenticationError if not authenticated\n */\n async getAvailableCredits(): Promise<number> {\n const balance = await this.getBalance();\n return balance.balance + balance.bonusCredits - balance.reserved;\n }\n\n /**\n * Execute an operation with automatic credit handling\n *\n * This is a convenience method that handles the full two-phase commit\n * pattern automatically.\n *\n * @param operationType - Type of operation\n * @param operation - The operation to execute\n * @param options - Optional reservation options\n * @returns Result of the operation\n * @throws InsufficientCreditsError if not enough credits\n * @throws AuthenticationError if not authenticated\n *\n * @example\n * ```typescript\n * const result = await client.withCredits(\n * \"story_generation\",\n * async () => {\n * return generateStory(data);\n * }\n * );\n * ```\n */\n async withCredits<T>(\n operationType: string,\n operation: () => Promise<T>,\n options?: {\n customCost?: number;\n resourceId?: string;\n resourceType?: string;\n }\n ): Promise<T> {\n const reservation = await this.reserve(operationType, options);\n\n try {\n const result = await operation();\n await this.commit(reservation.reservationId);\n return result;\n } catch (error) {\n // Always attempt to release on failure\n try {\n await this.release(reservation.reservationId);\n } catch {\n // Ignore release errors, original error is more important\n }\n throw error;\n }\n }\n}\n","/**\n * AdminCreditsClient - Admin REST API client for the credits system\n *\n * Provides administrative access to the credits system for managing\n * user credits, configurations, and subscriptions.\n *\n * @example\n * ```typescript\n * const adminClient = new AdminCreditsClient({\n * baseUrl: process.env.CREDITS_API_URL,\n * apiKey: process.env.CREDITS_ADMIN_API_KEY,\n * });\n *\n * // Add credits to a user\n * await adminClient.addCredits(\"user-123\", 100, \"Support credit grant\");\n *\n * // Update subscription tier\n * await adminClient.updateSubscription(\"user-123\", \"premium\", \"2027-01-01T00:00:00Z\");\n *\n * // Update operation costs\n * await adminClient.updateCosts({\n * story_generation: 8,\n * image_generation: 12,\n * });\n * ```\n */\n\nimport type { AdminCreditsClientConfig, UserCredits } from \"./types.js\";\nimport { parseApiError, NetworkError } from \"./errors.js\";\n\n/**\n * Credits configuration returned by the API\n */\nexport interface CreditsConfig {\n operationCosts: Record<string, number>;\n tierConfigs: Record<\n string,\n {\n monthlyLimit: number;\n features: string[];\n }\n >;\n}\n\n/**\n * Response from listing users\n */\nexport interface ListUsersResponse {\n users: Array<{\n userId: string;\n balance: number;\n bonusCredits?: number;\n tier: string;\n monthlyUsed?: number;\n }>;\n hasMore: boolean;\n total?: number;\n}\n\n/**\n * Options for listing users\n */\nexport interface ListUsersOptions {\n page?: number;\n limit?: number;\n tier?: string;\n search?: string;\n}\n\nexport class AdminCreditsClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n private readonly fetchFn: typeof fetch;\n\n constructor(config: AdminCreditsClientConfig) {\n // Ensure baseUrl doesn't have trailing slash\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n this.apiKey = config.apiKey;\n this.fetchFn = config.fetch ?? fetch;\n }\n\n /**\n * Make an authenticated admin request to the API\n */\n private async request<T>(\n path: string,\n options: {\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n body?: unknown;\n params?: Record<string, string | number | undefined>;\n } = {}\n ): Promise<T> {\n const { method = \"GET\", body, params } = options;\n\n // Build URL with query params\n let url = `${this.baseUrl}${path}`;\n if (params) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n searchParams.set(key, String(value));\n }\n }\n const queryString = searchParams.toString();\n if (queryString) {\n url += `?${queryString}`;\n }\n }\n\n // Build headers with admin API key\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n };\n\n // Make the request\n let response: Response;\n try {\n response = await this.fetchFn(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch (error) {\n throw new NetworkError(\n error instanceof Error ? error.message : \"Network request failed\",\n error instanceof Error ? error : undefined\n );\n }\n\n // Parse response\n const responseBody = (await response.json()) as {\n data?: T;\n error?: { code?: string; message?: string; details?: unknown };\n };\n\n // Handle errors\n if (!response.ok) {\n throw parseApiError(response.status, responseBody);\n }\n\n // Return data\n return responseBody.data as T;\n }\n\n /**\n * Get a specific user's credits\n *\n * @param userId - ID of the user\n * @returns User's credit information\n * @throws AuthorizationError if API key is invalid\n */\n async getUserCredits(userId: string): Promise<UserCredits> {\n return this.request<UserCredits>(`/api/admin/credits/users/${userId}`);\n }\n\n /**\n * Add credits to a user's account\n *\n * Credits are added to bonusCredits, which don't expire on monthly reset.\n *\n * @param userId - ID of the user\n * @param amount - Amount of credits to add\n * @param description - Reason for adding credits\n * @throws AuthorizationError if API key is invalid\n */\n async addCredits(\n userId: string,\n amount: number,\n description: string\n ): Promise<void> {\n await this.request<{ message: string; newBalance: number }>(\n `/api/admin/credits/users/${userId}/add`,\n {\n method: \"POST\",\n body: { amount, description },\n }\n );\n }\n\n /**\n * Update a user's subscription tier\n *\n * @param userId - ID of the user\n * @param tier - New subscription tier\n * @param expiresAt - Optional expiration date (ISO 8601)\n * @throws AuthorizationError if API key is invalid\n */\n async updateSubscription(\n userId: string,\n tier: string,\n expiresAt?: string\n ): Promise<void> {\n const body: { tier: string; expiresAt?: string } = { tier };\n if (expiresAt) {\n body.expiresAt = expiresAt;\n }\n\n await this.request<{ message: string }>(\n `/api/admin/credits/users/${userId}/subscription`,\n {\n method: \"PUT\",\n body,\n }\n );\n }\n\n /**\n * Get the current credits configuration\n *\n * @returns Credits system configuration\n * @throws AuthorizationError if API key is invalid\n */\n async getConfig(): Promise<CreditsConfig> {\n return this.request<CreditsConfig>(\"/api/admin/credits/config\");\n }\n\n /**\n * Update operation costs\n *\n * @param costs - New operation costs (operation type -> cost)\n * @throws AuthorizationError if API key is invalid\n */\n async updateCosts(costs: Record<string, number>): Promise<void> {\n await this.request<{ message: string }>(\"/api/admin/credits/config/costs\", {\n method: \"PUT\",\n body: { operationCosts: costs },\n });\n }\n\n /**\n * List all users with their credits\n *\n * @param options - Pagination and filtering options\n * @returns Paginated list of users\n * @throws AuthorizationError if API key is invalid\n */\n async listUsers(options?: ListUsersOptions): Promise<ListUsersResponse> {\n return this.request<ListUsersResponse>(\"/api/admin/credits/users\", {\n params: {\n page: options?.page,\n limit: options?.limit,\n tier: options?.tier,\n search: options?.search,\n },\n });\n }\n\n /**\n * Deduct credits from a user's account\n *\n * @param userId - ID of the user\n * @param amount - Amount of credits to deduct\n * @param description - Reason for deducting credits\n * @throws AuthorizationError if API key is invalid\n */\n async deductCredits(\n userId: string,\n amount: number,\n description: string\n ): Promise<void> {\n await this.request<{ message: string; newBalance: number }>(\n `/api/admin/credits/users/${userId}/deduct`,\n {\n method: \"POST\",\n body: { amount, description },\n }\n );\n }\n\n /**\n * Get usage statistics for a user\n *\n * @param userId - ID of the user\n * @param options - Optional date range\n * @returns Usage statistics\n * @throws AuthorizationError if API key is invalid\n */\n async getUserStats(\n userId: string,\n options?: { startDate?: string; endDate?: string }\n ): Promise<{\n totalCreditsUsed: number;\n operationBreakdown: Record<string, number>;\n successRate: number;\n }> {\n return this.request<{\n totalCreditsUsed: number;\n operationBreakdown: Record<string, number>;\n successRate: number;\n }>(`/api/admin/credits/users/${userId}/stats`, {\n params: {\n startDate: options?.startDate,\n endDate: options?.endDate,\n },\n });\n }\n}\n"],"mappings":"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","names":[]}