@themainstack/communication 1.0.2 → 1.1.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/README.md CHANGED
@@ -182,6 +182,7 @@ See `examples/full-grpc-demo.ts` in the repository for a complete working exampl
182
182
  ### Proto Generation
183
183
  - `generateProtoFromMethods(methods, options)` - Generate proto with service definition
184
184
  - `generateProtoFromFunction(fn, name)` - Generate proto for a single message
185
+ - `AnyType` - Marker symbol for `google.protobuf.Any` dynamic fields
185
186
 
186
187
  ### Server
187
188
  - `GrpcServerFactory.createServer(options, handlers)` - Create a gRPC server
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * 3. Client Factory - Call gRPC services from other services
10
10
  * 4. Error Handling - Standardized error translation
11
11
  */
12
- export { generateProtoFromMethods, generateProtoFromFunction } from './proto-generator';
12
+ export { generateProtoFromMethods, generateProtoFromFunction, AnyType } from './proto-generator';
13
13
  export type { MethodDefinition, GenerateProtoOptions } from './proto-generator';
14
14
  export { GrpcServerFactory, exposeAsGrpc } from './grpc/server-factory';
15
15
  export type { GrpcServerOptions, ServerMethodHandler } from './grpc/server-factory';
package/dist/index.js CHANGED
@@ -11,12 +11,13 @@
11
11
  * 4. Error Handling - Standardized error translation
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.GrpcError = exports.handleGrpcError = exports.GrpcClientFactory = exports.exposeAsGrpc = exports.GrpcServerFactory = exports.generateProtoFromFunction = exports.generateProtoFromMethods = void 0;
14
+ exports.GrpcError = exports.handleGrpcError = exports.GrpcClientFactory = exports.exposeAsGrpc = exports.GrpcServerFactory = exports.AnyType = exports.generateProtoFromFunction = exports.generateProtoFromMethods = void 0;
15
15
  // Proto Generation
16
16
  // Auto-generate .proto files from TypeScript types
17
17
  var proto_generator_1 = require("./proto-generator");
18
18
  Object.defineProperty(exports, "generateProtoFromMethods", { enumerable: true, get: function () { return proto_generator_1.generateProtoFromMethods; } });
19
19
  Object.defineProperty(exports, "generateProtoFromFunction", { enumerable: true, get: function () { return proto_generator_1.generateProtoFromFunction; } });
20
+ Object.defineProperty(exports, "AnyType", { enumerable: true, get: function () { return proto_generator_1.AnyType; } });
20
21
  // Server Factory
21
22
  // Expose existing functions as gRPC endpoints
22
23
  var server_factory_1 = require("./grpc/server-factory");
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Marker symbol to indicate a field should use google.protobuf.Any type.
3
+ * Use this in your sample objects to mark dynamic/generic fields.
4
+ *
5
+ * @example
6
+ * requestSample: () => ({ id: '', data: AnyType })
7
+ */
8
+ export declare const AnyType: unique symbol;
1
9
  /**
2
10
  * Options for generating a proto file
3
11
  */
@@ -10,6 +18,8 @@ export interface GenerateProtoOptions {
10
18
  outputDir?: string;
11
19
  /** Output filename (default: derived from serviceName or 'generated.proto') */
12
20
  outputFilename?: string;
21
+ /** Enable google.protobuf.Any for dynamic types marked with AnyType symbol (default: true) */
22
+ enableAnyType?: boolean;
13
23
  }
14
24
  /**
15
25
  * Definition of a gRPC method with request and response generators
@@ -33,10 +33,19 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AnyType = void 0;
36
37
  exports.generateProtoFromMethods = generateProtoFromMethods;
37
38
  exports.generateProtoFromFunction = generateProtoFromFunction;
38
39
  const fs = __importStar(require("fs"));
39
40
  const path = __importStar(require("path"));
41
+ /**
42
+ * Marker symbol to indicate a field should use google.protobuf.Any type.
43
+ * Use this in your sample objects to mark dynamic/generic fields.
44
+ *
45
+ * @example
46
+ * requestSample: () => ({ id: '', data: AnyType })
47
+ */
48
+ exports.AnyType = Symbol('google.protobuf.Any');
40
49
  /**
41
50
  * Generate a complete .proto file from method definitions
42
51
  *
@@ -45,10 +54,11 @@ const path = __importStar(require("path"));
45
54
  * @returns The generated proto string
46
55
  */
