@themainstack/communication 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/grpc/client-factory.d.ts +5 -0
- package/dist/grpc/client-factory.js +73 -0
- package/dist/grpc/errors.d.ts +8 -0
- package/dist/grpc/errors.js +24 -0
- package/dist/grpc/server-factory.d.ts +98 -0
- package/dist/grpc/server-factory.js +220 -0
- package/dist/grpc/types.d.ts +10 -0
- package/dist/grpc/types.js +2 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +33 -0
- package/dist/proto-generator/index.d.ts +36 -0
- package/dist/proto-generator/index.js +250 -0
- package/docs/GRPC_USAGE.md +203 -0
- package/docs/INTEGRATION_EXAMPLE.md +364 -0
- package/examples/full-grpc-demo.ts +139 -0
- package/examples/grpc/calculator.proto +22 -0
- package/examples/grpc/fee.proto +60 -0
- package/examples/protos/hero.proto +17 -0
- package/package.json +26 -0
- package/src/grpc/client-factory.ts +48 -0
- package/src/grpc/errors.ts +31 -0
- package/src/grpc/server-factory.ts +263 -0
- package/src/grpc/types.ts +11 -0
- package/src/index.ts +42 -0
- package/src/proto-generator/index.ts +293 -0
- package/tests/fixtures/dummy.proto +15 -0
- package/tests/fixtures/no-package.proto +15 -0
- package/tests/grpc/client-factory.spec.ts +44 -0
- package/tests/grpc/errors.spec.ts +31 -0
- package/tests/index.spec.ts +29 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,73 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GrpcClientFactory = void 0;
|
|
37
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
39
|
+
class GrpcClientFactory {
|
|
40
|
+
static createClient(options) {
|
|
41
|
+
const packageDefinition = protoLoader.loadSync(options.protoPath, {
|
|
42
|
+
keepCase: true,
|
|
43
|
+
longs: String,
|
|
44
|
+
enums: String,
|
|
45
|
+
defaults: true,
|
|
46
|
+
oneofs: true,
|
|
47
|
+
...options.loaderOptions,
|
|
48
|
+
});
|
|
49
|
+
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
50
|
+
let serviceDef = protoDescriptor;
|
|
51
|
+
// Default to empty string (root) if not provided
|
|
52
|
+
const packageName = options.packageName || '';
|
|
53
|
+
// Only traverse if packageName is non-empty
|
|
54
|
+
if (packageName) {
|
|
55
|
+
const pkgNameParts = packageName.split('.');
|
|
56
|
+
for (const part of pkgNameParts) {
|
|
57
|
+
if (serviceDef[part]) {
|
|
58
|
+
serviceDef = serviceDef[part];
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
throw new Error(`Package part '${part}' not found in proto definition`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (!serviceDef[options.serviceName]) {
|
|
66
|
+
const location = packageName ? `package '${packageName}'` : 'root definition';
|
|
67
|
+
throw new Error(`Service '${options.serviceName}' not found in ${location}`);
|
|
68
|
+
}
|
|
69
|
+
const ServiceClient = serviceDef[options.serviceName];
|
|
70
|
+
return new ServiceClient(options.url, options.credentials || grpc.credentials.createInsecure(), options.channelOptions);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.GrpcClientFactory = GrpcClientFactory;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { status } from '@grpc/grpc-js';
|
|
2
|
+
export declare class GrpcError extends Error {
|
|
3
|
+
code: status;
|
|
4
|
+
details: string;
|
|
5
|
+
metadata: any;
|
|
6
|
+
constructor(code: status, message: string, details?: string, metadata?: any);
|
|
7
|
+
}
|
|
8
|
+
export declare const handleGrpcError: (err: any) => never;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleGrpcError = exports.GrpcError = void 0;
|
|
4
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
5
|
+
class GrpcError extends Error {
|
|
6
|
+
constructor(code, message, details, metadata) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = 'GrpcError';
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.details = details || message;
|
|
11
|
+
this.metadata = metadata;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.GrpcError = GrpcError;
|
|
15
|
+
const handleGrpcError = (err) => {
|
|
16
|
+
if (err && (err.code !== undefined || err.message)) {
|
|
17
|
+
const error = err;
|
|
18
|
+
// Map gRPC status codes to more friendly errors if needed
|
|
19
|
+
// For now, we wrap it in our GrpcError
|
|
20
|
+
throw new GrpcError(error.code ?? grpc_js_1.status.UNKNOWN, error.message || 'Unknown gRPC error', error.details, error.metadata);
|
|
21
|
+
}
|
|
22
|
+
throw new GrpcError(grpc_js_1.status.UNKNOWN, 'An unexpected error occurred', JSON.stringify(err));
|
|
23
|
+
};
|
|
24
|
+
exports.handleGrpcError = handleGrpcError;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for creating a gRPC server
|
|
3
|
+
*/
|
|
4
|
+
export interface GrpcServerOptions {
|
|
5
|
+
/** The package name (e.g., 'fee.v1') */
|
|
6
|
+
packageName: string;
|
|
7
|
+
/** The service name (e.g., 'FeeService') */
|
|
8
|
+
serviceName: string;
|
|
9
|
+
/** Port to listen on (default: 50051) */
|
|
10
|
+
port?: number;
|
|
11
|
+
/** Host to bind to (default: '0.0.0.0') */
|
|
12
|
+
host?: string;
|
|
13
|
+
/** Path to existing proto file (if not provided, will auto-generate) */
|
|
14
|
+
protoPath?: string;
|
|
15
|
+
/** Directory to save auto-generated proto (default: './grpc') */
|
|
16
|
+
protoOutputDir?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Method handler definition for the server
|
|
20
|
+
*/
|
|
21
|
+
export interface ServerMethodHandler<TReq = any, TRes = any> {
|
|
22
|
+
/** Method name (e.g., 'CalculateWithdrawalFee') */
|
|
23
|
+
name: string;
|
|
24
|
+
/** The actual handler function */
|
|
25
|
+
handler: (request: TReq) => Promise<TRes> | TRes;
|
|
26
|
+
/** Sample request for proto generation */
|
|
27
|
+
requestSample: () => TReq;
|
|
28
|
+
/** Sample response for proto generation */
|
|
29
|
+
responseSample: () => TRes;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* GrpcServerFactory - Create and run gRPC servers easily
|
|
33
|
+
*
|
|
34
|
+
* Usage:
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const server = await GrpcServerFactory.createServer({
|
|
37
|
+
* packageName: 'fee.v1',
|
|
38
|
+
* serviceName: 'FeeService',
|
|
39
|
+
* port: 50053,
|
|
40
|
+
* }, [
|
|
41
|
+
* {
|
|
42
|
+
* name: 'CalculateWithdrawalFee',
|
|
43
|
+
* handler: async (req) => calculateFee(req),
|
|
44
|
+
* requestSample: () => ({ merchantId: '', amount: 0 }),
|
|
45
|
+
* responseSample: () => ({ dollarAmount: 0, fees: [] }),
|
|
46
|
+
* }
|
|
47
|
+
* ]);
|
|
48
|
+
*
|
|
49
|
+
* await server.start();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare class GrpcServerFactory {
|
|
53
|
+
private server;
|
|
54
|
+
private options;
|
|
55
|
+
private isRunning;
|
|
56
|
+
private constructor();
|
|
57
|
+
/**
|
|
58
|
+
* Create a new gRPC server with the given handlers
|
|
59
|
+
*/
|
|
60
|
+
static createServer(options: GrpcServerOptions, handlers: ServerMethodHandler[]): Promise<GrpcServerFactory>;
|
|
61
|
+
/**
|
|
62
|
+
* Start the gRPC server
|
|
63
|
+
*/
|
|
64
|
+
start(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Stop the gRPC server
|
|
67
|
+
*/
|
|
68
|
+
stop(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Force stop the server immediately
|
|
71
|
+
*/
|
|
72
|
+
forceStop(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Check if server is running
|
|
75
|
+
*/
|
|
76
|
+
get running(): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Get the server address
|
|
79
|
+
*/
|
|
80
|
+
get address(): string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Helper function to quickly expose a function as a gRPC service
|
|
84
|
+
*
|
|
85
|
+
* Usage:
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const server = await exposeAsGrpc(
|
|
88
|
+
* 'CalculateFee',
|
|
89
|
+
* async (req) => ({ fee: req.amount * 0.02 }),
|
|
90
|
+
* { requestSample: () => ({ amount: 100 }), responseSample: () => ({ fee: 2 }) },
|
|
91
|
+
* { packageName: 'fee.v1', serviceName: 'FeeService', port: 50053 }
|
|
92
|
+
* );
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function exposeAsGrpc<TReq extends object, TRes extends object>(methodName: string, handler: (request: TReq) => Promise<TRes> | TRes, samples: {
|
|
96
|
+
requestSample: () => TReq;
|
|
97
|
+
responseSample: () => TRes;
|
|
98
|
+
}, options: GrpcServerOptions): Promise<GrpcServerFactory>;
|
|
@@ -0,0 +1,220 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GrpcServerFactory = void 0;
|
|
37
|
+
exports.exposeAsGrpc = exposeAsGrpc;
|
|
38
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
39
|
+
const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const proto_generator_1 = require("../proto-generator");
|
|
42
|
+
/**
|
|
43
|
+
* GrpcServerFactory - Create and run gRPC servers easily
|
|
44
|
+
*
|
|
45
|
+
* Usage:
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const server = await GrpcServerFactory.createServer({
|
|
48
|
+
* packageName: 'fee.v1',
|
|
49
|
+
* serviceName: 'FeeService',
|
|
50
|
+
* port: 50053,
|
|
51
|
+
* }, [
|
|
52
|
+
* {
|
|
53
|
+
* name: 'CalculateWithdrawalFee',
|
|
54
|
+
* handler: async (req) => calculateFee(req),
|
|
55
|
+
* requestSample: () => ({ merchantId: '', amount: 0 }),
|
|
56
|
+
* responseSample: () => ({ dollarAmount: 0, fees: [] }),
|
|
57
|
+
* }
|
|
58
|
+
* ]);
|
|
59
|
+
*
|
|
60
|
+
* await server.start();
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
class GrpcServerFactory {
|
|
64
|
+
constructor(server, options) {
|
|
65
|
+
this.isRunning = false;
|
|
66
|
+
this.server = server;
|
|
67
|
+
this.options = options;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a new gRPC server with the given handlers
|
|
71
|
+
*/
|
|
72
|
+
static async createServer(options, handlers) {
|
|
73
|
+
const fullOptions = {
|
|
74
|
+
port: 50051,
|
|
75
|
+
host: '0.0.0.0',
|
|
76
|
+
protoOutputDir: './grpc',
|
|
77
|
+
protoPath: '',
|
|
78
|
+
...options,
|
|
79
|
+
};
|
|
80
|
+
// Step 1: Generate or load proto file
|
|
81
|
+
let protoPath = fullOptions.protoPath;
|
|
82
|
+
if (!protoPath) {
|
|
83
|
+
// Auto-generate proto from handlers
|
|
84
|
+
const methodDefinitions = handlers.map(h => ({
|
|
85
|
+
name: h.name,
|
|
86
|
+
requestSample: h.requestSample,
|
|
87
|
+
responseSample: h.responseSample,
|
|
88
|
+
}));
|
|
89
|
+
const protoContent = (0, proto_generator_1.generateProtoFromMethods)(methodDefinitions, {
|
|
90
|
+
packageName: fullOptions.packageName,
|
|
91
|
+
serviceName: fullOptions.serviceName,
|
|
92
|
+
outputDir: fullOptions.protoOutputDir,
|
|
93
|
+
});
|
|
94
|
+
protoPath = path.join(fullOptions.protoOutputDir, `${fullOptions.serviceName.toLowerCase().replace(/service$/, '')}.proto`);
|
|
95
|
+
console.log(`Proto file auto-generated: ${protoPath}`);
|
|
96
|
+
}
|
|
97
|
+
// Step 2: Load the proto definition
|
|
98
|
+
const packageDefinition = protoLoader.loadSync(protoPath, {
|
|
99
|
+
keepCase: false, // Convert snake_case to camelCase
|
|
100
|
+
longs: String,
|
|
101
|
+
enums: String,
|
|
102
|
+
defaults: true,
|
|
103
|
+
oneofs: true,
|
|
104
|
+
});
|
|
105
|
+
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
106
|
+
// Step 3: Navigate to the service definition
|
|
107
|
+
let serviceDefinition = protoDescriptor;
|
|
108
|
+
const packageParts = fullOptions.packageName.split('.');
|
|
109
|
+
for (const part of packageParts) {
|
|
110
|
+
serviceDefinition = serviceDefinition[part];
|
|
111
|
+
if (!serviceDefinition) {
|
|
112
|
+
throw new Error(`Package '${fullOptions.packageName}' not found in proto`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const ServiceConstructor = serviceDefinition[fullOptions.serviceName];
|
|
116
|
+
if (!ServiceConstructor || !ServiceConstructor.service) {
|
|
117
|
+
throw new Error(`Service '${fullOptions.serviceName}' not found in package '${fullOptions.packageName}'`);
|
|
118
|
+
}
|
|
119
|
+
// Step 4: Create handler implementations
|
|
120
|
+
const implementations = {};
|
|
121
|
+
for (const handler of handlers) {
|
|
122
|
+
// gRPC uses lowercase first letter for method names in implementation
|
|
123
|
+
const methodName = handler.name.charAt(0).toLowerCase() + handler.name.slice(1);
|
|
124
|
+
implementations[methodName] = async (call, callback) => {
|
|
125
|
+
try {
|
|
126
|
+
const result = await handler.handler(call.request);
|
|
127
|
+
callback(null, result);
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error(`gRPC Error in ${handler.name}:`, error);
|
|
131
|
+
callback({
|
|
132
|
+
code: grpc.status.INTERNAL,
|
|
133
|
+
message: error.message || 'Internal server error',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Step 5: Create and configure the server
|
|
139
|
+
const server = new grpc.Server();
|
|
140
|
+
server.addService(ServiceConstructor.service, implementations);
|
|
141
|
+
return new GrpcServerFactory(server, { ...fullOptions, protoPath });
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Start the gRPC server
|
|
145
|
+
*/
|
|
146
|
+
async start() {
|
|
147
|
+
return new Promise((resolve, reject) => {
|
|
148
|
+
const address = `${this.options.host}:${this.options.port}`;
|
|
149
|
+
this.server.bindAsync(address, grpc.ServerCredentials.createInsecure(), (error, port) => {
|
|
150
|
+
if (error) {
|
|
151
|
+
reject(error);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
this.isRunning = true;
|
|
155
|
+
console.log(` gRPC Server running on ${address}`);
|
|
156
|
+
console.log(` Service: ${this.options.serviceName}`);
|
|
157
|
+
console.log(` Package: ${this.options.packageName}`);
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Stop the gRPC server
|
|
164
|
+
*/
|
|
165
|
+
async stop() {
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
this.server.tryShutdown(() => {
|
|
168
|
+
this.isRunning = false;
|
|
169
|
+
console.log('gRPC Server stopped');
|
|
170
|
+
resolve();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Force stop the server immediately
|
|
176
|
+
*/
|
|
177
|
+
forceStop() {
|
|
178
|
+
this.server.forceShutdown();
|
|
179
|
+
this.isRunning = false;
|
|
180
|
+
console.log('gRPC Server force stopped');
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Check if server is running
|
|
184
|
+
*/
|
|
185
|
+
get running() {
|
|
186
|
+
return this.isRunning;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get the server address
|
|
190
|
+
*/
|
|
191
|
+
get address() {
|
|
192
|
+
return `${this.options.host}:${this.options.port}`;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
exports.GrpcServerFactory = GrpcServerFactory;
|
|
196
|
+
/**
|
|
197
|
+
* Helper function to quickly expose a function as a gRPC service
|
|
198
|
+
*
|
|
199
|
+
* Usage:
|
|
200
|
+
* ```typescript
|
|
201
|
+
* const server = await exposeAsGrpc(
|
|
202
|
+
* 'CalculateFee',
|
|
203
|
+
* async (req) => ({ fee: req.amount * 0.02 }),
|
|
204
|
+
* { requestSample: () => ({ amount: 100 }), responseSample: () => ({ fee: 2 }) },
|
|
205
|
+
* { packageName: 'fee.v1', serviceName: 'FeeService', port: 50053 }
|
|
206
|
+
* );
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
async function exposeAsGrpc(methodName, handler, samples, options) {
|
|
210
|
+
const server = await GrpcServerFactory.createServer(options, [
|
|
211
|
+
{
|
|
212
|
+
name: methodName,
|
|
213
|
+
handler,
|
|
214
|
+
requestSample: samples.requestSample,
|
|
215
|
+
responseSample: samples.responseSample,
|
|
216
|
+
},
|
|
217
|
+
]);
|
|
218
|
+
await server.start();
|
|
219
|
+
return server;
|
|
220
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ChannelOptions, ChannelCredentials } from '@grpc/grpc-js';
|
|
2
|
+
export interface GrpcClientOptions {
|
|
3
|
+
serviceName: string;
|
|
4
|
+
packageName?: string;
|
|
5
|
+
protoPath: string;
|
|
6
|
+
url: string;
|
|
7
|
+
credentials?: ChannelCredentials;
|
|
8
|
+
channelOptions?: ChannelOptions;
|
|
9
|
+
loaderOptions?: object;
|
|
10
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @themainstack/communication
|
|
3
|
+
*
|
|
4
|
+
* A unified gRPC framework for inter-service communication.
|
|
5
|
+
*
|
|
6
|
+
* Workflow:
|
|
7
|
+
* 1. Proto Generation - Auto-generate .proto files from TypeScript
|
|
8
|
+
* 2. Server Factory - Expose functions as gRPC endpoints
|
|
9
|
+
* 3. Client Factory - Call gRPC services from other services
|
|
10
|
+
* 4. Error Handling - Standardized error translation
|
|
11
|
+
*/
|
|
12
|
+
export { generateProtoFromMethods, generateProtoFromFunction } from './proto-generator';
|
|
13
|
+
export type { MethodDefinition, GenerateProtoOptions } from './proto-generator';
|
|
14
|
+
export { GrpcServerFactory, exposeAsGrpc } from './grpc/server-factory';
|
|
15
|
+
export type { GrpcServerOptions, ServerMethodHandler } from './grpc/server-factory';
|
|
16
|
+
export { GrpcClientFactory } from './grpc/client-factory';
|
|
17
|
+
export type { GrpcClientOptions } from './grpc/types';
|
|
18
|
+
export { handleGrpcError, GrpcError } from './grpc/errors';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @themainstack/communication
|
|
4
|
+
*
|
|
5
|
+
* A unified gRPC framework for inter-service communication.
|
|
6
|
+
*
|
|
7
|
+
* Workflow:
|
|
8
|
+
* 1. Proto Generation - Auto-generate .proto files from TypeScript
|
|
9
|
+
* 2. Server Factory - Expose functions as gRPC endpoints
|
|
10
|
+
* 3. Client Factory - Call gRPC services from other services
|
|
11
|
+
* 4. Error Handling - Standardized error translation
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.GrpcError = exports.handleGrpcError = exports.GrpcClientFactory = exports.exposeAsGrpc = exports.GrpcServerFactory = exports.generateProtoFromFunction = exports.generateProtoFromMethods = void 0;
|
|
15
|
+
// Proto Generation
|
|
16
|
+
// Auto-generate .proto files from TypeScript types
|
|
17
|
+
var proto_generator_1 = require("./proto-generator");
|
|
18
|
+
Object.defineProperty(exports, "generateProtoFromMethods", { enumerable: true, get: function () { return proto_generator_1.generateProtoFromMethods; } });
|
|
19
|
+
Object.defineProperty(exports, "generateProtoFromFunction", { enumerable: true, get: function () { return proto_generator_1.generateProtoFromFunction; } });
|
|
20
|
+
// Server Factory
|
|
21
|
+
// Expose existing functions as gRPC endpoints
|
|
22
|
+
var server_factory_1 = require("./grpc/server-factory");
|
|
23
|
+
Object.defineProperty(exports, "GrpcServerFactory", { enumerable: true, get: function () { return server_factory_1.GrpcServerFactory; } });
|
|
24
|
+
Object.defineProperty(exports, "exposeAsGrpc", { enumerable: true, get: function () { return server_factory_1.exposeAsGrpc; } });
|
|
25
|
+
// Client Factory
|
|
26
|
+
// Create clients to call gRPC services
|
|
27
|
+
var client_factory_1 = require("./grpc/client-factory");
|
|
28
|
+
Object.defineProperty(exports, "GrpcClientFactory", { enumerable: true, get: function () { return client_factory_1.GrpcClientFactory; } });
|
|
29
|
+
// Error Handling
|
|
30
|
+
// Standardized gRPC error translation
|
|
31
|
+
var errors_1 = require("./grpc/errors");
|
|
32
|
+
Object.defineProperty(exports, "handleGrpcError", { enumerable: true, get: function () { return errors_1.handleGrpcError; } });
|
|
33
|
+
Object.defineProperty(exports, "GrpcError", { enumerable: true, get: function () { return errors_1.GrpcError; } });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for generating a proto file
|
|
3
|
+
*/
|
|
4
|
+
export interface GenerateProtoOptions {
|
|
5
|
+
/** The package name (e.g., 'fee.v1') */
|
|
6
|
+
packageName?: string;
|
|
7
|
+
/** The service name (e.g., 'FeeService') */
|
|
8
|
+
serviceName?: string;
|
|
9
|
+
/** Directory to save the proto file (if not provided, won't save to file) */
|
|
10
|
+
outputDir?: string;
|
|
11
|
+
/** Output filename (default: derived from serviceName or 'generated.proto') */
|
|
12
|
+
outputFilename?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Definition of a gRPC method with request and response generators
|
|
16
|
+
*/
|
|
17
|
+
export interface MethodDefinition<TReq extends object, TRes extends object> {
|
|
18
|
+
/** Method name (e.g., 'CalculateWithdrawalFee') */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Function that returns a sample request object */
|
|
21
|
+
requestSample: () => TReq;
|
|
22
|
+
/** Function that returns a sample response object */
|
|
23
|
+
responseSample: () => TRes;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate a complete .proto file from method definitions
|
|
27
|
+
*
|
|
28
|
+
* @param methods Array of method definitions
|
|
29
|
+
* @param options Generation options
|
|
30
|
+
* @returns The generated proto string
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateProtoFromMethods(methods: MethodDefinition<any, any>[], options?: GenerateProtoOptions): string;
|
|
33
|
+
/**
|
|
34
|
+
* Convenience function for single-function proto generation (original API)
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateProtoFromFunction<T extends object>(generatorFn: () => T, rootMessageName?: string): string;
|