@globalart/zod-to-proto 1.0.6 → 1.0.7

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.
Files changed (2) hide show
  1. package/README.md +189 -17
  2. package/package.json +12 -1
package/README.md CHANGED
@@ -56,6 +56,52 @@ message Message {
56
56
 
57
57
  ### Generating gRPC Services
58
58
 
59
+ There are two ways to define gRPC services:
60
+
61
+ #### Option 1: Using Zod Schemas with Functions (Recommended)
62
+
63
+ ```typescript
64
+ import { zodToProtobuf } from "@globalart/zod-to-proto";
65
+ import { z } from "zod";
66
+
67
+ // Define request/response schemas
68
+ const getUserByIdRequestSchema = z.object({
69
+ id: z.number().int(),
70
+ });
71
+
72
+ const userSchema = z.object({
73
+ id: z.number().int(),
74
+ name: z.string(),
75
+ email: z.string(),
76
+ });
77
+
78
+ // Define service using z.function()
79
+ const userServiceSchema = z.object({
80
+ getUserById: z.function({
81
+ input: [getUserByIdRequestSchema],
82
+ output: userSchema,
83
+ }),
84
+ createUser: z.function({
85
+ input: [userSchema],
86
+ output: z.object({
87
+ id: z.number().int(),
88
+ success: z.boolean(),
89
+ }),
90
+ }),
91
+ });
92
+
93
+ const protoDefinition = zodToProtobuf(z.object(), {
94
+ packageName: "user.service",
95
+ services: {
96
+ UserService: userServiceSchema,
97
+ },
98
+ });
99
+
100
+ console.log(protoDefinition);
101
+ ```
102
+
103
+ #### Option 2: Using Service Definitions Array
104
+
59
105
  ```typescript
60
106
  import { zodToProtobuf } from "@globalart/zod-to-proto";
61
107
  import { z } from "zod";
@@ -97,36 +143,36 @@ const protoDefinition = zodToProtobuf(z.object(), {
97
143
  console.log(protoDefinition);
98
144
  ```
99
145
 
100
- Output:
146
+ Both approaches produce the same output:
101
147
 
102
148
  ```protobuf
103
149
  syntax = "proto3";
104
150
  package user.service;
105
151
 
106
- message GetUserRequest {
107
- string id = 1;
152
+ message GetUserByIdRequest {
153
+ int32 id = 1;
108
154
  }
109
155
 
110
- message GetUserResponse {
111
- string name = 1;
112
- int32 age = 2;
156
+ message GetUserByIdResponse {
157
+ int32 id = 1;
158
+ string name = 2;
113
159
  string email = 3;
114
160
  }
115
161
 
116
162
  message CreateUserRequest {
117
- string name = 1;
118
- int32 age = 2;
163
+ int32 id = 1;
164
+ string name = 2;
119
165
  string email = 3;
120
166
  }
121
167
 
122
168
  message CreateUserResponse {
123
- string id = 1;
169
+ int32 id = 1;
124
170
  bool success = 2;
125
171
  }
126
172
 
127
173
  service UserService {
128
- rpc getUser(GetUserRequest) returns (GetUserResponse);
129
- rpc createUser(CreateUserRequest) returns (CreateUserResponse);
174
+ rpc GetUserById(GetUserByIdRequest) returns (GetUserByIdResponse);
175
+ rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
130
176
  }
131
177
  ```
132
178
 
@@ -145,16 +191,41 @@ Converts a Zod schema to a Protobuf definition.
145
191
 
146
192
  ```typescript
147
193
  interface ZodToProtobufOptions {
148
- packageName?: string; // Protobuf package name (default: "default")
149
- rootMessageName?: string; // Root message name (default: "Message")
150
- typePrefix?: string; // Prefix for message and enum types
151
- services?: ServiceDefinition[]; // gRPC service definitions
152
- skipRootMessage?: boolean; // Skip root message generation
194
+ packageName?: string; // Protobuf package name (default: "default")
195
+ rootMessageName?: string; // Root message name (default: "Message")
196
+ typePrefix?: string; // Prefix for message and enum types
197
+ services?: ServicesInput; // gRPC service definitions
198
+ skipRootMessage?: boolean; // Skip root message generation
153
199
  }
200
+
201
+ type ServicesInput = ServiceDefinition[] | Record<string, ZodObject<any>>;
154
202
  ```
155
203
 
156
204
  #### Service Definition
157
205
 
206
+ You can define services in two ways:
207
+
208
+ **Option 1: Using Zod Schemas (Recommended)**
209
+
210
+ ```typescript
211
+ const serviceSchema = z.object({
212
+ methodName: z.function({
213
+ input: [requestSchema],
214
+ output: responseSchema,
215
+ }),
216
+ // ... more methods
217
+ });
218
+
219
+ // Use in options
220
+ const proto = zodToProtobuf(z.object(), {
221
+ services: {
222
+ ServiceName: serviceSchema,
223
+ },
224
+ });
225
+ ```
226
+
227
+ **Option 2: Using Service Definitions Array**
228
+
158
229
  ```typescript
159
230
  interface ServiceDefinition {
160
231
  name: string; // Service name
@@ -183,7 +254,7 @@ interface ServiceMethod {
183
254
 
184
255
  - `z.array()` → `repeated`
185
256
  - `z.set()` → `repeated`
186
- - `z.map()` → `map<keyType, valueType>`
257
+ - `z.map()` → `map<keyType, valueType>` (key must be int32, int64, string, or bool)
187
258
  - `z.tuple()` → nested message
188
259
 
189
260
  ### Complex Types
@@ -282,12 +353,113 @@ const protoDefinition = zodToProtobuf(schema, {
282
353
  });
283
354
  ```
284
355
 
356
+ ## Advanced Usage
357
+
358
+ ### Working with Maps
359
+
360
+ Protobuf maps have strict key type requirements. Only integral types, strings, and booleans are allowed as keys:
361
+
362
+ ```typescript
363
+ const schema = z.object({
364
+ // ✅ Valid map keys
365
+ usersByStringId: z.map(z.string(), userSchema),
366
+ usersByIntId: z.map(z.number().int(), userSchema),
367
+ flagsMap: z.map(z.boolean(), z.string()),
368
+
369
+ // ❌ Invalid - double is not allowed as map key
370
+ invalidMap: z.map(z.number(), userSchema), // Use .int() instead!
371
+ });
372
+ ```
373
+
374
+ ### Type-Safe Service Definitions
375
+
376
+ When using Zod schemas for services, you get full TypeScript type safety:
377
+
378
+ ```typescript
379
+ const userServiceSchema = z.object({
380
+ getUser: z.function({
381
+ input: [z.object({ id: z.number().int() })],
382
+ output: userSchema,
383
+ }),
384
+ });
385
+
386
+ type UserService = z.infer<typeof userServiceSchema>;
387
+
388
+ // Implementation with type checking
389
+ class UserServiceImpl implements UserService {
390
+ async getUser({ id }: { id: number }) {
391
+ // TypeScript knows the shape of input and output
392
+ return { id, name: "John", email: "john@example.com" };
393
+ }
394
+ }
395
+ ```
396
+
397
+ ### Avoiding Circular Dependencies
398
+
399
+ **Important:** Avoid circular dependencies between schema files. Circular imports will cause types to become `undefined` during schema construction.
400
+
401
+ ❌ **Bad Example:**
402
+ ```typescript
403
+ // user.schema.ts
404
+ import { getUserByIdResponseSchema } from "./get-user-by-id.schema";
405
+
406
+ export const userSchema = z.object({ ... });
407
+
408
+ export const userServiceSchema = z.object({
409
+ getUserById: z.function({
410
+ input: [...],
411
+ output: getUserByIdResponseSchema, // ❌ Circular dependency
412
+ }),
413
+ });
414
+
415
+ // get-user-by-id.schema.ts
416
+ import { userSchema } from "./user.schema"; // ❌ Circular dependency
417
+
418
+ export const getUserByIdResponseSchema = userSchema;
419
+ ```
420
+
421
+ ✅ **Good Example:**
422
+ ```typescript
423
+ // user.schema.ts
424
+ import { getUserByIdRequestSchema } from "./get-user-by-id.schema";
425
+
426
+ export const userSchema = z.object({ ... });
427
+
428
+ export const userServiceSchema = z.object({
429
+ getUserById: z.function({
430
+ input: [getUserByIdRequestSchema],
431
+ output: userSchema, // ✅ Direct reference, no circular dependency
432
+ }),
433
+ });
434
+
435
+ // get-user-by-id.schema.ts
436
+ export const getUserByIdRequestSchema = z.object({ id: z.number().int() });
437
+ // No import of userSchema needed
438
+ ```
439
+
440
+ ### Multiple Services
441
+
442
+ You can define multiple services in a single protobuf file:
443
+
444
+ ```typescript
445
+ const protoDefinition = zodToProtobuf(z.object(), {
446
+ packageName: "api.v1",
447
+ services: {
448
+ UserService: userServiceSchema,
449
+ AuthService: authServiceSchema,
450
+ ProductService: productServiceSchema,
451
+ },
452
+ });
453
+ ```
454
+
285
455
  ## Limitations
286
456
 
287
457
  - Only the types listed above are supported
288
458
  - Unsupported types will throw `UnsupportedTypeException`
289
459
  - Nested objects are automatically converted to separate messages
290
460
  - Enum values are converted to numbers starting from 0
461
+ - Map keys must be integral types (int32, int64, etc.), strings, or booleans - **not** doubles or floats
462
+ - Avoid circular dependencies between schema files
291
463
 
292
464
  ## License
293
465
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalart/zod-to-proto",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "author": {
5
5
  "name": "GlobalArt, Inc"
6
6
  },
@@ -26,6 +26,17 @@
26
26
  }
27
27
  }
28
28
  },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/GlobalArtInc/ecosystem.git#main"
32
+ },
33
+ "keywords": [
34
+ "zod",
35
+ "protobuf",
36
+ "grpc",
37
+ "protobuf-schema",
38
+ "protobuf-generator"
39
+ ],
29
40
  "scripts": {
30
41
  "format": "prettier --write \"**/*.ts\"",
31
42
  "test": "jest --runInBand --passWithNoTests",