47
56
  function generateProtoFromMethods(methods, options = {}) {
48
- const { packageName, serviceName = 'GeneratedService', outputDir, outputFilename } = options;
57
+ const { packageName, serviceName = 'GeneratedService', outputDir, outputFilename, enableAnyType = true } = options;
49
58
  const messages = [];
50
59
  const protoMethods = [];
51
60
  const processedObjects = new Set();
61
+ let usesAnyType = false;
52
62
  // Helper: Capitalize string
53
63
  function capitalize(s) {
54
64
  return s.charAt(0).toUpperCase() + s.slice(1);
@@ -72,7 +82,12 @@ function generateProtoFromMethods(methods, options = {}) {
72
82
  let type = "string";
73
83
  let rule = undefined;
74
84
  const valueType = typeof value;
75
- if (value === null || value === undefined) {
85
+ // Check for AnyType marker symbol
86
+ if (enableAnyType && (value === exports.AnyType || (typeof value === 'symbol' && value.description === 'google.protobuf.Any'))) {
87
+ type = "google.protobuf.Any";
88
+ usesAnyType = true;
89
+ }
90
+ else if (value === null || value === undefined) {
76
91
  type = "string";
77
92
  }
78
93
  else if (valueType === "string") {
@@ -140,6 +155,10 @@ function generateProtoFromMethods(methods, options = {}) {
140
155
  if (packageName) {
141
156
  lines.push(`package ${packageName};`, '');
142
157
  }
158
+ // Add google.protobuf.Any import if needed
159
+ if (usesAnyType) {
160
+ lines.push('import "google/protobuf/any.proto";', '');
161
+ }
143
162
  // Service definition
144
163
  lines.push(`// ${serviceName} - Auto-generated gRPC service`);
145
164
  lines.push(`service ${serviceName} {`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@themainstack/communication",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "private": false,
5
5
  "description": "Unified gRPC framework for inter-service communication - auto-generates protos, creates servers, and provides type-safe clients",
6
6
  "main": "dist/index.js",
@@ -23,4 +23,4 @@
23
23
  "@grpc/grpc-js": "^1.14.3",
24
24
  "@grpc/proto-loader": "^0.8.0"
25
25
  }
26
- }
26
+ }
package/src/index.ts CHANGED
@@ -14,7 +14,8 @@
14
14
  // Auto-generate .proto files from TypeScript types
15
15
  export {
16
16
  generateProtoFromMethods,
17
- generateProtoFromFunction
17
+ generateProtoFromFunction,
18
+ AnyType
18
19
  } from './proto-generator';
19
20
  export type {
20
21
  MethodDefinition,
@@ -1,10 +1,19 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
 
4
+ /**
5
+ * Marker symbol to indicate a field should use google.protobuf.Any type.
6
+ * Use this in your sample objects to mark dynamic/generic fields.
7
+ *
8
+ * @example
9
+ * requestSample: () => ({ id: '', data: AnyType })
10
+ */
11
+ export const AnyType = Symbol('google.protobuf.Any');
12
+
4
13
  /**
5
14
  * Maps JavaScript/TypeScript runtime types to Protobuf types.
6
15
  */
7
- type ProtoType = "double" | "float" | "int32" | "int64" | "bool" | "string" | "bytes" | string;
16
+ type ProtoType = "double" | "float" | "int32" | "int64" | "bool" | "string" | "bytes" | "google.protobuf.Any" | string;
8
17
 
9
18
  interface ProtoField {
10
19
  name: string;
@@ -36,6 +45,8 @@ export interface GenerateProtoOptions {
36
45
  outputDir?: string;
37
46
  /** Output filename (default: derived from serviceName or 'generated.proto') */
38
47
  outputFilename?: string;
48
+ /** Enable google.protobuf.Any for dynamic types marked with AnyType symbol (default: true) */
49
+ enableAnyType?: boolean;
39
50
  }
40
51
 
41
52
  /**
@@ -61,11 +72,12 @@ export function generateProtoFromMethods(
61
72
  methods: MethodDefinition<any, any>[],
62
73
  options: GenerateProtoOptions = {}
63
74
  ): string {
64
- const { packageName, serviceName = 'GeneratedService', outputDir, outputFilename } = options;
75
+ const { packageName, serviceName = 'GeneratedService', outputDir, outputFilename, enableAnyType = true } = options;
65
76
 
66
77
  const messages: ProtoMessage[] = [];
67
78
  const protoMethods: ProtoMethod[] = [];
68
79
  const processedObjects = new Set<object>();
80
+ let usesAnyType = false;
69
81
 
70
82
  // Helper: Capitalize string
71
83
  function capitalize(s: string): string {
@@ -96,7 +108,11 @@ export function generateProtoFromMethods(
96
108
 
97
109
  const valueType = typeof value;
98
110
 
99
- if (value === null || value === undefined) {
111
+ // Check for AnyType marker symbol
112
+ if (enableAnyType && (value === AnyType || (typeof value === 'symbol' && value.description === 'google.protobuf.Any'))) {
113
+ type = "google.protobuf.Any";
114
+ usesAnyType = true;
115
+ } else if (value === null || value === undefined) {
100
116
  type = "string";
101
117
  } else if (valueType === "string") {
102
118
  type = "string";
@@ -167,6 +183,11 @@ export function generateProtoFromMethods(
167
183
  lines.push(`package ${packageName};`, '');
168
184
  }
169
185
 
186
+ // Add google.protobuf.Any import if needed
187
+ if (usesAnyType) {
188
+ lines.push('import "google/protobuf/any.proto";', '');
189
+ }
190
+
170
191
  // Service definition
171
192
  lines.push(`// ${serviceName} - Auto-generated gRPC service`);
172
193
  lines.push(`service ${serviceName} {`);
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateProtoFromMethods, AnyType } from '../src/index';
3
+
4
+ describe('google.protobuf.Any support', () => {
5
+ it('should export AnyType symbol', () => {
6
+ expect(AnyType).toBeDefined();
7
+ expect(typeof AnyType).toBe('symbol');
8
+ expect(AnyType.description).toBe('google.protobuf.Any');
9
+ });
10
+
11
+ it('should generate proto with google.protobuf.Any for marked fields', () => {
12
+ const proto = generateProtoFromMethods([
13
+ {
14
+ name: 'ProcessGenericData',
15
+ requestSample: () => ({
16
+ id: '',
17
+ data: AnyType,
18
+ }),
19
+ responseSample: () => ({
20
+ success: true,
21
+ result: AnyType,
22
+ }),
23
+ }
24
+ ], {
25
+ packageName: 'generic.v1',
26
+ serviceName: 'GenericService',
27
+ });
28
+
29
+ // Should include the import statement
30
+ expect(proto).toContain('import "google/protobuf/any.proto";');
31
+
32
+ // Should use google.protobuf.Any type
33
+ expect(proto).toContain('google.protobuf.Any data = 2;');
34
+ expect(proto).toContain('google.protobuf.Any result = 2;');
35
+ });
36
+
37
+ it('should not include import if no Any types are used', () => {
38
+ const proto = generateProtoFromMethods([
39
+ {
40
+ name: 'SimpleMethod',
41
+ requestSample: () => ({ id: '', amount: 0 }),
42
+ responseSample: () => ({ success: true }),
43
+ }
44
+ ], {
45
+ packageName: 'simple.v1',
46
+ serviceName: 'SimpleService',
47
+ });
48
+
49
+ expect(proto).not.toContain('import "google/protobuf/any.proto";');
50
+ expect(proto).not.toContain('google.protobuf.Any');
51
+ });
52
+
53
+ it('should handle mixed fields with some Any types', () => {
54
+ const proto = generateProtoFromMethods([
55
+ {
56
+ name: 'MixedMethod',
57
+ requestSample: () => ({
58
+ id: '',
59
+ name: 'test',
60
+ metadata: AnyType,
61
+ count: 0,
62
+ }),
63
+ responseSample: () => ({
64
+ status: 'ok',
65
+ payload: AnyType,
66
+ }),
67
+ }
68
+ ], {
69
+ packageName: 'mixed.v1',
70
+ serviceName: 'MixedService',
71
+ });
72
+
73
+ expect(proto).toContain('import "google/protobuf/any.proto";');
74
+ expect(proto).toContain('string id = 1;');
75
+ expect(proto).toContain('string name = 2;');
76
+ expect(proto).toContain('google.protobuf.Any metadata = 3;');
77
+ expect(proto).toContain('int32 count = 4;');
78
+ });
79
+
80
+ it('should respect enableAnyType: false option', () => {
81
+ const proto = generateProtoFromMethods([
82
+ {
83
+ name: 'DisabledAny',
84
+ requestSample: () => ({
85
+ id: '',
86
+ data: AnyType,
87
+ }),
88
+ responseSample: () => ({ success: true }),
89
+ }
90
+ ], {
91
+ packageName: 'test.v1',
92
+ serviceName: 'TestService',
93
+ enableAnyType: false,
94
+ });
95
+
96
+ // When disabled, AnyType should be treated as a regular value (string fallback)
97
+ expect(proto).not.toContain('import "google/protobuf/any.proto";');
98
+ expect(proto).not.toContain('google.protobuf.Any');
99
+ });
100
+ });
@@ -1 +0,0 @@
1
- //registry.npmjs.org/:_authToken=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