@nehorai/credits 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/adapters/index.cjs +463 -0
- package/dist/adapters/index.cjs.map +1 -0
- package/dist/adapters/index.d.cts +66 -0
- package/dist/adapters/index.d.ts +66 -3
- package/dist/adapters/index.js +435 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/auth/index.cjs +96 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/{api-key.d.ts → index.d.cts} +9 -6
- package/dist/auth/index.d.ts +37 -5
- package/dist/auth/index.js +65 -7
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.cjs +522 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +119 -0
- package/dist/core/index.d.ts +115 -25
- package/dist/core/index.js +475 -27
- package/dist/core/index.js.map +1 -1
- package/dist/{core/deferred.d.ts → deferred-C7rrRfkm.d.cts} +7 -6
- package/dist/{core/deferred.js → deferred-C7rrRfkm.d.ts} +23 -29
- package/dist/index.cjs +2514 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +437 -0
- package/dist/index.d.ts +429 -50
- package/dist/index.js +2407 -63
- package/dist/index.js.map +1 -1
- package/dist/repository/index.cjs +819 -0
- package/dist/repository/index.cjs.map +1 -0
- package/dist/repository/{memory/index.d.ts → index.d.cts} +35 -5
- package/dist/repository/index.d.ts +97 -7
- package/dist/repository/index.js +786 -10
- package/dist/repository/index.js.map +1 -1
- package/dist/sdk/index.cjs +608 -0
- package/dist/sdk/index.cjs.map +1 -0
- package/dist/sdk/index.d.cts +495 -0
- package/dist/sdk/index.d.ts +491 -9
- package/dist/sdk/index.js +568 -12
- package/dist/sdk/index.js.map +1 -1
- package/dist/service/index.cjs +675 -0
- package/dist/service/index.cjs.map +1 -0
- package/dist/service/{credits-service.d.ts → index.d.cts} +25 -8
- package/dist/service/index.d.ts +156 -3
- package/dist/service/index.js +646 -1
- package/dist/service/index.js.map +1 -1
- package/dist/{core/types.d.ts → types-BBxIWHK2.d.cts} +25 -24
- package/dist/types-BBxIWHK2.d.ts +277 -0
- package/dist/types-D1vdeBcA.d.cts +305 -0
- package/dist/{auth/types.d.ts → types-OZU3I5OM.d.cts} +5 -4
- package/dist/types-OZU3I5OM.d.ts +32 -0
- package/dist/{repository/types.d.ts → types-aJsyv8df.d.ts} +35 -13
- package/package.json +21 -19
- package/dist/adapters/generic.d.ts +0 -18
- package/dist/adapters/generic.d.ts.map +0 -1
- package/dist/adapters/generic.js +0 -151
- package/dist/adapters/generic.js.map +0 -1
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/types.d.ts +0 -45
- package/dist/adapters/types.d.ts.map +0 -1
- package/dist/adapters/types.js +0 -8
- package/dist/adapters/types.js.map +0 -1
- package/dist/auth/api-key.d.ts.map +0 -1
- package/dist/auth/api-key.js +0 -94
- package/dist/auth/api-key.js.map +0 -1
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -2
- package/dist/auth/types.js.map +0 -1
- package/dist/config/costs.d.ts +0 -56
- package/dist/config/costs.d.ts.map +0 -1
- package/dist/config/costs.js +0 -71
- package/dist/config/costs.js.map +0 -1
- package/dist/config/index.d.ts +0 -184
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -342
- package/dist/config/index.js.map +0 -1
- package/dist/config/provider.d.ts +0 -12
- package/dist/config/provider.d.ts.map +0 -1
- package/dist/config/provider.js +0 -14
- package/dist/config/provider.js.map +0 -1
- package/dist/core/deferred.d.ts.map +0 -1
- package/dist/core/deferred.js.map +0 -1
- package/dist/core/errors.d.ts +0 -78
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -110
- package/dist/core/errors.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/operations.d.ts +0 -36
- package/dist/core/operations.d.ts.map +0 -1
- package/dist/core/operations.js +0 -88
- package/dist/core/operations.js.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -82
- package/dist/core/types.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/notifications/index.d.ts +0 -76
- package/dist/notifications/index.d.ts.map +0 -1
- package/dist/notifications/index.js +0 -174
- package/dist/notifications/index.js.map +0 -1
- package/dist/repository/index.d.ts.map +0 -1
- package/dist/repository/memory/index.d.ts.map +0 -1
- package/dist/repository/memory/index.js +0 -518
- package/dist/repository/memory/index.js.map +0 -1
- package/dist/repository/types.d.ts.map +0 -1
- package/dist/repository/types.js +0 -39
- package/dist/repository/types.js.map +0 -1
- package/dist/repository/utils.d.ts +0 -22
- package/dist/repository/utils.d.ts.map +0 -1
- package/dist/repository/utils.js +0 -40
- package/dist/repository/utils.js.map +0 -1
- package/dist/sdk/admin-client.d.ts +0 -146
- package/dist/sdk/admin-client.d.ts.map +0 -1
- package/dist/sdk/admin-client.js +0 -196
- package/dist/sdk/admin-client.js.map +0 -1
- package/dist/sdk/client.d.ts +0 -144
- package/dist/sdk/client.d.ts.map +0 -1
- package/dist/sdk/client.js +0 -247
- package/dist/sdk/client.js.map +0 -1
- package/dist/sdk/errors.d.ts +0 -91
- package/dist/sdk/errors.d.ts.map +0 -1
- package/dist/sdk/errors.js +0 -185
- package/dist/sdk/errors.js.map +0 -1
- package/dist/sdk/index.d.ts.map +0 -1
- package/dist/sdk/types.d.ts +0 -110
- package/dist/sdk/types.d.ts.map +0 -1
- package/dist/sdk/types.js +0 -20
- package/dist/sdk/types.js.map +0 -1
- package/dist/service/credits-service.d.ts.map +0 -1
- package/dist/service/credits-service.js +0 -368
- package/dist/service/credits-service.js.map +0 -1
- package/dist/service/index.d.ts.map +0 -1
- package/dist/utils/error-utils.d.ts +0 -48
- package/dist/utils/error-utils.d.ts.map +0 -1
- package/dist/utils/error-utils.js +0 -57
- package/dist/utils/error-utils.js.map +0 -1
- package/dist/utils/index.d.ts +0 -54
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -72
- package/dist/utils/index.js.map +0 -1
package/dist/sdk/index.js
CHANGED
|
@@ -1,13 +1,569 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
package/dist/sdk/index.js.map
CHANGED
|
@@ -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":[]}
|