@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
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { I as ICreditsAuthProvider, a as CreditsUser } from '../types-OZU3I5OM.cjs';
|
|
2
|
+
export { C as CreditsAuthProviderFactory } from '../types-OZU3I5OM.cjs';
|
|
3
|
+
|
|
2
4
|
/**
|
|
3
5
|
* Verify a Bearer token from an Authorization header
|
|
4
6
|
*
|
|
@@ -6,14 +8,14 @@ import type { ICreditsAuthProvider, CreditsUser } from "./types.js";
|
|
|
6
8
|
* @param secret - The expected secret value
|
|
7
9
|
* @returns True if token is valid
|
|
8
10
|
*/
|
|
9
|
-
|
|
11
|
+
declare function verifyBearerToken(header: string | null, secret: string): boolean;
|
|
10
12
|
/**
|
|
11
13
|
* API Key auth provider for admin routes
|
|
12
14
|
*
|
|
13
15
|
* Uses Bearer token authentication for external API access
|
|
14
16
|
* The API key is stored in CREDITS_ADMIN_API_KEY environment variable
|
|
15
17
|
*/
|
|
16
|
-
|
|
18
|
+
declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
|
|
17
19
|
private readonly apiKey;
|
|
18
20
|
private isValidated;
|
|
19
21
|
constructor(authHeader?: string | null, apiKey?: string);
|
|
@@ -26,12 +28,13 @@ export declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
|
|
|
26
28
|
* @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)
|
|
27
29
|
* @returns True if API key is valid
|
|
28
30
|
*/
|
|
29
|
-
|
|
31
|
+
declare function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean;
|
|
30
32
|
/**
|
|
31
33
|
* Create an API key auth provider from a request
|
|
32
34
|
* @param request - Request object with headers
|
|
33
35
|
* @param apiKey - Optional API key to use
|
|
34
36
|
* @returns API key auth provider
|
|
35
37
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
declare function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider;
|
|
39
|
+
|
|
40
|
+
export { ApiKeyCreditsProvider, CreditsUser, ICreditsAuthProvider, createApiKeyProvider, verifyAdminApiKey, verifyBearerToken };
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,8 +1,40 @@
|
|
|
1
|
+
import { I as ICreditsAuthProvider, a as CreditsUser } from '../types-OZU3I5OM.js';
|
|
2
|
+
export { C as CreditsAuthProviderFactory } from '../types-OZU3I5OM.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
5
|
+
* Verify a Bearer token from an Authorization header
|
|
3
6
|
*
|
|
4
|
-
*
|
|
7
|
+
* @param header - The Authorization header value (e.g., "Bearer secret123")
|
|
8
|
+
* @param secret - The expected secret value
|
|
9
|
+
* @returns True if token is valid
|
|
5
10
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
declare function verifyBearerToken(header: string | null, secret: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* API Key auth provider for admin routes
|
|
14
|
+
*
|
|
15
|
+
* Uses Bearer token authentication for external API access
|
|
16
|
+
* The API key is stored in CREDITS_ADMIN_API_KEY environment variable
|
|
17
|
+
*/
|
|
18
|
+
declare class ApiKeyCreditsProvider implements ICreditsAuthProvider {
|
|
19
|
+
private readonly apiKey;
|
|
20
|
+
private isValidated;
|
|
21
|
+
constructor(authHeader?: string | null, apiKey?: string);
|
|
22
|
+
getCurrentUser(): Promise<CreditsUser | null>;
|
|
23
|
+
verifyAdminAccess(): Promise<boolean>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Verify an API key from a request header
|
|
27
|
+
* @param authHeader - Authorization header value
|
|
28
|
+
* @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)
|
|
29
|
+
* @returns True if API key is valid
|
|
30
|
+
*/
|
|
31
|
+
declare function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Create an API key auth provider from a request
|
|
34
|
+
* @param request - Request object with headers
|
|
35
|
+
* @param apiKey - Optional API key to use
|
|
36
|
+
* @returns API key auth provider
|
|
37
|
+
*/
|
|
38
|
+
declare function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider;
|
|
39
|
+
|
|
40
|
+
export { ApiKeyCreditsProvider, CreditsUser, ICreditsAuthProvider, createApiKeyProvider, verifyAdminApiKey, verifyBearerToken };
|
package/dist/auth/index.js
CHANGED
|
@@ -1,8 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
// src/auth/api-key.ts
|
|
2
|
+
function timingSafeEqual(a, b) {
|
|
3
|
+
if (!a || !b) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
const maxLength = Math.max(a.length, b.length);
|
|
7
|
+
const paddedA = a.padEnd(maxLength);
|
|
8
|
+
const paddedB = b.padEnd(maxLength);
|
|
9
|
+
let result = 0;
|
|
10
|
+
for (let i = 0; i < maxLength; i++) {
|
|
11
|
+
result |= paddedA.charCodeAt(i) ^ paddedB.charCodeAt(i);
|
|
12
|
+
}
|
|
13
|
+
return result === 0 && a.length === b.length;
|
|
14
|
+
}
|
|
15
|
+
function verifyBearerToken(header, secret) {
|
|
16
|
+
if (!header || !secret) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const token = header.replace(/^Bearer\s+/i, "");
|
|
20
|
+
return timingSafeEqual(token, secret);
|
|
21
|
+
}
|
|
22
|
+
var ApiKeyCreditsProvider = class {
|
|
23
|
+
apiKey;
|
|
24
|
+
isValidated = false;
|
|
25
|
+
constructor(authHeader, apiKey) {
|
|
26
|
+
this.apiKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;
|
|
27
|
+
if (authHeader && this.apiKey) {
|
|
28
|
+
this.isValidated = verifyBearerToken(authHeader, this.apiKey);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async getCurrentUser() {
|
|
32
|
+
if (!this.isValidated) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
id: "api-admin",
|
|
37
|
+
email: "api@admin.internal",
|
|
38
|
+
name: "API Admin"
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async verifyAdminAccess() {
|
|
42
|
+
return this.isValidated;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
function verifyAdminApiKey(authHeader, apiKey) {
|
|
46
|
+
if (!authHeader) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const expectedKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;
|
|
50
|
+
if (!expectedKey) {
|
|
51
|
+
console.warn("CREDITS_ADMIN_API_KEY not configured");
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return verifyBearerToken(authHeader, expectedKey);
|
|
55
|
+
}
|
|
56
|
+
function createApiKeyProvider(request, apiKey) {
|
|
57
|
+
const authHeader = request.headers.get("Authorization");
|
|
58
|
+
return new ApiKeyCreditsProvider(authHeader, apiKey);
|
|
59
|
+
}
|
|
60
|
+
export {
|
|
61
|
+
ApiKeyCreditsProvider,
|
|
62
|
+
createApiKeyProvider,
|
|
63
|
+
verifyAdminApiKey,
|
|
64
|
+
verifyBearerToken
|
|
65
|
+
};
|
|
8
66
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"
|
|
1
|
+
{"version":3,"sources":["../../src/auth/api-key.ts"],"sourcesContent":["import type { ICreditsAuthProvider, CreditsUser } from \"./types.js\";\n\n/**\n * Timing-safe string comparison to prevent timing attacks\n */\nfunction timingSafeEqual(a: string, b: string): boolean {\n if (!a || !b) {\n return false;\n }\n\n // Pad strings to same length to prevent length-based timing attacks\n const maxLength = Math.max(a.length, b.length);\n const paddedA = a.padEnd(maxLength);\n const paddedB = b.padEnd(maxLength);\n\n let result = 0;\n for (let i = 0; i < maxLength; i++) {\n result |= paddedA.charCodeAt(i) ^ paddedB.charCodeAt(i);\n }\n\n // Also check that lengths were originally equal\n return result === 0 && a.length === b.length;\n}\n\n/**\n * Verify a Bearer token from an Authorization header\n *\n * @param header - The Authorization header value (e.g., \"Bearer secret123\")\n * @param secret - The expected secret value\n * @returns True if token is valid\n */\nexport function verifyBearerToken(header: string | null, secret: string): boolean {\n if (!header || !secret) {\n return false;\n }\n\n // Extract token from \"Bearer {token}\" format\n const token = header.replace(/^Bearer\\s+/i, \"\");\n\n // Use timing-safe comparison\n return timingSafeEqual(token, secret);\n}\n\n/**\n * API Key auth provider for admin routes\n *\n * Uses Bearer token authentication for external API access\n * The API key is stored in CREDITS_ADMIN_API_KEY environment variable\n */\nexport class ApiKeyCreditsProvider implements ICreditsAuthProvider {\n private readonly apiKey: string | undefined;\n private isValidated = false;\n\n constructor(authHeader?: string | null, apiKey?: string) {\n this.apiKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\n\n // Validate the provided auth header using timing-safe comparison\n if (authHeader && this.apiKey) {\n this.isValidated = verifyBearerToken(authHeader, this.apiKey);\n }\n }\n\n async getCurrentUser(): Promise<CreditsUser | null> {\n if (!this.isValidated) {\n return null;\n }\n\n // API key auth returns a special \"admin\" user\n return {\n id: \"api-admin\",\n email: \"api@admin.internal\",\n name: \"API Admin\",\n };\n }\n\n async verifyAdminAccess(): Promise<boolean> {\n return this.isValidated;\n }\n}\n\n/**\n * Verify an API key from a request header\n * @param authHeader - Authorization header value\n * @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)\n * @returns True if API key is valid\n */\nexport function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean {\n if (!authHeader) {\n return false;\n }\n\n const expectedKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\n if (!expectedKey) {\n console.warn(\"CREDITS_ADMIN_API_KEY not configured\");\n return false;\n }\n\n // Use timing-safe comparison to prevent timing attacks\n return verifyBearerToken(authHeader, expectedKey);\n}\n\n/**\n * Create an API key auth provider from a request\n * @param request - Request object with headers\n * @param apiKey - Optional API key to use\n * @returns API key auth provider\n */\nexport function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider {\n const authHeader = request.headers.get(\"Authorization\");\n return new ApiKeyCreditsProvider(authHeader, apiKey);\n}\n"],"mappings":";AAKA,SAAS,gBAAgB,GAAW,GAAoB;AACtD,MAAI,CAAC,KAAK,CAAC,GAAG;AACZ,WAAO;AAAA,EACT;AAGA,QAAM,YAAY,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AAC7C,QAAM,UAAU,EAAE,OAAO,SAAS;AAClC,QAAM,UAAU,EAAE,OAAO,SAAS;AAElC,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,cAAU,QAAQ,WAAW,CAAC,IAAI,QAAQ,WAAW,CAAC;AAAA,EACxD;AAGA,SAAO,WAAW,KAAK,EAAE,WAAW,EAAE;AACxC;AASO,SAAS,kBAAkB,QAAuB,QAAyB;AAChF,MAAI,CAAC,UAAU,CAAC,QAAQ;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,OAAO,QAAQ,eAAe,EAAE;AAG9C,SAAO,gBAAgB,OAAO,MAAM;AACtC;AAQO,IAAM,wBAAN,MAA4D;AAAA,EAChD;AAAA,EACT,cAAc;AAAA,EAEtB,YAAY,YAA4B,QAAiB;AACvD,SAAK,SAAS,UAAU,QAAQ,IAAI;AAGpC,QAAI,cAAc,KAAK,QAAQ;AAC7B,WAAK,cAAc,kBAAkB,YAAY,KAAK,MAAM;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,MAAM,iBAA8C;AAClD,QAAI,CAAC,KAAK,aAAa;AACrB,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,MAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,oBAAsC;AAC1C,WAAO,KAAK;AAAA,EACd;AACF;AAQO,SAAS,kBAAkB,YAA2B,QAA0B;AACrF,MAAI,CAAC,YAAY;AACf,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,UAAU,QAAQ,IAAI;AAC1C,MAAI,CAAC,aAAa;AAChB,YAAQ,KAAK,sCAAsC;AACnD,WAAO;AAAA,EACT;AAGA,SAAO,kBAAkB,YAAY,WAAW;AAClD;AAQO,SAAS,qBAAqB,SAAkB,QAAwC;AAC7F,QAAM,aAAa,QAAQ,QAAQ,IAAI,eAAe;AACtD,SAAO,IAAI,sBAAsB,YAAY,MAAM;AACrD;","names":[]}
|
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/core/index.ts
|
|
21
|
+
var core_exports = {};
|
|
22
|
+
__export(core_exports, {
|
|
23
|
+
CreditError: () => CreditError,
|
|
24
|
+
CreditErrorCode: () => CreditErrorCode,
|
|
25
|
+
calculateAvailableCredits: () => calculateAvailableCredits,
|
|
26
|
+
commitReservationWithJournal: () => commitReservationWithJournal,
|
|
27
|
+
createDeferredExecutor: () => createDeferredExecutor,
|
|
28
|
+
createInsufficientCreditsError: () => createInsufficientCreditsError,
|
|
29
|
+
createInvalidOperationTypeError: () => createInvalidOperationTypeError,
|
|
30
|
+
createReservationAlreadyProcessedError: () => createReservationAlreadyProcessedError,
|
|
31
|
+
createReservationExpiredError: () => createReservationExpiredError,
|
|
32
|
+
createReservationNotFoundError: () => createReservationNotFoundError,
|
|
33
|
+
createUserNotFoundError: () => createUserNotFoundError,
|
|
34
|
+
genericDeferred: () => genericDeferred,
|
|
35
|
+
isCreditError: () => isCreditError,
|
|
36
|
+
isInsufficientCreditsError: () => isInsufficientCreditsError,
|
|
37
|
+
noopDeferred: () => noopDeferred,
|
|
38
|
+
releaseReservationWithJournal: () => releaseReservationWithJournal,
|
|
39
|
+
reserveCreditsForOperation: () => reserveCreditsForOperation,
|
|
40
|
+
synchronousDeferred: () => synchronousDeferred,
|
|
41
|
+
toDate: () => toDate,
|
|
42
|
+
toPortableTimestamp: () => toPortableTimestamp
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(core_exports);
|
|
45
|
+
|
|
46
|
+
// src/core/types.ts
|
|
47
|
+
function calculateAvailableCredits(balance, bonusCredits, reserved) {
|
|
48
|
+
return balance + bonusCredits - reserved;
|
|
49
|
+
}
|
|
50
|
+
function toPortableTimestamp(value) {
|
|
51
|
+
if (!value) {
|
|
52
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
if (value instanceof Date) {
|
|
58
|
+
return value.toISOString();
|
|
59
|
+
}
|
|
60
|
+
if (typeof value === "object" && value !== null && "toDate" in value && typeof value.toDate === "function") {
|
|
61
|
+
return value.toDate().toISOString();
|
|
62
|
+
}
|
|
63
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
64
|
+
}
|
|
65
|
+
function toDate(value) {
|
|
66
|
+
if (value instanceof Date) {
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === "string") {
|
|
70
|
+
return new Date(value);
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === "object" && value !== null && "toDate" in value && typeof value.toDate === "function") {
|
|
73
|
+
return value.toDate();
|
|
74
|
+
}
|
|
75
|
+
return /* @__PURE__ */ new Date();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/core/errors.ts
|
|
79
|
+
var CreditErrorCode = {
|
|
80
|
+
INSUFFICIENT_CREDITS: "INSUFFICIENT_CREDITS",
|
|
81
|
+
RESERVATION_NOT_FOUND: "RESERVATION_NOT_FOUND",
|
|
82
|
+
RESERVATION_EXPIRED: "RESERVATION_EXPIRED",
|
|
83
|
+
RESERVATION_ALREADY_PROCESSED: "RESERVATION_ALREADY_PROCESSED",
|
|
84
|
+
USER_NOT_FOUND: "USER_NOT_FOUND",
|
|
85
|
+
INVALID_OPERATION_TYPE: "INVALID_OPERATION_TYPE",
|
|
86
|
+
CONFIGURATION_ERROR: "CONFIGURATION_ERROR",
|
|
87
|
+
DATABASE_ERROR: "DATABASE_ERROR"
|
|
88
|
+
};
|
|
89
|
+
var CreditError = class _CreditError extends Error {
|
|
90
|
+
code;
|
|
91
|
+
details;
|
|
92
|
+
constructor(message, code, details) {
|
|
93
|
+
super(message);
|
|
94
|
+
this.name = "CreditError";
|
|
95
|
+
this.code = code;
|
|
96
|
+
this.details = details;
|
|
97
|
+
if (Error.captureStackTrace) {
|
|
98
|
+
Error.captureStackTrace(this, _CreditError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Convert to JSON-serializable object
|
|
103
|
+
*/
|
|
104
|
+
toJSON() {
|
|
105
|
+
return {
|
|
106
|
+
name: this.name,
|
|
107
|
+
code: this.code,
|
|
108
|
+
message: this.message,
|
|
109
|
+
...this.details && { details: this.details }
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
function isCreditError(error) {
|
|
114
|
+
return error instanceof CreditError;
|
|
115
|
+
}
|
|
116
|
+
function isInsufficientCreditsError(error) {
|
|
117
|
+
if (isCreditError(error)) {
|
|
118
|
+
return error.code === CreditErrorCode.INSUFFICIENT_CREDITS;
|
|
119
|
+
}
|
|
120
|
+
if (error instanceof Error) {
|
|
121
|
+
return error.message.toLowerCase().includes("insufficient credits");
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
function createInsufficientCreditsError(required, available) {
|
|
126
|
+
return new CreditError(
|
|
127
|
+
`Insufficient credits: available ${available}, required ${required}`,
|
|
128
|
+
CreditErrorCode.INSUFFICIENT_CREDITS,
|
|
129
|
+
{ required, available, shortfall: required - available }
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
function createReservationNotFoundError(reservationId) {
|
|
133
|
+
return new CreditError(
|
|
134
|
+
`Reservation ${reservationId} not found`,
|
|
135
|
+
CreditErrorCode.RESERVATION_NOT_FOUND,
|
|
136
|
+
{ reservationId }
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function createReservationExpiredError(reservationId) {
|
|
140
|
+
return new CreditError(
|
|
141
|
+
`Reservation ${reservationId} has expired`,
|
|
142
|
+
CreditErrorCode.RESERVATION_EXPIRED,
|
|
143
|
+
{ reservationId }
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function createReservationAlreadyProcessedError(reservationId, status) {
|
|
147
|
+
return new CreditError(
|
|
148
|
+
`Reservation ${reservationId} has already been ${status}`,
|
|
149
|
+
CreditErrorCode.RESERVATION_ALREADY_PROCESSED,
|
|
150
|
+
{ reservationId, status }
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function createUserNotFoundError(userId) {
|
|
154
|
+
return new CreditError(
|
|
155
|
+
`User ${userId} not found`,
|
|
156
|
+
CreditErrorCode.USER_NOT_FOUND,
|
|
157
|
+
{ userId }
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function createInvalidOperationTypeError(operationType, validTypes) {
|
|
161
|
+
return new CreditError(
|
|
162
|
+
`Invalid operation type: ${operationType}. Valid types: ${validTypes.join(", ")}`,
|
|
163
|
+
CreditErrorCode.INVALID_OPERATION_TYPE,
|
|
164
|
+
{ operationType, validTypes }
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/core/deferred.ts
|
|
169
|
+
var genericDeferred = {
|
|
170
|
+
defer(fn) {
|
|
171
|
+
const scheduler = typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
|
|
172
|
+
scheduler(() => {
|
|
173
|
+
fn().catch((error) => {
|
|
174
|
+
console.error("[Credits] Deferred task error:", error);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
var synchronousDeferred = {
|
|
180
|
+
defer(fn) {
|
|
181
|
+
fn().catch((error) => {
|
|
182
|
+
console.error("[Credits] Deferred task error:", error);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
function createDeferredExecutor(schedule) {
|
|
187
|
+
return {
|
|
188
|
+
defer: schedule
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
var noopDeferred = {
|
|
192
|
+
defer(_fn) {
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// src/config/index.ts
|
|
197
|
+
var import_zod = require("zod");
|
|
198
|
+
|
|
199
|
+
// src/config/provider.ts
|
|
200
|
+
var configProvider = null;
|
|
201
|
+
function getConfigProvider() {
|
|
202
|
+
return configProvider;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/config/index.ts
|
|
206
|
+
var tierConfigSchema = import_zod.z.object({
|
|
207
|
+
tier: import_zod.z.enum(["free", "basic", "premium", "unlimited"]),
|
|
208
|
+
monthlyCredits: import_zod.z.number().min(0),
|
|
209
|
+
priceUsd: import_zod.z.number().min(0),
|
|
210
|
+
features: import_zod.z.array(import_zod.z.string())
|
|
211
|
+
});
|
|
212
|
+
var creditSystemConfigSchema = import_zod.z.object({
|
|
213
|
+
/**
|
|
214
|
+
* Operation costs in credits
|
|
215
|
+
* Keys are operation type names (any non-empty string)
|
|
216
|
+
* Values are positive numbers representing credit cost
|
|
217
|
+
*/
|
|
218
|
+
operationCosts: import_zod.z.record(
|
|
219
|
+
import_zod.z.string().min(1),
|
|
220
|
+
import_zod.z.number().positive()
|
|
221
|
+
),
|
|
222
|
+
/**
|
|
223
|
+
* Display labels for operation types (English canonical labels).
|
|
224
|
+
* Falls back to auto-generating from snake_case keys if not provided.
|
|
225
|
+
*/
|
|
226
|
+
operationLabels: import_zod.z.record(
|
|
227
|
+
import_zod.z.string().min(1),
|
|
228
|
+
import_zod.z.string().min(1)
|
|
229
|
+
).optional().default({}),
|
|
230
|
+
/**
|
|
231
|
+
* Tier configurations
|
|
232
|
+
*/
|
|
233
|
+
tierConfigs: import_zod.z.record(
|
|
234
|
+
import_zod.z.enum(["free", "basic", "premium", "unlimited"]),
|
|
235
|
+
tierConfigSchema
|
|
236
|
+
),
|
|
237
|
+
/**
|
|
238
|
+
* Reservation expiry time in milliseconds
|
|
239
|
+
*/
|
|
240
|
+
reservationExpiryMs: import_zod.z.number().positive().default(5 * 60 * 1e3),
|
|
241
|
+
/**
|
|
242
|
+
* Default credits for new users (free tier)
|
|
243
|
+
*/
|
|
244
|
+
defaultFreeCredits: import_zod.z.number().positive().default(25),
|
|
245
|
+
/**
|
|
246
|
+
* Grace period in days after subscription expires before downgrade
|
|
247
|
+
*/
|
|
248
|
+
subscriptionGracePeriodDays: import_zod.z.number().min(0).default(3),
|
|
249
|
+
/**
|
|
250
|
+
* Low balance notification threshold
|
|
251
|
+
*/
|
|
252
|
+
lowBalanceThreshold: import_zod.z.number().min(0).default(10),
|
|
253
|
+
/**
|
|
254
|
+
* Cooldown in hours between low balance notifications
|
|
255
|
+
*/
|
|
256
|
+
lowBalanceNotificationCooldownHours: import_zod.z.number().positive().default(24),
|
|
257
|
+
/**
|
|
258
|
+
* Feature flags for optional features
|
|
259
|
+
*/
|
|
260
|
+
features: import_zod.z.object({
|
|
261
|
+
journalEntries: import_zod.z.boolean().default(true),
|
|
262
|
+
notifications: import_zod.z.boolean().default(true),
|
|
263
|
+
subscriptionExpiry: import_zod.z.boolean().default(true),
|
|
264
|
+
usageHistory: import_zod.z.boolean().default(true)
|
|
265
|
+
}).default({
|
|
266
|
+
journalEntries: true,
|
|
267
|
+
notifications: true,
|
|
268
|
+
subscriptionExpiry: true,
|
|
269
|
+
usageHistory: true
|
|
270
|
+
})
|
|
271
|
+
});
|
|
272
|
+
var DEFAULT_CONFIG = {
|
|
273
|
+
operationCosts: {
|
|
274
|
+
story_generation: 5,
|
|
275
|
+
conversation: 2,
|
|
276
|
+
image_generation: 10,
|
|
277
|
+
template_generation: 10
|
|
278
|
+
},
|
|
279
|
+
operationLabels: {
|
|
280
|
+
story_generation: "Story generation",
|
|
281
|
+
conversation: "Conversation",
|
|
282
|
+
image_generation: "Image generation",
|
|
283
|
+
template_generation: "Template generation"
|
|
284
|
+
},
|
|
285
|
+
tierConfigs: {
|
|
286
|
+
free: {
|
|
287
|
+
tier: "free",
|
|
288
|
+
monthlyCredits: 25,
|
|
289
|
+
priceUsd: 0,
|
|
290
|
+
features: [
|
|
291
|
+
"25 credits per month",
|
|
292
|
+
"Basic story generation",
|
|
293
|
+
"Standard image quality"
|
|
294
|
+
]
|
|
295
|
+
},
|
|
296
|
+
basic: {
|
|
297
|
+
tier: "basic",
|
|
298
|
+
monthlyCredits: 200,
|
|
299
|
+
priceUsd: 9.99,
|
|
300
|
+
features: [
|
|
301
|
+
"200 credits per month",
|
|
302
|
+
"All story features",
|
|
303
|
+
"High quality images",
|
|
304
|
+
"Priority support"
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
premium: {
|
|
308
|
+
tier: "premium",
|
|
309
|
+
monthlyCredits: 500,
|
|
310
|
+
priceUsd: 19.99,
|
|
311
|
+
features: [
|
|
312
|
+
"500 credits per month",
|
|
313
|
+
"All features",
|
|
314
|
+
"Highest quality images",
|
|
315
|
+
"Early access to new features",
|
|
316
|
+
"Priority support"
|
|
317
|
+
]
|
|
318
|
+
},
|
|
319
|
+
unlimited: {
|
|
320
|
+
tier: "unlimited",
|
|
321
|
+
monthlyCredits: 0,
|
|
322
|
+
// 0 = unlimited
|
|
323
|
+
priceUsd: 49.99,
|
|
324
|
+
features: [
|
|
325
|
+
"Unlimited credits",
|
|
326
|
+
"All features",
|
|
327
|
+
"Highest quality images",
|
|
328
|
+
"Early access to new features",
|
|
329
|
+
"Dedicated support"
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
reservationExpiryMs: 5 * 60 * 1e3,
|
|
334
|
+
// 5 minutes
|
|
335
|
+
defaultFreeCredits: 25,
|
|
336
|
+
subscriptionGracePeriodDays: 3,
|
|
337
|
+
lowBalanceThreshold: 10,
|
|
338
|
+
lowBalanceNotificationCooldownHours: 24,
|
|
339
|
+
features: {
|
|
340
|
+
journalEntries: true,
|
|
341
|
+
notifications: true,
|
|
342
|
+
subscriptionExpiry: true,
|
|
343
|
+
usageHistory: true
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
var currentConfig = DEFAULT_CONFIG;
|
|
347
|
+
function loadConfigFromEnv() {
|
|
348
|
+
const envConfig = {};
|
|
349
|
+
const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;
|
|
350
|
+
if (operationCostsEnv) {
|
|
351
|
+
try {
|
|
352
|
+
const parsed = JSON.parse(operationCostsEnv);
|
|
353
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
354
|
+
envConfig.operationCosts = {
|
|
355
|
+
...DEFAULT_CONFIG.operationCosts,
|
|
356
|
+
...parsed
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
} catch {
|
|
360
|
+
console.warn("[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON");
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
const reservationExpiry = process.env.CREDITS_RESERVATION_EXPIRY_MS;
|
|
364
|
+
if (reservationExpiry) {
|
|
365
|
+
envConfig.reservationExpiryMs = parseInt(reservationExpiry, 10);
|
|
366
|
+
}
|
|
367
|
+
const defaultCredits = process.env.CREDITS_DEFAULT_FREE;
|
|
368
|
+
if (defaultCredits) {
|
|
369
|
+
envConfig.defaultFreeCredits = parseInt(defaultCredits, 10);
|
|
370
|
+
}
|
|
371
|
+
const gracePeriod = process.env.CREDITS_GRACE_PERIOD_DAYS;
|
|
372
|
+
if (gracePeriod) {
|
|
373
|
+
envConfig.subscriptionGracePeriodDays = parseInt(gracePeriod, 10);
|
|
374
|
+
}
|
|
375
|
+
const lowBalanceThreshold = process.env.CREDITS_LOW_BALANCE_THRESHOLD;
|
|
376
|
+
if (lowBalanceThreshold) {
|
|
377
|
+
envConfig.lowBalanceThreshold = parseInt(lowBalanceThreshold, 10);
|
|
378
|
+
}
|
|
379
|
+
const featuresEnv = process.env.CREDITS_FEATURES;
|
|
380
|
+
if (featuresEnv) {
|
|
381
|
+
try {
|
|
382
|
+
const features = JSON.parse(featuresEnv);
|
|
383
|
+
envConfig.features = {
|
|
384
|
+
...DEFAULT_CONFIG.features,
|
|
385
|
+
...features
|
|
386
|
+
};
|
|
387
|
+
} catch {
|
|
388
|
+
console.warn("[Credits Config] Failed to parse CREDITS_FEATURES env var");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return envConfig;
|
|
392
|
+
}
|
|
393
|
+
function initializeConfig(overrides) {
|
|
394
|
+
const envConfig = loadConfigFromEnv();
|
|
395
|
+
const mergedConfig = {
|
|
396
|
+
...DEFAULT_CONFIG,
|
|
397
|
+
...envConfig,
|
|
398
|
+
...overrides,
|
|
399
|
+
operationCosts: {
|
|
400
|
+
...DEFAULT_CONFIG.operationCosts,
|
|
401
|
+
...envConfig.operationCosts,
|
|
402
|
+
...overrides?.operationCosts
|
|
403
|
+
},
|
|
404
|
+
operationLabels: {
|
|
405
|
+
...DEFAULT_CONFIG.operationLabels,
|
|
406
|
+
...envConfig.operationLabels,
|
|
407
|
+
...overrides?.operationLabels
|
|
408
|
+
},
|
|
409
|
+
tierConfigs: {
|
|
410
|
+
...DEFAULT_CONFIG.tierConfigs,
|
|
411
|
+
...envConfig.tierConfigs,
|
|
412
|
+
...overrides?.tierConfigs
|
|
413
|
+
},
|
|
414
|
+
features: {
|
|
415
|
+
...DEFAULT_CONFIG.features,
|
|
416
|
+
...envConfig.features,
|
|
417
|
+
...overrides?.features
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const result = creditSystemConfigSchema.safeParse(mergedConfig);
|
|
421
|
+
if (!result.success) {
|
|
422
|
+
console.error("[Credits Config] Invalid configuration:", result.error.issues);
|
|
423
|
+
throw new Error("Invalid credit system configuration");
|
|
424
|
+
}
|
|
425
|
+
currentConfig = result.data;
|
|
426
|
+
return currentConfig;
|
|
427
|
+
}
|
|
428
|
+
function getConfig() {
|
|
429
|
+
const provider = getConfigProvider();
|
|
430
|
+
if (provider) {
|
|
431
|
+
return provider.getConfig();
|
|
432
|
+
}
|
|
433
|
+
return currentConfig;
|
|
434
|
+
}
|
|
435
|
+
function snakeCaseToTitleCase(str) {
|
|
436
|
+
return str.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
437
|
+
}
|
|
438
|
+
function getOperationLabel(operationType) {
|
|
439
|
+
const labels = getConfig().operationLabels;
|
|
440
|
+
if (labels && operationType in labels) {
|
|
441
|
+
return labels[operationType];
|
|
442
|
+
}
|
|
443
|
+
return snakeCaseToTitleCase(operationType);
|
|
444
|
+
}
|
|
445
|
+
initializeConfig();
|
|
446
|
+
|
|
447
|
+
// src/core/operations.ts
|
|
448
|
+
async function commitReservationWithJournal(repository, userId, reservationId) {
|
|
449
|
+
const reservation = await repository.getReservation(userId, reservationId);
|
|
450
|
+
if (!reservation) {
|
|
451
|
+
throw new Error(`Reservation ${reservationId} not found`);
|
|
452
|
+
}
|
|
453
|
+
await repository.commitReservationAtomic(userId, reservationId);
|
|
454
|
+
const credits = await repository.getUserCredits(userId);
|
|
455
|
+
if (credits) {
|
|
456
|
+
await repository.createJournalEntry({
|
|
457
|
+
userId,
|
|
458
|
+
entryType: "debit",
|
|
459
|
+
amount: reservation.amount,
|
|
460
|
+
balanceAfter: credits.balance,
|
|
461
|
+
source: "operation_commit",
|
|
462
|
+
referenceId: reservationId,
|
|
463
|
+
referenceType: "reservation",
|
|
464
|
+
description: `Committed ${reservation.amount} credits for ${getOperationLabel(reservation.operationType)}`,
|
|
465
|
+
metadata: {
|
|
466
|
+
operationType: reservation.operationType
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
async function releaseReservationWithJournal(repository, userId, reservationId) {
|
|
472
|
+
const reservation = await repository.getReservation(userId, reservationId);
|
|
473
|
+
await repository.releaseReservationAtomic(userId, reservationId);
|
|
474
|
+
if (reservation?.status === "reserved") {
|
|
475
|
+
const credits = await repository.getUserCredits(userId);
|
|
476
|
+
if (credits) {
|
|
477
|
+
await repository.createJournalEntry({
|
|
478
|
+
userId,
|
|
479
|
+
entryType: "credit",
|
|
480
|
+
amount: 0,
|
|
481
|
+
// No actual credits returned (they were reserved, not spent)
|
|
482
|
+
balanceAfter: credits.balance,
|
|
483
|
+
source: "operation_release",
|
|
484
|
+
referenceId: reservationId,
|
|
485
|
+
referenceType: "reservation",
|
|
486
|
+
description: `Released ${reservation.amount} reserved credits for ${getOperationLabel(reservation.operationType)}`,
|
|
487
|
+
metadata: {
|
|
488
|
+
operationType: reservation.operationType,
|
|
489
|
+
amount: reservation.amount
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async function reserveCreditsForOperation(repository, userId, amount, operationType, expiryMs = 5 * 60 * 1e3) {
|
|
496
|
+
const expiresAt = new Date(Date.now() + expiryMs);
|
|
497
|
+
return repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);
|
|
498
|
+
}
|
|
499
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
500
|
+
0 && (module.exports = {
|
|
501
|
+
CreditError,
|
|
502
|
+
CreditErrorCode,
|
|
503
|
+
calculateAvailableCredits,
|
|
504
|
+
commitReservationWithJournal,
|
|
505
|
+
createDeferredExecutor,
|
|
506
|
+
createInsufficientCreditsError,
|
|
507
|
+
createInvalidOperationTypeError,
|
|
508
|
+
createReservationAlreadyProcessedError,
|
|
509
|
+
createReservationExpiredError,
|
|
510
|
+
createReservationNotFoundError,
|
|
511
|
+
createUserNotFoundError,
|
|
512
|
+
genericDeferred,
|
|
513
|
+
isCreditError,
|
|
514
|
+
isInsufficientCreditsError,
|
|
515
|
+
noopDeferred,
|
|
516
|
+
releaseReservationWithJournal,
|
|
517
|
+
reserveCreditsForOperation,
|
|
518
|
+
synchronousDeferred,
|
|
519
|
+
toDate,
|
|
520
|
+
toPortableTimestamp
|
|
521
|
+
});
|
|
522
|
+
//# sourceMappingURL=index.cjs.map
|