clhq-credits-client 1.1.0-alpha.116

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/.versionrc ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "tagPrefix": "clhq-credits-client-"
3
+ }
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # CLHQ Credits Client
2
+
3
+ A TypeScript client for invoking the CLHQ Credits Service Lambda functions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install clhq-credits-client
9
+ # or
10
+ yarn add clhq-credits-client
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { CreditsLambdaClient, OperationType } from 'clhq-credits-client';
17
+
18
+ // Initialize client
19
+ const creditsClient = new CreditsLambdaClient({
20
+ region: 'ap-southeast-2',
21
+ stage: 'dev'
22
+ });
23
+
24
+ // Reserve credits before operation
25
+ const reservation = await creditsClient.reserveCredits({
26
+ userId: 'user-123',
27
+ operationType: OperationType.VIDEO_EXPORT_1080P,
28
+ estimatedCredits: 8,
29
+ operationId: 'video-export-456'
30
+ });
31
+
32
+ // Execute operation...
33
+
34
+ // Finalize credits after completion
35
+ const finalization = await creditsClient.finalizeCredits({
36
+ reservationId: reservation.reservationId,
37
+ actualCredits: 8,
38
+ resultUrl: 'https://s3.amazonaws.com/bucket/video.mp4'
39
+ });
40
+
41
+ // Or use the convenience method
42
+ const result = await creditsClient.executeWithCredits(
43
+ () => performVideoExport(), // Your operation
44
+ {
45
+ userId: 'user-123',
46
+ operationType: OperationType.VIDEO_EXPORT_1080P,
47
+ estimatedCredits: 8,
48
+ operationId: 'video-export-456'
49
+ },
50
+ (exportResult) => ({
51
+ reservationId: reservation.reservationId,
52
+ actualCredits: 8,
53
+ resultUrl: exportResult.url
54
+ })
55
+ );
56
+ ```
57
+
58
+ ## API Methods
59
+
60
+ ### `reserveCredits(params)`
61
+ Reserve credits before starting an operation.
62
+
63
+ ### `finalizeCredits(params)`
64
+ Finalize credit consumption after operation completes.
65
+
66
+ ### `releaseCredits(params)`
67
+ Release reserved credits if operation fails.
68
+
69
+ ### `getBalance(params)`
70
+ Get user's current credit balance.
71
+
72
+ ### `estimateCost(params)`
73
+ Estimate credit cost for an operation.
74
+
75
+ ### `executeWithCredits(operation, reserveParams, finalizeParams)`
76
+ Convenience method that handles the full reserve → execute → finalize flow.
77
+
78
+ ## Operation Types
79
+
80
+ - `VIDEO_EXPORT_720P`, `VIDEO_EXPORT_1080P`, `VIDEO_EXPORT_4K`
81
+ - `BACKGROUND_REMOVAL`, `AI_IMAGE_GENERATION`, `SUBTITLE_GENERATION`
82
+ - `HLS_TRANSCODING`, `REMOTION_RENDER_*` variants
83
+ - And more...
84
+
85
+ ## Configuration
86
+
87
+ The client automatically detects AWS region and stage from environment variables:
88
+
89
+ - `AWS_REGION` (default: 'ap-southeast-2')
90
+ - `STAGE` (default: 'dev')
91
+
92
+ Or you can pass them explicitly to the constructor.
93
+
94
+ ## Error Handling
95
+
96
+ The client throws specific errors:
97
+ - `ResourceNotFoundException`: Credits service not available
98
+ - `TooManyRequestsException`: Rate limited
99
+ - Business logic errors from the credits service
@@ -0,0 +1,45 @@
1
+ import { ReserveCreditsRequest, ReserveCreditsResponse, FinalizeCreditsRequest, FinalizeCreditsResponse, ReleaseCreditsRequest, ReleaseCreditsResponse, GetBalanceRequest, GetBalanceResponse, EstimateCostRequest, EstimateCostResponse } from './types';
2
+ export declare class CreditsLambdaClient {
3
+ private lambda;
4
+ private stage;
5
+ constructor(options?: {
6
+ region?: string;
7
+ stage?: string;
8
+ });
9
+ /**
10
+ * Get the Lambda function name for a specific handler
11
+ */
12
+ private getFunctionName;
13
+ /**
14
+ * Generic method to invoke Lambda functions with proper error handling
15
+ */
16
+ private invoke;
17
+ /**
18
+ * Reserve credits before starting an operation
19
+ */
20
+ reserveCredits(params: ReserveCreditsRequest): Promise<ReserveCreditsResponse>;
21
+ /**
22
+ * Finalize credit consumption after operation completes
23
+ */
24
+ finalizeCredits(params: FinalizeCreditsRequest): Promise<FinalizeCreditsResponse>;
25
+ /**
26
+ * Release reserved credits if operation fails
27
+ */
28
+ releaseCredits(params: ReleaseCreditsRequest): Promise<ReleaseCreditsResponse>;
29
+ /**
30
+ * Get user's credit balance
31
+ */
32
+ getBalance(params: GetBalanceRequest): Promise<GetBalanceResponse>;
33
+ /**
34
+ * Estimate credit cost for an operation
35
+ */
36
+ estimateCost(params: EstimateCostRequest): Promise<EstimateCostResponse>;
37
+ /**
38
+ * Convenience method: Reserve and execute operation with automatic finalization
39
+ */
40
+ executeWithCredits<T>(operation: () => Promise<T>, reserveParams: ReserveCreditsRequest, finalizeParams: (result: T) => FinalizeCreditsRequest): Promise<{
41
+ result: T;
42
+ creditsUsed: number;
43
+ }>;
44
+ }
45
+ //# sourceMappingURL=credits-lambda-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits-lambda-client.d.ts","sourceRoot":"","sources":["../src/credits-lambda-client.ts"],"names":[],"mappings":"AACA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAEjB,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAOzD;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;YACW,MAAM;IAsCpB;;OAEG;IACG,cAAc,CAClB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,sBAAsB,CAAC;IAIlC;;OAEG;IACG,eAAe,CACnB,MAAM,EAAE,sBAAsB,GAC7B,OAAO,CAAC,uBAAuB,CAAC;IAInC;;OAEG;IACG,cAAc,CAClB,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,sBAAsB,CAAC;IAIlC;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIxE;;OAEG;IACG,YAAY,CAChB,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,oBAAoB,CAAC;IAIhC;;OAEG;IACG,kBAAkB,CAAC,CAAC,EACxB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,aAAa,EAAE,qBAAqB,EACpC,cAAc,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,sBAAsB,GACpD,OAAO,CAAC;QAAE,MAAM,EAAE,CAAC,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAyC/C"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CreditsLambdaClient = void 0;
4
+ const client_lambda_1 = require("@aws-sdk/client-lambda");
5
+ class CreditsLambdaClient {
6
+ lambda;
7
+ stage;
8
+ constructor(options) {
9
+ this.lambda = new client_lambda_1.LambdaClient({
10
+ region: options?.region || process.env.AWS_REGION || 'ap-southeast-2',
11
+ });
12
+ this.stage = options?.stage || process.env.STAGE || 'dev';
13
+ }
14
+ /**
15
+ * Get the Lambda function name for a specific handler
16
+ */
17
+ getFunctionName(handler) {
18
+ return `clippy-credits-service-${this.stage}-${handler}`;
19
+ }
20
+ /**
21
+ * Generic method to invoke Lambda functions with proper error handling
22
+ */
23
+ async invoke(handler, payload) {
24
+ const command = new client_lambda_1.InvokeCommand({
25
+ FunctionName: this.getFunctionName(handler),
26
+ Payload: Buffer.from(JSON.stringify(payload)),
27
+ });
28
+ try {
29
+ const response = await this.lambda.send(command);
30
+ if (response.FunctionError) {
31
+ const errorPayload = JSON.parse(Buffer.from(response.Payload).toString());
32
+ throw new Error(errorPayload.errorMessage || 'Lambda invocation failed');
33
+ }
34
+ const result = JSON.parse(Buffer.from(response.Payload).toString());
35
+ if (!result.success && result.error) {
36
+ throw new Error(result.error);
37
+ }
38
+ return result;
39
+ }
40
+ catch (error) {
41
+ // Re-throw Lambda invocation errors
42
+ if (error.name === 'ResourceNotFoundException') {
43
+ throw new Error(`Credits service not available: ${handler}`);
44
+ }
45
+ if (error.name === 'TooManyRequestsException') {
46
+ throw new Error('Credits service rate limited');
47
+ }
48
+ throw error;
49
+ }
50
+ }
51
+ /**
52
+ * Reserve credits before starting an operation
53
+ */
54
+ async reserveCredits(params) {
55
+ return this.invoke('reserve', params);
56
+ }
57
+ /**
58
+ * Finalize credit consumption after operation completes
59
+ */
60
+ async finalizeCredits(params) {
61
+ return this.invoke('finalize', params);
62
+ }
63
+ /**
64
+ * Release reserved credits if operation fails
65
+ */
66
+ async releaseCredits(params) {
67
+ return this.invoke('release', params);
68
+ }
69
+ /**
70
+ * Get user's credit balance
71
+ */
72
+ async getBalance(params) {
73
+ return this.invoke('balance', params);
74
+ }
75
+ /**
76
+ * Estimate credit cost for an operation
77
+ */
78
+ async estimateCost(params) {
79
+ return this.invoke('estimate', params);
80
+ }
81
+ /**
82
+ * Convenience method: Reserve and execute operation with automatic finalization
83
+ */
84
+ async executeWithCredits(operation, reserveParams, finalizeParams) {
85
+ // Reserve credits
86
+ const reservation = await this.reserveCredits(reserveParams);
87
+ if (!reservation.success || !reservation.reservationId) {
88
+ throw new Error(reservation.error || 'Failed to reserve credits');
89
+ }
90
+ try {
91
+ // Execute the operation
92
+ const result = await operation();
93
+ // Finalize credits
94
+ const finalizeRequest = finalizeParams(result);
95
+ finalizeRequest.reservationId = reservation.reservationId;
96
+ const finalization = await this.finalizeCredits(finalizeRequest);
97
+ if (!finalization.success) {
98
+ throw new Error(finalization.error || 'Failed to finalize credits');
99
+ }
100
+ return {
101
+ result,
102
+ creditsUsed: finalization.creditsUsed || 0,
103
+ };
104
+ }
105
+ catch (error) {
106
+ // Release credits on failure
107
+ try {
108
+ await this.releaseCredits({
109
+ reservationId: reservation.reservationId,
110
+ reason: error instanceof Error ? error.message : 'Operation failed',
111
+ });
112
+ }
113
+ catch (releaseError) {
114
+ console.warn('Failed to release credits:', releaseError);
115
+ }
116
+ throw error;
117
+ }
118
+ }
119
+ }
120
+ exports.CreditsLambdaClient = CreditsLambdaClient;
121
+ //# sourceMappingURL=credits-lambda-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credits-lambda-client.js","sourceRoot":"","sources":["../src/credits-lambda-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { CreditsLambdaClient } from './credits-lambda-client';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.CreditsLambdaClient = void 0;
18
+ // Main client class
19
+ var credits_lambda_client_1 = require("./credits-lambda-client");
20
+ Object.defineProperty(exports, "CreditsLambdaClient", { enumerable: true, get: function () { return credits_lambda_client_1.CreditsLambdaClient; } });
21
+ // All types
22
+ __exportStar(require("./types"), exports);
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,oBAAoB;AACpB,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAE5B,YAAY;AACZ,0CAAwB"}
@@ -0,0 +1,88 @@
1
+ export declare enum OperationType {
2
+ VIDEO_EXPORT_720P = "VIDEO_EXPORT_720P",
3
+ VIDEO_EXPORT_1080P = "VIDEO_EXPORT_1080P",
4
+ VIDEO_EXPORT_4K = "VIDEO_EXPORT_4K",
5
+ GIF_EXPORT = "GIF_EXPORT",
6
+ AUDIO_EXTRACTION = "AUDIO_EXTRACTION",
7
+ BACKGROUND_REMOVAL = "BACKGROUND_REMOVAL",
8
+ AI_IMAGE_GENERATION = "AI_IMAGE_GENERATION",
9
+ AI_TEXT_COMPLETION = "AI_TEXT_COMPLETION",
10
+ SUBTITLE_GENERATION = "SUBTITLE_GENERATION",
11
+ HLS_TRANSCODING = "HLS_TRANSCODING",
12
+ THUMBNAIL_GENERATION = "THUMBNAIL_GENERATION",
13
+ FILMSTRIP_GENERATION = "FILMSTRIP_GENERATION",
14
+ REMOTION_RENDER_720P = "REMOTION_RENDER_720P",
15
+ REMOTION_RENDER_1080P = "REMOTION_RENDER_1080P",
16
+ REMOTION_RENDER_4K = "REMOTION_RENDER_4K",
17
+ REMOTION_THUMBNAIL = "REMOTION_THUMBNAIL"
18
+ }
19
+ export interface ReserveCreditsRequest {
20
+ userId: string;
21
+ operationType: OperationType;
22
+ estimatedCredits: number;
23
+ operationId: string;
24
+ metadata?: Record<string, any>;
25
+ }
26
+ export interface FinalizeCreditsRequest {
27
+ reservationId: string;
28
+ actualCredits: number;
29
+ resultUrl?: string;
30
+ }
31
+ export interface ReleaseCreditsRequest {
32
+ reservationId: string;
33
+ reason?: string;
34
+ }
35
+ export interface GetBalanceRequest {
36
+ userId: string;
37
+ }
38
+ export interface EstimateCostRequest {
39
+ userId: string;
40
+ operationType: OperationType;
41
+ durationMinutes?: number;
42
+ durationSeconds?: number;
43
+ variants?: number;
44
+ isHighRes?: boolean;
45
+ }
46
+ export interface ReserveCreditsResponse {
47
+ success: boolean;
48
+ reservationId?: string;
49
+ creditsReserved?: number;
50
+ newAvailableBalance?: number;
51
+ error?: string;
52
+ }
53
+ export interface FinalizeCreditsResponse {
54
+ success: boolean;
55
+ transactionId?: string;
56
+ creditsUsed?: number;
57
+ newBalance?: number;
58
+ refundedCredits?: number;
59
+ error?: string;
60
+ }
61
+ export interface ReleaseCreditsResponse {
62
+ success: boolean;
63
+ creditsReleased?: number;
64
+ newAvailableBalance?: number;
65
+ error?: string;
66
+ }
67
+ export interface GetBalanceResponse {
68
+ success: boolean;
69
+ totalEarned?: number;
70
+ totalUsed?: number;
71
+ currentBalance?: number;
72
+ expiringCredits?: number;
73
+ expiringDate?: string;
74
+ error?: string;
75
+ }
76
+ export interface EstimateCostResponse {
77
+ success: boolean;
78
+ estimatedCredits?: number;
79
+ breakdown?: {
80
+ baseCost: number;
81
+ durationCost: number;
82
+ modifierCost: number;
83
+ };
84
+ currentBalance?: number;
85
+ canProceed?: boolean;
86
+ error?: string;
87
+ }
88
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ // ============================================
3
+ // Operation Types for Credit Calculation
4
+ // ============================================
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OperationType = void 0;
7
+ var OperationType;
8
+ (function (OperationType) {
9
+ // Video exports
10
+ OperationType["VIDEO_EXPORT_720P"] = "VIDEO_EXPORT_720P";
11
+ OperationType["VIDEO_EXPORT_1080P"] = "VIDEO_EXPORT_1080P";
12
+ OperationType["VIDEO_EXPORT_4K"] = "VIDEO_EXPORT_4K";
13
+ OperationType["GIF_EXPORT"] = "GIF_EXPORT";
14
+ OperationType["AUDIO_EXTRACTION"] = "AUDIO_EXTRACTION";
15
+ // AI operations
16
+ OperationType["BACKGROUND_REMOVAL"] = "BACKGROUND_REMOVAL";
17
+ OperationType["AI_IMAGE_GENERATION"] = "AI_IMAGE_GENERATION";
18
+ OperationType["AI_TEXT_COMPLETION"] = "AI_TEXT_COMPLETION";
19
+ OperationType["SUBTITLE_GENERATION"] = "SUBTITLE_GENERATION";
20
+ // Media processing
21
+ OperationType["HLS_TRANSCODING"] = "HLS_TRANSCODING";
22
+ OperationType["THUMBNAIL_GENERATION"] = "THUMBNAIL_GENERATION";
23
+ OperationType["FILMSTRIP_GENERATION"] = "FILMSTRIP_GENERATION";
24
+ // Remotion rendering
25
+ OperationType["REMOTION_RENDER_720P"] = "REMOTION_RENDER_720P";
26
+ OperationType["REMOTION_RENDER_1080P"] = "REMOTION_RENDER_1080P";
27
+ OperationType["REMOTION_RENDER_4K"] = "REMOTION_RENDER_4K";
28
+ OperationType["REMOTION_THUMBNAIL"] = "REMOTION_THUMBNAIL";
29
+ })(OperationType || (exports.OperationType = OperationType = {}));
30
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,+CAA+C;AAC/C,yCAAyC;AACzC,+CAA+C;;;AAE/C,IAAY,aAwBX;AAxBD,WAAY,aAAa;IACvB,gBAAgB;IAChB,wDAAuC,CAAA;IACvC,0DAAyC,CAAA;IACzC,oDAAmC,CAAA;IACnC,0CAAyB,CAAA;IACzB,sDAAqC,CAAA;IAErC,gBAAgB;IAChB,0DAAyC,CAAA;IACzC,4DAA2C,CAAA;IAC3C,0DAAyC,CAAA;IACzC,4DAA2C,CAAA;IAE3C,mBAAmB;IACnB,oDAAmC,CAAA;IACnC,8DAA6C,CAAA;IAC7C,8DAA6C,CAAA;IAE7C,qBAAqB;IACrB,8DAA6C,CAAA;IAC7C,gEAA+C,CAAA;IAC/C,0DAAyC,CAAA;IACzC,0DAAyC,CAAA;AAC3C,CAAC,EAxBW,aAAa,6BAAb,aAAa,QAwBxB"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "clhq-credits-client",
3
+ "version": "1.1.0-alpha.116",
4
+ "description": "Lambda client for invoking credits service",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "license": "UNLICENSED",
8
+ "scripts": {
9
+ "build": "yarn exec tsc -p tsconfig.json",
10
+ "clean:dist": "rimraf dist",
11
+ "clean:all": "yarn run clean:dist",
12
+ "format": "prettier --write \"src/**/*.ts\"",
13
+ "lint": "eslint \"src/**/*.ts\" --fix",
14
+ "version:upgrade:alpha": "standard-version --prerelease alpha",
15
+ "release": "npm publish --tag alpha",
16
+ "release:alpha": "npm publish --tag alpha",
17
+ "release:prealpha": "",
18
+ "clean:build": "rimraf .build",
19
+ "clean:webpack": "rimraf .webpack",
20
+ "clean:serverless": "rimraf .serverless",
21
+ "sls:deploy": "sls deploy",
22
+ "sls:deploy:dev": "env-cmd -f ../../.env.dev sls deploy --stage dev",
23
+ "sls:remove:dev": "env-cmd -f ../../.env.dev sls remove --stage dev",
24
+ "sls:offline": "sls offline",
25
+ "sls:package": "sls package",
26
+ "sls:package2": "npm run clean:all&& npm run sls:package",
27
+ "sls:remove": "sls remove"
28
+ },
29
+ "dependencies": {
30
+ "@aws-sdk/client-lambda": "^3.490.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^24.0.4",
34
+ "typescript": "^5.8.3"
35
+ },
36
+ "peerDependencies": {
37
+ "@aws-sdk/client-lambda": "^3.490.0"
38
+ }
39
+ }
@@ -0,0 +1,165 @@
1
+ import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
2
+ import {
3
+ ReserveCreditsRequest,
4
+ ReserveCreditsResponse,
5
+ FinalizeCreditsRequest,
6
+ FinalizeCreditsResponse,
7
+ ReleaseCreditsRequest,
8
+ ReleaseCreditsResponse,
9
+ GetBalanceRequest,
10
+ GetBalanceResponse,
11
+ EstimateCostRequest,
12
+ EstimateCostResponse,
13
+ } from './types';
14
+
15
+ export class CreditsLambdaClient {
16
+ private lambda: LambdaClient;
17
+ private stage: string;
18
+
19
+ constructor(options?: { region?: string; stage?: string }) {
20
+ this.lambda = new LambdaClient({
21
+ region: options?.region || process.env.AWS_REGION || 'ap-southeast-2',
22
+ });
23
+ this.stage = options?.stage || process.env.STAGE || 'dev';
24
+ }
25
+
26
+ /**
27
+ * Get the Lambda function name for a specific handler
28
+ */
29
+ private getFunctionName(handler: string): string {
30
+ return `clippy-credits-service-${this.stage}-${handler}`;
31
+ }
32
+
33
+ /**
34
+ * Generic method to invoke Lambda functions with proper error handling
35
+ */
36
+ private async invoke<TRequest, TResponse>(
37
+ handler: string,
38
+ payload: TRequest
39
+ ): Promise<TResponse> {
40
+ const command = new InvokeCommand({
41
+ FunctionName: this.getFunctionName(handler),
42
+ Payload: Buffer.from(JSON.stringify(payload)),
43
+ });
44
+
45
+ try {
46
+ const response = await this.lambda.send(command);
47
+
48
+ if (response.FunctionError) {
49
+ const errorPayload = JSON.parse(
50
+ Buffer.from(response.Payload!).toString()
51
+ );
52
+ throw new Error(errorPayload.errorMessage || 'Lambda invocation failed');
53
+ }
54
+
55
+ const result = JSON.parse(Buffer.from(response.Payload!).toString());
56
+
57
+ if (!result.success && result.error) {
58
+ throw new Error(result.error);
59
+ }
60
+
61
+ return result;
62
+ } catch (error: any) {
63
+ // Re-throw Lambda invocation errors
64
+ if (error.name === 'ResourceNotFoundException') {
65
+ throw new Error(`Credits service not available: ${handler}`);
66
+ }
67
+ if (error.name === 'TooManyRequestsException') {
68
+ throw new Error('Credits service rate limited');
69
+ }
70
+ throw error;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Reserve credits before starting an operation
76
+ */
77
+ async reserveCredits(
78
+ params: ReserveCreditsRequest
79
+ ): Promise<ReserveCreditsResponse> {
80
+ return this.invoke('reserve', params);
81
+ }
82
+
83
+ /**
84
+ * Finalize credit consumption after operation completes
85
+ */
86
+ async finalizeCredits(
87
+ params: FinalizeCreditsRequest
88
+ ): Promise<FinalizeCreditsResponse> {
89
+ return this.invoke('finalize', params);
90
+ }
91
+
92
+ /**
93
+ * Release reserved credits if operation fails
94
+ */
95
+ async releaseCredits(
96
+ params: ReleaseCreditsRequest
97
+ ): Promise<ReleaseCreditsResponse> {
98
+ return this.invoke('release', params);
99
+ }
100
+
101
+ /**
102
+ * Get user's credit balance
103
+ */
104
+ async getBalance(params: GetBalanceRequest): Promise<GetBalanceResponse> {
105
+ return this.invoke('balance', params);
106
+ }
107
+
108
+ /**
109
+ * Estimate credit cost for an operation
110
+ */
111
+ async estimateCost(
112
+ params: EstimateCostRequest
113
+ ): Promise<EstimateCostResponse> {
114
+ return this.invoke('estimate', params);
115
+ }
116
+
117
+ /**
118
+ * Convenience method: Reserve and execute operation with automatic finalization
119
+ */
120
+ async executeWithCredits<T>(
121
+ operation: () => Promise<T>,
122
+ reserveParams: ReserveCreditsRequest,
123
+ finalizeParams: (result: T) => FinalizeCreditsRequest
124
+ ): Promise<{ result: T; creditsUsed: number }> {
125
+ // Reserve credits
126
+ const reservation = await this.reserveCredits(reserveParams);
127
+
128
+ if (!reservation.success || !reservation.reservationId) {
129
+ throw new Error(reservation.error || 'Failed to reserve credits');
130
+ }
131
+
132
+ try {
133
+ // Execute the operation
134
+ const result = await operation();
135
+
136
+ // Finalize credits
137
+ const finalizeRequest = finalizeParams(result);
138
+ finalizeRequest.reservationId = reservation.reservationId;
139
+
140
+ const finalization = await this.finalizeCredits(finalizeRequest);
141
+
142
+ if (!finalization.success) {
143
+ throw new Error(finalization.error || 'Failed to finalize credits');
144
+ }
145
+
146
+ return {
147
+ result,
148
+ creditsUsed: finalization.creditsUsed || 0,
149
+ };
150
+
151
+ } catch (error) {
152
+ // Release credits on failure
153
+ try {
154
+ await this.releaseCredits({
155
+ reservationId: reservation.reservationId,
156
+ reason: error instanceof Error ? error.message : 'Operation failed',
157
+ });
158
+ } catch (releaseError) {
159
+ console.warn('Failed to release credits:', releaseError);
160
+ }
161
+
162
+ throw error;
163
+ }
164
+ }
165
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // Main client class
2
+ export { CreditsLambdaClient } from './credits-lambda-client';
3
+
4
+ // All types
5
+ export * from './types';
package/src/types.ts ADDED
@@ -0,0 +1,116 @@
1
+ // ============================================
2
+ // Operation Types for Credit Calculation
3
+ // ============================================
4
+
5
+ export enum OperationType {
6
+ // Video exports
7
+ VIDEO_EXPORT_720P = 'VIDEO_EXPORT_720P',
8
+ VIDEO_EXPORT_1080P = 'VIDEO_EXPORT_1080P',
9
+ VIDEO_EXPORT_4K = 'VIDEO_EXPORT_4K',
10
+ GIF_EXPORT = 'GIF_EXPORT',
11
+ AUDIO_EXTRACTION = 'AUDIO_EXTRACTION',
12
+
13
+ // AI operations
14
+ BACKGROUND_REMOVAL = 'BACKGROUND_REMOVAL',
15
+ AI_IMAGE_GENERATION = 'AI_IMAGE_GENERATION',
16
+ AI_TEXT_COMPLETION = 'AI_TEXT_COMPLETION',
17
+ SUBTITLE_GENERATION = 'SUBTITLE_GENERATION',
18
+
19
+ // Media processing
20
+ HLS_TRANSCODING = 'HLS_TRANSCODING',
21
+ THUMBNAIL_GENERATION = 'THUMBNAIL_GENERATION',
22
+ FILMSTRIP_GENERATION = 'FILMSTRIP_GENERATION',
23
+
24
+ // Remotion rendering
25
+ REMOTION_RENDER_720P = 'REMOTION_RENDER_720P',
26
+ REMOTION_RENDER_1080P = 'REMOTION_RENDER_1080P',
27
+ REMOTION_RENDER_4K = 'REMOTION_RENDER_4K',
28
+ REMOTION_THUMBNAIL = 'REMOTION_THUMBNAIL',
29
+ }
30
+
31
+ // ============================================
32
+ // Request Types
33
+ // ============================================
34
+
35
+ export interface ReserveCreditsRequest {
36
+ userId: string;
37
+ operationType: OperationType;
38
+ estimatedCredits: number;
39
+ operationId: string;
40
+ metadata?: Record<string, any>;
41
+ }
42
+
43
+ export interface FinalizeCreditsRequest {
44
+ reservationId: string;
45
+ actualCredits: number;
46
+ resultUrl?: string;
47
+ }
48
+
49
+ export interface ReleaseCreditsRequest {
50
+ reservationId: string;
51
+ reason?: string;
52
+ }
53
+
54
+ export interface GetBalanceRequest {
55
+ userId: string;
56
+ }
57
+
58
+ export interface EstimateCostRequest {
59
+ userId: string;
60
+ operationType: OperationType;
61
+ durationMinutes?: number;
62
+ durationSeconds?: number;
63
+ variants?: number;
64
+ isHighRes?: boolean;
65
+ }
66
+
67
+ // ============================================
68
+ // Response Types
69
+ // ============================================
70
+
71
+ export interface ReserveCreditsResponse {
72
+ success: boolean;
73
+ reservationId?: string;
74
+ creditsReserved?: number;
75
+ newAvailableBalance?: number;
76
+ error?: string;
77
+ }
78
+
79
+ export interface FinalizeCreditsResponse {
80
+ success: boolean;
81
+ transactionId?: string;
82
+ creditsUsed?: number;
83
+ newBalance?: number;
84
+ refundedCredits?: number;
85
+ error?: string;
86
+ }
87
+
88
+ export interface ReleaseCreditsResponse {
89
+ success: boolean;
90
+ creditsReleased?: number;
91
+ newAvailableBalance?: number;
92
+ error?: string;
93
+ }
94
+
95
+ export interface GetBalanceResponse {
96
+ success: boolean;
97
+ totalEarned?: number;
98
+ totalUsed?: number;
99
+ currentBalance?: number;
100
+ expiringCredits?: number;
101
+ expiringDate?: string;
102
+ error?: string;
103
+ }
104
+
105
+ export interface EstimateCostResponse {
106
+ success: boolean;
107
+ estimatedCredits?: number;
108
+ breakdown?: {
109
+ baseCost: number;
110
+ durationCost: number;
111
+ modifierCost: number;
112
+ };
113
+ currentBalance?: number;
114
+ canProceed?: boolean;
115
+ error?: string;
116
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "lib": ["ES2022"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }