@globalart/zod-to-proto 2.0.5 → 2.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/dist/index.mjs CHANGED
@@ -50,8 +50,8 @@ const getEnumByIndex = (schema, index) => {
50
50
  * @param parentKey - Optional parent message name for nested types
51
51
  * @returns Array of protobuf field definitions
52
52
  */
53
- const traverseArray = ({ key, value, messages, enums, typePrefix, parentKey }) => {
54
- const nestedValue = value instanceof ZodArray ? value.element : value instanceof ZodSet ? value.def.valueType : value.def.element;
53
+ const traverseArray = ({ key, value, messages, enums, typePrefix, parentKey, schemaToMessageName }) => {
54
+ const nestedValue = value instanceof ZodArray ? value.element : value instanceof ZodSet ? value.def.valueType : value.element;
55
55
  const singularKey = inflection.singularize(key);
56
56
  return traverseKey({
57
57
  key: singularKey,
@@ -61,7 +61,8 @@ const traverseArray = ({ key, value, messages, enums, typePrefix, parentKey }) =
61
61
  isOptional: false,
62
62
  isInArray: true,
63
63
  typePrefix,
64
- parentKey: nestedValue instanceof ZodObject ? parentKey : void 0
64
+ parentKey: nestedValue instanceof ZodObject ? parentKey : void 0,
65
+ schemaToMessageName
65
66
  }).map((field) => ({
66
67
  ...field,
67
68
  types: ["repeated", ...field.types],
@@ -80,7 +81,7 @@ const traverseArray = ({ key, value, messages, enums, typePrefix, parentKey }) =
80
81
  * @param parentKey - Optional parent message name for nested types
81
82
  * @returns Array containing a single protobuf map field definition, or empty array if invalid
82
83
  */
83
- const traverseMap = ({ key, value, messages, enums, typePrefix, parentKey }) => {
84
+ const traverseMap = ({ key, value, messages, enums, typePrefix, parentKey, schemaToMessageName }) => {
84
85
  const mapDef = value.def;
85
86
  const keyType = traverseKey({
86
87
  key: inflection.singularize(key),
@@ -90,7 +91,8 @@ const traverseMap = ({ key, value, messages, enums, typePrefix, parentKey }) =>
90
91
  isOptional: false,
91
92
  isInArray: true,
92
93
  typePrefix,
93
- parentKey
94
+ parentKey,
95
+ schemaToMessageName
94
96
  });
95
97
  const valueType = traverseKey({
96
98
  key: inflection.singularize(key),
@@ -100,7 +102,8 @@ const traverseMap = ({ key, value, messages, enums, typePrefix, parentKey }) =>
100
102
  isOptional: false,
101
103
  isInArray: true,
102
104
  typePrefix,
103
- parentKey
105
+ parentKey,
106
+ schemaToMessageName
104
107
  });
105
108
  if (!keyType[0] || keyType.length !== 1) return [];
106
109
  if (!valueType[0] || valueType.length !== 1) return [];
@@ -121,7 +124,7 @@ const traverseMap = ({ key, value, messages, enums, typePrefix, parentKey }) =>
121
124
  * @param parentKey - Optional parent message name for nested types
122
125
  * @returns Array containing a single protobuf map field definition, or empty array if invalid
123
126
  */
124
- const traverseRecord = ({ key, value, messages, enums, typePrefix, parentKey }) => {
127
+ const traverseRecord = ({ key, value, messages, enums, typePrefix, parentKey, schemaToMessageName }) => {
125
128
  const recordDef = value.def;
126
129
  const keyType = traverseKey({
127
130
  key: inflection.singularize(key),
@@ -131,7 +134,8 @@ const traverseRecord = ({ key, value, messages, enums, typePrefix, parentKey })
131
134
  isOptional: false,
132
135
  isInArray: true,
133
136
  typePrefix,
134
- parentKey
137
+ parentKey,
138
+ schemaToMessageName
135
139
  });
136
140
  const valueType = traverseKey({
137
141
  key: inflection.singularize(key),
@@ -141,7 +145,8 @@ const traverseRecord = ({ key, value, messages, enums, typePrefix, parentKey })
141
145
  isOptional: false,
142
146
  isInArray: true,
143
147
  typePrefix,
144
- parentKey
148
+ parentKey,
149
+ schemaToMessageName
145
150
  });
146
151
  if (!keyType[0] || keyType.length !== 1) return [];
147
152
  if (!valueType[0] || valueType.length !== 1) return [];
@@ -164,7 +169,7 @@ const traverseRecord = ({ key, value, messages, enums, typePrefix, parentKey })
164
169
  * @param parentKey - Optional parent message name for nested types
165
170
  * @returns Array of protobuf field definitions
166
171
  */
167
- const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typePrefix, parentKey }) => {
172
+ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typePrefix, parentKey, schemaToMessageName }) => {
168
173
  if (!value) return [];
169
174
  if (value instanceof ZodOptional || value instanceof ZodNullable) return traverseKey({
170
175
  key,
@@ -174,7 +179,8 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
174
179
  isOptional: true,
175
180
  isInArray,
176
181
  typePrefix,
177
- parentKey
182
+ parentKey,
183
+ schemaToMessageName
178
184
  });
179
185
  if (value instanceof ZodArray || value instanceof ZodSet) return traverseArray({
180
186
  key,
@@ -182,7 +188,8 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
182
188
  messages,
183
189
  enums,
184
190
  typePrefix,
185
- parentKey
191
+ parentKey,
192
+ schemaToMessageName
186
193
  });
187
194
  if (value instanceof ZodMap) return traverseMap({
188
195
  key,
@@ -190,7 +197,8 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
190
197
  messages,
191
198
  enums,
192
199
  typePrefix,
193
- parentKey
200
+ parentKey,
201
+ schemaToMessageName
194
202
  });
195
203
  if (value instanceof ZodRecord) return traverseRecord({
196
204
  key,
@@ -198,22 +206,39 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
198
206
  messages,
199
207
  enums,
200
208
  typePrefix,
201
- parentKey
209
+ parentKey,
210
+ schemaToMessageName
202
211
  });
203
212
  const optional = isOptional && !isInArray ? "optional" : null;
204
213
  if (value instanceof ZodObject) {
205
- let messageName = toPascalCase({ value: key });
206
- if (parentKey) if (/^[A-Z][a-zA-Z0-9]*$/.test(parentKey)) messageName = `${parentKey}${messageName}`;
207
- else messageName = `${toPascalCase({ value: parentKey })}${messageName}`;
208
- if (typePrefix) messageName = `${typePrefix}${messageName}`;
214
+ const existingName = schemaToMessageName?.get(value);
215
+ if (existingName !== void 0) return [{
216
+ types: [optional, existingName],
217
+ name: key
218
+ }];
219
+ const keyPascal = toPascalCase({ value: key });
220
+ const shortName = typePrefix ? `${typePrefix}${keyPascal}` : keyPascal;
221
+ const longName = (() => {
222
+ let long = keyPascal;
223
+ if (parentKey) long = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey) ? `${parentKey}${long}` : `${toPascalCase({ value: parentKey })}${long}`;
224
+ return typePrefix ? `${typePrefix}${long}` : long;
225
+ })();
226
+ let messageName;
227
+ if (!messages.has(shortName) && !enums.has(shortName)) messageName = shortName;
228
+ else if (parentKey && (parentKey.endsWith(keyPascal) || longName === `${parentKey}${keyPascal}`)) {
229
+ const suffix = "Item";
230
+ messageName = !messages.has(shortName + suffix) && !enums.has(shortName + suffix) ? shortName + suffix : longName;
231
+ } else messageName = longName;
209
232
  const nestedMessageFields = traverseSchema({
210
233
  schema: value,
211
234
  messages,
212
235
  enums,
213
236
  typePrefix,
214
- parentKey: messageName
237
+ parentKey: messageName,
238
+ schemaToMessageName
215
239
  });
216
240
  messages.set(messageName, nestedMessageFields);
241
+ schemaToMessageName?.set(value, messageName);
217
242
  return [{
218
243
  types: [optional, messageName],
219
244
  name: key
@@ -270,7 +295,8 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
270
295
  isOptional: false,
271
296
  isInArray,
272
297
  typePrefix,
273
- parentKey
298
+ parentKey,
299
+ schemaToMessageName
274
300
  });
275
301
  });
276
302
  let tupleMessageName = toPascalCase({ value: key });
@@ -297,7 +323,7 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
297
323
  * @param parentKey - Optional parent message name for nested types
298
324
  * @returns Array of protobuf field definition strings
299
325
  */
300
- const traverseSchema = ({ schema, messages, enums, typePrefix, parentKey }) => {
326
+ const traverseSchema = ({ schema, messages, enums, typePrefix, parentKey, schemaToMessageName }) => {
301
327
  if (!schema || typeof schema !== "object" || !("def" in schema) || schema.constructor.name !== "ZodObject" && schema.def.type !== "object") return [];
302
328
  const zodObject = schema;
303
329
  return Object.entries(zodObject.shape).flatMap(([key, value]) => {
@@ -309,7 +335,8 @@ const traverseSchema = ({ schema, messages, enums, typePrefix, parentKey }) => {
309
335
  isOptional: false,
310
336
  isInArray: false,
311
337
  typePrefix,
312
- parentKey
338
+ parentKey,
339
+ schemaToMessageName
313
340
  });
314
341
  }).map((field, index) => `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`);
315
342
  };
@@ -406,28 +433,36 @@ const generateResponseMessageName = (methodName, typePrefix) => {
406
433
  * @returns Object containing request and response message names
407
434
  */
408
435
  const processServiceMethod = (method, context) => {
409
- const { messages, enums, typePrefix } = context;
410
- const requestName = generateRequestMessageName(method.name, typePrefix);
411
- const responseName = generateResponseMessageName(method.name, typePrefix);
412
- if (!messages.has(requestName)) {
436
+ const { messages, enums, typePrefix, schemaToMessageName } = context;
437
+ const requestSchema = ensureZodObject(method.request);
438
+ let requestName = schemaToMessageName?.get(requestSchema);
439
+ if (requestName === void 0) {
440
+ requestName = generateRequestMessageName(method.name, typePrefix);
413
441
  const requestFields = traverseSchema({
414
- schema: ensureZodObject(method.request),
442
+ schema: requestSchema,
415
443
  messages,
416
444
  enums,
417
445
  typePrefix,
418
- parentKey: requestName
446
+ parentKey: requestName,
447
+ schemaToMessageName
419
448
  });
420
449
  messages.set(requestName, requestFields);
450
+ schemaToMessageName?.set(requestSchema, requestName);
421
451
  }
422
- if (!messages.has(responseName)) {
452
+ const responseSchema = ensureZodObject(method.response);
453
+ let responseName = schemaToMessageName?.get(responseSchema);
454
+ if (responseName === void 0) {
455
+ responseName = generateResponseMessageName(method.name, typePrefix);
423
456
  const responseFields = traverseSchema({
424
- schema: ensureZodObject(method.response),
457
+ schema: responseSchema,
425
458
  messages,
426
459
  enums,
427
460
  typePrefix,
428
- parentKey: responseName
461
+ parentKey: responseName,
462
+ schemaToMessageName
429
463
  });
430
464
  messages.set(responseName, responseFields);
465
+ schemaToMessageName?.set(responseSchema, responseName);
431
466
  }
432
467
  return {
433
468
  requestName,
@@ -473,7 +508,8 @@ const zodToProtobufService = (options = {}) => {
473
508
  const context = {
474
509
  messages,
475
510
  enums,
476
- typePrefix: typePrefix || null
511
+ typePrefix: typePrefix || null,
512
+ schemaToMessageName: /* @__PURE__ */ new WeakMap()
477
513
  };
478
514
  const content = [
479
515
  services && (Array.isArray(services) ? services.length > 0 : Object.keys(services).length > 0) ? generateServices(services, context) : [],
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import { ZodEnum, ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\n/**\n * Determines the protobuf number type name based on Zod number schema.\n * Returns \"int32\" for integers, \"double\" for floating point numbers.\n *\n * @param value - Zod number schema\n * @returns Protobuf number type name (\"int32\" or \"double\")\n */\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return ['float32', 'float64'].includes(value.format ?? '') ? \"double\" : \"int32\";\n};\n\n/**\n * Converts a string to PascalCase format.\n * Handles dot-separated strings by capitalizing each part.\n *\n * @param value - String to convert\n * @returns PascalCase string\n */\nexport const toPascalCase = ({ value }: { value: string }): string => {\n return value\n .split(\".\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n};\n\n/**\n * Converts a protobuf field definition to its type string representation.\n * Filters out null/empty values and joins the remaining types.\n *\n * @param field - Protobuf field definition\n * @returns Type string for the protobuf field\n */\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\n};\n\nexport const getEnumByIndex = (schema: ZodEnum, index: any): any => {\n return schema.options[index] || null;\n}\n","import * as inflection from \"inflection\";\nimport {\n ZodArray,\n ZodBigInt,\n ZodBoolean,\n ZodDate,\n ZodEnum,\n ZodMap,\n ZodNullable,\n ZodNumber,\n ZodObject,\n ZodOptional,\n ZodRecord,\n ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport {\n ZodArrayDefinition,\n ZodMapDefinition,\n ZodRecordDefinition,\n type ProtobufField,\n} from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\n/**\n * Traverses a Zod array or set schema and converts it to protobuf repeated fields.\n * Handles nested types and generates appropriate field definitions.\n *\n * @param key - Field name\n * @param value - Zod array or set schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definitions\n */\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n}: {\n key: string;\n value: ZodArray<ZodTypeAny> | ZodSet<ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value.def as unknown as ZodArrayDefinition).valueType\n : // @ts-expect-error\n (value.def as unknown as ZodArrayDefinition).element;\n\n const singularKey = inflection.singularize(key);\n const elementFields = traverseKey({\n key: singularKey,\n value: nestedValue,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey: nestedValue instanceof ZodObject ? parentKey : undefined,\n });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\n/**\n * Traverses a Zod map schema and converts it to a protobuf map type.\n * Validates that both key and value types are simple types suitable for map keys/values.\n *\n * @param key - Field name\n * @param value - Zod map schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array containing a single protobuf map field definition, or empty array if invalid\n */\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n}): ProtobufField[] => {\n const mapDef = value.def as ZodMapDefinition;\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value: mapDef.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value: mapDef.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n return [];\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n return [];\n }\n\n const mapType = `map<${protobufFieldToType({ field: keyType[0] })}, ${protobufFieldToType({ field: valueType[0] })}>`;\n return [\n {\n types: [mapType],\n name: key,\n },\n ];\n};\n\n/**\n * Traverses a Zod record schema and converts it to a protobuf map type.\n * Similar to traverseMap but handles ZodRecord type.\n *\n * @param key - Field name\n * @param value - Zod record schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array containing a single protobuf map field definition, or empty array if invalid\n */\nexport const traverseRecord = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n}: {\n key: string;\n value: ZodRecord;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n}): ProtobufField[] => {\n const recordDef = value.def as unknown as ZodRecordDefinition;\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value: recordDef.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value: recordDef.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n return [];\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n return [];\n }\n\n const mapType = `map<${protobufFieldToType({ field: keyType[0] })}, ${protobufFieldToType({ field: valueType[0] })}>`;\n return [\n {\n types: [mapType],\n name: key,\n },\n ];\n};\n\n/**\n * Traverses a single key-value pair from a Zod schema and converts it to protobuf field definitions.\n * Handles various Zod types including primitives, objects, arrays, maps, enums, tuples, and optional/nullable fields.\n *\n * @param key - Field name\n * @param value - Zod schema value\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param isOptional - Whether the field is optional\n * @param isInArray - Whether the field is inside an array\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definitions\n */\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\n parentKey,\n}: {\n key: string;\n value: unknown;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n isOptional: boolean;\n isInArray: boolean;\n typePrefix: string | null;\n parentKey?: string;\n}): ProtobufField[] => {\n if (!value) {\n return [];\n }\n\n if (value instanceof ZodOptional || value instanceof ZodNullable) {\n return traverseKey({\n key,\n value: value.unwrap(),\n messages,\n enums,\n isOptional: true,\n isInArray,\n typePrefix,\n parentKey,\n });\n }\n\n if (value instanceof ZodArray || value instanceof ZodSet) {\n return traverseArray({\n key,\n value: value as ZodArray<ZodTypeAny> | ZodSet<ZodTypeAny>,\n messages,\n enums,\n typePrefix,\n parentKey,\n });\n }\n\n if (value instanceof ZodMap) {\n return traverseMap({\n key,\n value: value as ZodMap<ZodTypeAny, ZodTypeAny>,\n messages,\n enums,\n typePrefix,\n parentKey,\n });\n }\n\n if (value instanceof ZodRecord) {\n return traverseRecord({\n key,\n value: value as ZodRecord,\n messages,\n enums,\n typePrefix,\n parentKey,\n });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n let messageName = toPascalCase({ value: key });\n if (parentKey) {\n const isParentAlreadyPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n if (isParentAlreadyPascalCase) {\n messageName = `${parentKey}${messageName}`;\n } else {\n const parentMessageName = toPascalCase({ value: parentKey });\n messageName = `${parentMessageName}${messageName}`;\n }\n }\n if (typePrefix) {\n messageName = `${typePrefix}${messageName}`;\n }\n const nestedMessageFields = traverseSchema({\n schema: value,\n messages,\n enums,\n typePrefix,\n parentKey: messageName,\n });\n messages.set(messageName, nestedMessageFields);\n return [\n {\n types: [optional, messageName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodString) {\n return [\n {\n types: [optional, \"string\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodNumber) {\n const typeName = getNumberTypeName({ value });\n return [\n {\n types: [optional, typeName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodBoolean) {\n return [\n {\n types: [optional, \"bool\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodEnum) {\n const enumFields = value.options\n .map(\n (option: string | number, index: number) =>\n ` ${String(option)} = ${index};`,\n )\n .join(\"\\n\");\n let enumName = toPascalCase({ value: key });\n if (parentKey) {\n const isParentAlreadyPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n if (isParentAlreadyPascalCase) {\n enumName = `${parentKey}${enumName}`;\n } else {\n const parentMessageName = toPascalCase({ value: parentKey });\n enumName = `${parentMessageName}${enumName}`;\n }\n }\n if (typePrefix) {\n enumName = `${typePrefix}${enumName}`;\n }\n enums.set(enumName, [`enum ${enumName} {\\n${enumFields}\\n}`]);\n return [\n {\n types: [optional, enumName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodDate) {\n return [\n {\n types: [optional, \"string\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodBigInt) {\n return [\n {\n types: [optional, \"int64\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodType) {\n const def = value.def as {\n type?: string;\n check?: unknown;\n fn?: (data: unknown) => boolean;\n };\n if (def.type === \"custom\" && def.check === \"custom\" && def.fn) {\n try {\n if (\n (typeof Buffer !== \"undefined\" && def.fn(Buffer.alloc(0))) ||\n (typeof Uint8Array !== \"undefined\" && def.fn(new Uint8Array(0)))\n ) {\n return [\n {\n types: [optional, \"bytes\"],\n name: key,\n },\n ];\n }\n } catch {}\n }\n }\n\n if (value instanceof ZodTuple) {\n const tupleFields: ProtobufField[] = (\n value.def.items as ZodTypeAny[]\n ).flatMap((item: ZodTypeAny, index: number) => {\n return traverseKey({\n key: `${key}_${index}`,\n value: item,\n messages,\n enums,\n isOptional: false,\n isInArray,\n typePrefix,\n parentKey,\n });\n });\n\n let tupleMessageName = toPascalCase({ value: key });\n if (parentKey) {\n const isParentAlreadyPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n if (isParentAlreadyPascalCase) {\n tupleMessageName = `${parentKey}${tupleMessageName}`;\n } else {\n const parentMessageName = toPascalCase({ value: parentKey });\n tupleMessageName = `${parentMessageName}${tupleMessageName}`;\n }\n }\n if (typePrefix) {\n tupleMessageName = `${typePrefix}${tupleMessageName}`;\n }\n messages.set(\n tupleMessageName,\n tupleFields.map(\n (field, index) =>\n ` ${field.types.join(\" \")} ${field.name} = ${index + 1};`,\n ),\n );\n return [\n {\n types: [optional, tupleMessageName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodType) {\n return [];\n }\n\n return [];\n};\n\n/**\n * Traverses a Zod object schema and converts it to protobuf message field definitions.\n * Processes all fields in the schema and generates appropriate protobuf types.\n *\n * @param schema - Zod schema to traverse\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definition strings\n */\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n parentKey,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n}): string[] => {\n if (\n !schema ||\n typeof schema !== \"object\" ||\n !(\"def\" in schema) ||\n (schema.constructor.name !== \"ZodObject\" &&\n (schema.def as { type?: string }).type !== \"object\")\n ) {\n return [];\n }\n\n const zodObject = schema as ZodObject<any>;\n const fields = Object.entries(zodObject.shape).flatMap(([key, value]) => {\n return traverseKey({\n key,\n value,\n messages,\n enums,\n isOptional: false,\n isInArray: false,\n typePrefix,\n parentKey,\n });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`,\n );\n};\n","import { z, ZodObject, type ZodTypeAny } from \"zod\";\nimport type {\n ServiceDefinition,\n ServiceMethod,\n ServicesInput,\n ZodFunctionDefinition,\n ZodTupleDefinition,\n} from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\n/**\n * Context for generating protobuf services.\n * Contains accumulated messages, enums, and type prefix.\n */\ninterface ServiceGenerationContext {\n /** Map of message names to their protobuf fields */\n messages: Map<string, string[]>;\n /** Map of enum names to their protobuf values */\n enums: Map<string, string[]>;\n /** Prefix for type names or null if prefix is not used */\n typePrefix: string | null;\n}\n\n/**\n * Parses a Zod service schema and extracts method definitions.\n * Each method must be a function with one argument (request) and a return value (response).\n *\n * @param name - Service name\n * @param schema - Zod object containing service methods as functions\n * @returns Service definition with extracted methods\n */\nconst parseZodServiceSchema = (\n name: string,\n schema: ZodObject<Record<string, ZodTypeAny>>,\n): ServiceDefinition => {\n const shape = schema.shape as Record<string, ZodTypeAny>;\n const methods: ServiceMethod[] = [];\n\n for (const [methodName, methodSchema] of Object.entries(shape)) {\n const methodDef = (methodSchema as ZodTypeAny).def as ZodFunctionDefinition;\n\n if (methodDef.type === \"function\") {\n const inputDef = methodDef.input;\n\n const args = (inputDef?.def as ZodTupleDefinition)?.items ?? [];\n const output = methodDef.output as ZodTypeAny;\n\n if (args.length > 0 && args[0] && output) {\n const request = args[0];\n const response = output;\n\n let streaming: \"client\" | \"server\" | \"bidirectional\" | undefined;\n const metadata = methodSchema.meta();\n streaming = metadata?.streaming as\n | \"client\"\n | \"server\"\n | \"bidirectional\"\n | undefined;\n\n methods.push({\n name: methodName,\n request,\n response,\n streaming,\n });\n }\n }\n }\n\n return {\n name,\n methods,\n };\n};\n\n/**\n * Normalizes service input data into an array of service definitions.\n * If an array is passed, returns it as is.\n * If an object is passed, converts each key-value pair into a service definition.\n *\n * @param services - Array of service definitions or object with Zod schemas\n * @returns Array of normalized service definitions\n */\nconst normalizeServices = (services: ServicesInput): ServiceDefinition[] => {\n if (Array.isArray(services)) {\n return services;\n }\n\n return Object.entries(services).map(([name, schema]) =>\n parseZodServiceSchema(name, schema),\n );\n};\n\n/**\n * Ensures that the schema is a ZodObject.\n * If the schema is already an object, returns it as is.\n * Otherwise, wraps the schema in an object with a \"data\" field.\n *\n * @param schema - Zod schema of any type\n * @returns ZodObject containing the original schema\n */\nconst ensureZodObject = (\n schema: ZodTypeAny,\n): ZodObject<Record<string, ZodTypeAny>> => {\n const schemaType =\n (schema.def as { type?: string }).type || schema.constructor.name;\n\n if (schemaType === \"object\" || schema.constructor.name === \"ZodObject\") {\n return schema as ZodObject<Record<string, ZodTypeAny>>;\n }\n\n return z.object({\n data: schema,\n });\n};\n\n/**\n * Generates the name for a request message based on the method name.\n *\n * @param methodName - Name of the service method\n * @param typePrefix - Optional prefix for type names\n * @returns Generated request message name in PascalCase\n */\nconst generateRequestMessageName = (\n methodName: string,\n typePrefix: string | null,\n): string => {\n const messageName = toPascalCase({ value: `${methodName}Request` });\n return typePrefix ? `${typePrefix}${messageName}` : messageName;\n};\n\n/**\n * Generates the name for a response message based on the method name.\n *\n * @param methodName - Name of the service method\n * @param typePrefix - Optional prefix for type names\n * @returns Generated response message name in PascalCase\n */\nconst generateResponseMessageName = (\n methodName: string,\n typePrefix: string | null,\n): string => {\n const messageName = toPascalCase({ value: `${methodName}Response` });\n return typePrefix ? `${typePrefix}${messageName}` : messageName;\n};\n\n/**\n * Processes a service method by generating request and response message names\n * and traversing their schemas to populate the context with message definitions.\n *\n * @param method - Service method definition\n * @param context - Generation context containing messages, enums, and type prefix\n * @returns Object containing request and response message names\n */\nconst processServiceMethod = (\n method: ServiceMethod,\n context: ServiceGenerationContext,\n): { requestName: string; responseName: string } => {\n const { messages, enums, typePrefix } = context;\n\n const requestName = generateRequestMessageName(method.name, typePrefix);\n const responseName = generateResponseMessageName(method.name, typePrefix);\n\n if (!messages.has(requestName)) {\n const requestSchema = ensureZodObject(method.request);\n const requestFields = traverseSchema({\n schema: requestSchema,\n messages,\n enums,\n typePrefix,\n parentKey: requestName,\n });\n messages.set(requestName, requestFields);\n }\n\n if (!messages.has(responseName)) {\n const responseSchema = ensureZodObject(method.response);\n const responseFields = traverseSchema({\n schema: responseSchema,\n messages,\n enums,\n typePrefix,\n parentKey: responseName,\n });\n messages.set(responseName, responseFields);\n }\n\n return { requestName, responseName };\n};\n\n/**\n * Generates protobuf service definitions from Zod service schemas.\n * Supports streaming methods (client, server, bidirectional).\n *\n * @param services - Service definitions as array or object with Zod schemas\n * @param context - Generation context containing messages, enums, and type prefix\n * @returns Array of protobuf service definition strings\n */\nexport const generateServices = (\n services: ServicesInput,\n context: ServiceGenerationContext,\n): string[] => {\n const normalizedServices = normalizeServices(services);\n\n return normalizedServices.map((service) => {\n const methods = service.methods.map((method) => {\n const { requestName, responseName } = processServiceMethod(\n method,\n context,\n );\n\n const requestStreaming =\n method.streaming === \"client\" || method.streaming === \"bidirectional\";\n const responseStreaming =\n method.streaming === \"server\" || method.streaming === \"bidirectional\";\n\n const requestType = requestStreaming\n ? `stream ${requestName}`\n : requestName;\n const responseType = responseStreaming\n ? `stream ${responseName}`\n : responseName;\n\n return ` rpc ${toPascalCase({ value: method.name })}(${requestType}) returns (${responseType});`;\n });\n\n return `service ${toPascalCase({ value: service.name })} {\\n${methods.join(\"\\n\")}\\n}`;\n });\n};\n","import type { ZodTypeAny } from \"zod\";\nimport type { ZodToProtobufOptions } from \"./types\";\nimport { traverseSchema } from \"./traversers\";\nimport { generateServices } from \"./service-generator\";\nimport { z } from \"zod\";\n\n/**\n * Converts a Zod schema to a protobuf definition string.\n * Generates messages, enums, and services based on the provided schema and options.\n *\n * @param schema - Optional Zod schema to convert (if not provided, only services will be generated)\n * @param options - Options for protobuf generation including package name, root message name, type prefix, and services\n * @returns Complete protobuf definition string\n */\nexport const zodToProtobufService = (\n options: ZodToProtobufOptions = {},\n): string => {\n const {\n packageName = \"default\",\n typePrefix = \"\",\n services,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n\n const context = {\n messages,\n enums,\n typePrefix: typePrefix || null,\n };\n\n const hasServices =\n services &&\n (Array.isArray(services)\n ? services.length > 0\n : Object.keys(services).length > 0);\n\n const servicesString = hasServices ? generateServices(services, context) : [];\n\n const enumsString = Array.from(enums.values()).map((enumDef) =>\n enumDef.join(\"\\n\"),\n );\n\n const messagesString = Array.from(messages.entries()).map(\n ([name, fields]) =>\n `message ${name} {\\n${fields.map((field) => ` ${field}`).join(\"\\n\")}\\n}`,\n );\n\n const content = [servicesString, enumsString, messagesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n let protoDefinition: string = \"\";\n if (options.warningDeclaration !== false) {\n protoDefinition += `\n/**\n * This file was automatically generated by zod-to-proto.\n * DO NOT MODIFY IT BY HAND. Instead, modify the source Zod schema.\n */\n`;\n }\n\n protoDefinition += `\nsyntax = \"proto3\";\npackage ${packageName};\n\n${content}\n`;\n\n return protoDefinition.trim();\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import { ZodEnum, ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\n/**\n * Determines the protobuf number type name based on Zod number schema.\n * Returns \"int32\" for integers, \"double\" for floating point numbers.\n *\n * @param value - Zod number schema\n * @returns Protobuf number type name (\"int32\" or \"double\")\n */\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return ['float32', 'float64'].includes(value.format ?? '') ? \"double\" : \"int32\";\n};\n\n/**\n * Converts a string to PascalCase format.\n * Handles dot-separated strings by capitalizing each part.\n *\n * @param value - String to convert\n * @returns PascalCase string\n */\nexport const toPascalCase = ({ value }: { value: string }): string => {\n return value\n .split(\".\")\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n};\n\n/**\n * Converts a protobuf field definition to its type string representation.\n * Filters out null/empty values and joins the remaining types.\n *\n * @param field - Protobuf field definition\n * @returns Type string for the protobuf field\n */\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\n};\n\nexport const getEnumByIndex = (schema: ZodEnum, index: any): any => {\n return schema.options[index] || null;\n}\n","import * as inflection from \"inflection\";\nimport {\n ZodArray,\n ZodBigInt,\n ZodBoolean,\n ZodDate,\n ZodEnum,\n ZodMap,\n ZodNullable,\n ZodNumber,\n ZodObject,\n ZodOptional,\n ZodRecord,\n ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport {\n ZodArrayDefinition,\n ZodMapDefinition,\n ZodRecordDefinition,\n type ProtobufField,\n} from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\n/**\n * Traverses a Zod array or set schema and converts it to protobuf repeated fields.\n * Handles nested types and generates appropriate field definitions.\n *\n * @param key - Field name\n * @param value - Zod array or set schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definitions\n */\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n}: {\n key: string;\n value: ZodArray<ZodTypeAny> | ZodSet<ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value.def as unknown as ZodArrayDefinition).valueType\n : (value as ZodArray<ZodTypeAny>).element;\n\n const singularKey = inflection.singularize(key);\n const elementFields = traverseKey({\n key: singularKey,\n value: nestedValue,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey: nestedValue instanceof ZodObject ? parentKey : undefined,\n schemaToMessageName,\n });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\n/**\n * Traverses a Zod map schema and converts it to a protobuf map type.\n * Validates that both key and value types are simple types suitable for map keys/values.\n *\n * @param key - Field name\n * @param value - Zod map schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array containing a single protobuf map field definition, or empty array if invalid\n */\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}): ProtobufField[] => {\n const mapDef = value.def as ZodMapDefinition;\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value: mapDef.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value: mapDef.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n return [];\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n return [];\n }\n\n const mapType = `map<${protobufFieldToType({ field: keyType[0] })}, ${protobufFieldToType({ field: valueType[0] })}>`;\n return [\n {\n types: [mapType],\n name: key,\n },\n ];\n};\n\n/**\n * Traverses a Zod record schema and converts it to a protobuf map type.\n * Similar to traverseMap but handles ZodRecord type.\n *\n * @param key - Field name\n * @param value - Zod record schema\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array containing a single protobuf map field definition, or empty array if invalid\n */\nexport const traverseRecord = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n}: {\n key: string;\n value: ZodRecord;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}): ProtobufField[] => {\n const recordDef = value.def as unknown as ZodRecordDefinition;\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value: recordDef.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value: recordDef.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n return [];\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n return [];\n }\n\n const mapType = `map<${protobufFieldToType({ field: keyType[0] })}, ${protobufFieldToType({ field: valueType[0] })}>`;\n return [\n {\n types: [mapType],\n name: key,\n },\n ];\n};\n\n/**\n * Traverses a single key-value pair from a Zod schema and converts it to protobuf field definitions.\n * Handles various Zod types including primitives, objects, arrays, maps, enums, tuples, and optional/nullable fields.\n *\n * @param key - Field name\n * @param value - Zod schema value\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param isOptional - Whether the field is optional\n * @param isInArray - Whether the field is inside an array\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definitions\n */\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\n parentKey,\n schemaToMessageName,\n}: {\n key: string;\n value: unknown;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n isOptional: boolean;\n isInArray: boolean;\n typePrefix: string | null;\n parentKey?: string;\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}): ProtobufField[] => {\n if (!value) {\n return [];\n }\n\n if (value instanceof ZodOptional || value instanceof ZodNullable) {\n return traverseKey({\n key,\n value: value.unwrap(),\n messages,\n enums,\n isOptional: true,\n isInArray,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n }\n\n if (value instanceof ZodArray || value instanceof ZodSet) {\n return traverseArray({\n key,\n value: value as ZodArray<ZodTypeAny> | ZodSet<ZodTypeAny>,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n }\n\n if (value instanceof ZodMap) {\n return traverseMap({\n key,\n value: value as ZodMap<ZodTypeAny, ZodTypeAny>,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n }\n\n if (value instanceof ZodRecord) {\n return traverseRecord({\n key,\n value: value as ZodRecord,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n const existingName = schemaToMessageName?.get(value as ZodTypeAny);\n if (existingName !== undefined) {\n return [\n {\n types: [optional, existingName],\n name: key,\n },\n ];\n }\n const keyPascal = toPascalCase({ value: key });\n const shortName = typePrefix ? `${typePrefix}${keyPascal}` : keyPascal;\n const longName = (() => {\n let long = keyPascal;\n if (parentKey) {\n const isParentAlreadyPascalCase =\n /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n long = isParentAlreadyPascalCase\n ? `${parentKey}${long}`\n : `${toPascalCase({ value: parentKey })}${long}`;\n }\n return typePrefix ? `${typePrefix}${long}` : long;\n })();\n let messageName: string;\n if (!messages.has(shortName) && !enums.has(shortName)) {\n messageName = shortName;\n } else if (\n parentKey &&\n (parentKey.endsWith(keyPascal) || longName === `${parentKey}${keyPascal}`)\n ) {\n const suffix = \"Item\";\n messageName = !messages.has(shortName + suffix) && !enums.has(shortName + suffix)\n ? shortName + suffix\n : longName;\n } else {\n messageName = longName;\n }\n const nestedMessageFields = traverseSchema({\n schema: value as ZodTypeAny,\n messages,\n enums,\n typePrefix,\n parentKey: messageName,\n schemaToMessageName,\n });\n messages.set(messageName, nestedMessageFields);\n schemaToMessageName?.set(value as ZodTypeAny, messageName);\n return [\n {\n types: [optional, messageName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodString) {\n return [\n {\n types: [optional, \"string\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodNumber) {\n const typeName = getNumberTypeName({ value });\n return [\n {\n types: [optional, typeName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodBoolean) {\n return [\n {\n types: [optional, \"bool\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodEnum) {\n const enumFields = value.options\n .map(\n (option: string | number, index: number) =>\n ` ${String(option)} = ${index};`,\n )\n .join(\"\\n\");\n let enumName = toPascalCase({ value: key });\n if (parentKey) {\n const isParentAlreadyPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n if (isParentAlreadyPascalCase) {\n enumName = `${parentKey}${enumName}`;\n } else {\n const parentMessageName = toPascalCase({ value: parentKey });\n enumName = `${parentMessageName}${enumName}`;\n }\n }\n if (typePrefix) {\n enumName = `${typePrefix}${enumName}`;\n }\n enums.set(enumName, [`enum ${enumName} {\\n${enumFields}\\n}`]);\n return [\n {\n types: [optional, enumName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodDate) {\n return [\n {\n types: [optional, \"string\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodBigInt) {\n return [\n {\n types: [optional, \"int64\"],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodType) {\n const def = value.def as {\n type?: string;\n check?: unknown;\n fn?: (data: unknown) => boolean;\n };\n if (def.type === \"custom\" && def.check === \"custom\" && def.fn) {\n try {\n if (\n (typeof Buffer !== \"undefined\" && def.fn(Buffer.alloc(0))) ||\n (typeof Uint8Array !== \"undefined\" && def.fn(new Uint8Array(0)))\n ) {\n return [\n {\n types: [optional, \"bytes\"],\n name: key,\n },\n ];\n }\n } catch {}\n }\n }\n\n if (value instanceof ZodTuple) {\n const tupleFields: ProtobufField[] = (\n value.def.items as ZodTypeAny[]\n ).flatMap((item: ZodTypeAny, index: number) => {\n return traverseKey({\n key: `${key}_${index}`,\n value: item,\n messages,\n enums,\n isOptional: false,\n isInArray,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n });\n\n let tupleMessageName = toPascalCase({ value: key });\n if (parentKey) {\n const isParentAlreadyPascalCase = /^[A-Z][a-zA-Z0-9]*$/.test(parentKey);\n if (isParentAlreadyPascalCase) {\n tupleMessageName = `${parentKey}${tupleMessageName}`;\n } else {\n const parentMessageName = toPascalCase({ value: parentKey });\n tupleMessageName = `${parentMessageName}${tupleMessageName}`;\n }\n }\n if (typePrefix) {\n tupleMessageName = `${typePrefix}${tupleMessageName}`;\n }\n messages.set(\n tupleMessageName,\n tupleFields.map(\n (field, index) =>\n ` ${field.types.join(\" \")} ${field.name} = ${index + 1};`,\n ),\n );\n return [\n {\n types: [optional, tupleMessageName],\n name: key,\n },\n ];\n }\n\n if (value instanceof ZodType) {\n return [];\n }\n\n return [];\n};\n\n/**\n * Traverses a Zod object schema and converts it to protobuf message field definitions.\n * Processes all fields in the schema and generates appropriate protobuf types.\n *\n * @param schema - Zod schema to traverse\n * @param messages - Map of message names to their protobuf fields\n * @param enums - Map of enum names to their protobuf values\n * @param typePrefix - Optional prefix for type names\n * @param parentKey - Optional parent message name for nested types\n * @returns Array of protobuf field definition strings\n */\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n parentKey,\n schemaToMessageName,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n parentKey?: string;\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}): string[] => {\n if (\n !schema ||\n typeof schema !== \"object\" ||\n !(\"def\" in schema) ||\n (schema.constructor.name !== \"ZodObject\" &&\n (schema.def as { type?: string }).type !== \"object\")\n ) {\n return [];\n }\n\n const zodObject = schema as ZodObject<Record<string, ZodTypeAny>>;\n const fields = Object.entries(zodObject.shape).flatMap(([key, value]) => {\n return traverseKey({\n key,\n value,\n messages,\n enums,\n isOptional: false,\n isInArray: false,\n typePrefix,\n parentKey,\n schemaToMessageName,\n });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`,\n );\n};\n","import { z, ZodObject, type ZodTypeAny } from \"zod\";\nimport type {\n ServiceDefinition,\n ServiceMethod,\n ServicesInput,\n ZodFunctionDefinition,\n ZodTupleDefinition,\n} from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\n/**\n * Context for generating protobuf services.\n * Contains accumulated messages, enums, and type prefix.\n */\ninterface ServiceGenerationContext {\n /** Map of message names to their protobuf fields */\n messages: Map<string, string[]>;\n /** Map of enum names to their protobuf values */\n enums: Map<string, string[]>;\n /** Prefix for type names or null if prefix is not used */\n typePrefix: string | null;\n /** Map of schema instance to message name for deduplication */\n schemaToMessageName?: WeakMap<ZodTypeAny, string>;\n}\n\n/**\n * Parses a Zod service schema and extracts method definitions.\n * Each method must be a function with one argument (request) and a return value (response).\n *\n * @param name - Service name\n * @param schema - Zod object containing service methods as functions\n * @returns Service definition with extracted methods\n */\nconst parseZodServiceSchema = (\n name: string,\n schema: ZodObject<Record<string, ZodTypeAny>>,\n): ServiceDefinition => {\n const shape = schema.shape as Record<string, ZodTypeAny>;\n const methods: ServiceMethod[] = [];\n\n for (const [methodName, methodSchema] of Object.entries(shape)) {\n const methodDef = (methodSchema as ZodTypeAny).def as ZodFunctionDefinition;\n\n if (methodDef.type === \"function\") {\n const inputDef = methodDef.input;\n\n const args = (inputDef?.def as ZodTupleDefinition)?.items ?? [];\n const output = methodDef.output as ZodTypeAny;\n\n if (args.length > 0 && args[0] && output) {\n const request = args[0];\n const response = output;\n\n let streaming: \"client\" | \"server\" | \"bidirectional\" | undefined;\n const metadata = methodSchema.meta();\n streaming = metadata?.streaming as\n | \"client\"\n | \"server\"\n | \"bidirectional\"\n | undefined;\n\n methods.push({\n name: methodName,\n request,\n response,\n streaming,\n });\n }\n }\n }\n\n return {\n name,\n methods,\n };\n};\n\n/**\n * Normalizes service input data into an array of service definitions.\n * If an array is passed, returns it as is.\n * If an object is passed, converts each key-value pair into a service definition.\n *\n * @param services - Array of service definitions or object with Zod schemas\n * @returns Array of normalized service definitions\n */\nconst normalizeServices = (services: ServicesInput): ServiceDefinition[] => {\n if (Array.isArray(services)) {\n return services;\n }\n\n return Object.entries(services).map(([name, schema]) =>\n parseZodServiceSchema(name, schema),\n );\n};\n\n/**\n * Ensures that the schema is a ZodObject.\n * If the schema is already an object, returns it as is.\n * Otherwise, wraps the schema in an object with a \"data\" field.\n *\n * @param schema - Zod schema of any type\n * @returns ZodObject containing the original schema\n */\nconst ensureZodObject = (\n schema: ZodTypeAny,\n): ZodObject<Record<string, ZodTypeAny>> => {\n const schemaType =\n (schema.def as { type?: string }).type || schema.constructor.name;\n\n if (schemaType === \"object\" || schema.constructor.name === \"ZodObject\") {\n return schema as ZodObject<Record<string, ZodTypeAny>>;\n }\n\n return z.object({\n data: schema,\n });\n};\n\n/**\n * Generates the name for a request message based on the method name.\n *\n * @param methodName - Name of the service method\n * @param typePrefix - Optional prefix for type names\n * @returns Generated request message name in PascalCase\n */\nconst generateRequestMessageName = (\n methodName: string,\n typePrefix: string | null,\n): string => {\n const messageName = toPascalCase({ value: `${methodName}Request` });\n return typePrefix ? `${typePrefix}${messageName}` : messageName;\n};\n\n/**\n * Generates the name for a response message based on the method name.\n *\n * @param methodName - Name of the service method\n * @param typePrefix - Optional prefix for type names\n * @returns Generated response message name in PascalCase\n */\nconst generateResponseMessageName = (\n methodName: string,\n typePrefix: string | null,\n): string => {\n const messageName = toPascalCase({ value: `${methodName}Response` });\n return typePrefix ? `${typePrefix}${messageName}` : messageName;\n};\n\n/**\n * Processes a service method by generating request and response message names\n * and traversing their schemas to populate the context with message definitions.\n *\n * @param method - Service method definition\n * @param context - Generation context containing messages, enums, and type prefix\n * @returns Object containing request and response message names\n */\nconst processServiceMethod = (\n method: ServiceMethod,\n context: ServiceGenerationContext,\n): { requestName: string; responseName: string } => {\n const { messages, enums, typePrefix, schemaToMessageName } = context;\n\n const requestSchema = ensureZodObject(method.request);\n let requestName = schemaToMessageName?.get(requestSchema);\n if (requestName === undefined) {\n requestName = generateRequestMessageName(method.name, typePrefix);\n const requestFields = traverseSchema({\n schema: requestSchema,\n messages,\n enums,\n typePrefix,\n parentKey: requestName,\n schemaToMessageName,\n });\n messages.set(requestName, requestFields);\n schemaToMessageName?.set(requestSchema, requestName);\n }\n\n const responseSchema = ensureZodObject(method.response);\n let responseName = schemaToMessageName?.get(responseSchema);\n if (responseName === undefined) {\n responseName = generateResponseMessageName(method.name, typePrefix);\n const responseFields = traverseSchema({\n schema: responseSchema,\n messages,\n enums,\n typePrefix,\n parentKey: responseName,\n schemaToMessageName,\n });\n messages.set(responseName, responseFields);\n schemaToMessageName?.set(responseSchema, responseName);\n }\n\n return { requestName, responseName };\n};\n\n/**\n * Generates protobuf service definitions from Zod service schemas.\n * Supports streaming methods (client, server, bidirectional).\n *\n * @param services - Service definitions as array or object with Zod schemas\n * @param context - Generation context containing messages, enums, and type prefix\n * @returns Array of protobuf service definition strings\n */\nexport const generateServices = (\n services: ServicesInput,\n context: ServiceGenerationContext,\n): string[] => {\n const normalizedServices = normalizeServices(services);\n\n return normalizedServices.map((service) => {\n const methods = service.methods.map((method) => {\n const { requestName, responseName } = processServiceMethod(\n method,\n context,\n );\n\n const requestStreaming =\n method.streaming === \"client\" || method.streaming === \"bidirectional\";\n const responseStreaming =\n method.streaming === \"server\" || method.streaming === \"bidirectional\";\n\n const requestType = requestStreaming\n ? `stream ${requestName}`\n : requestName;\n const responseType = responseStreaming\n ? `stream ${responseName}`\n : responseName;\n\n return ` rpc ${toPascalCase({ value: method.name })}(${requestType}) returns (${responseType});`;\n });\n\n return `service ${toPascalCase({ value: service.name })} {\\n${methods.join(\"\\n\")}\\n}`;\n });\n};\n","import type { ZodTypeAny } from \"zod\";\nimport type { ZodToProtobufOptions } from \"./types\";\nimport { traverseSchema } from \"./traversers\";\nimport { generateServices } from \"./service-generator\";\nimport { z } from \"zod\";\n\n/**\n * Converts a Zod schema to a protobuf definition string.\n * Generates messages, enums, and services based on the provided schema and options.\n *\n * @param schema - Optional Zod schema to convert (if not provided, only services will be generated)\n * @param options - Options for protobuf generation including package name, root message name, type prefix, and services\n * @returns Complete protobuf definition string\n */\nexport const zodToProtobufService = (\n options: ZodToProtobufOptions = {},\n): string => {\n const {\n packageName = \"default\",\n typePrefix = \"\",\n services,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n const schemaToMessageName = new WeakMap<ZodTypeAny, string>();\n\n const context = {\n messages,\n enums,\n typePrefix: typePrefix || null,\n schemaToMessageName,\n };\n\n const hasServices =\n services &&\n (Array.isArray(services)\n ? services.length > 0\n : Object.keys(services).length > 0);\n\n const servicesString = hasServices ? generateServices(services, context) : [];\n\n const enumsString = Array.from(enums.values()).map((enumDef) =>\n enumDef.join(\"\\n\"),\n );\n\n const messagesString = Array.from(messages.entries()).map(\n ([name, fields]) =>\n `message ${name} {\\n${fields.map((field) => ` ${field}`).join(\"\\n\")}\\n}`,\n );\n\n const content = [servicesString, enumsString, messagesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n let protoDefinition: string = \"\";\n if (options.warningDeclaration !== false) {\n protoDefinition += `\n/**\n * This file was automatically generated by zod-to-proto.\n * DO NOT MODIFY IT BY HAND. Instead, modify the source Zod schema.\n */\n`;\n }\n\n protoDefinition += `\nsyntax = \"proto3\";\npackage ${packageName};\n\n${content}\n`;\n\n return protoDefinition.trim();\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalart/zod-to-proto",
3
- "version": "2.0.5",
3
+ "version": "2.1.0",
4
4
  "author": {
5
5
  "name": "GlobalArt, Inc"
6
6
  },
@@ -39,12 +39,12 @@
39
39
  "description": "Convert Zod schemas to Protobuf definitions",
40
40
  "devDependencies": {
41
41
  "@types/express": "5.0.6",
42
- "@types/node": "25.1.0",
42
+ "@types/node": "25.2.1",
43
43
  "@types/passport": "^1.0.17",
44
44
  "prettier": "3.8.1",
45
45
  "release-it": "19.2.4",
46
46
  "ts-node": "^10.9.2",
47
- "tsdown": "0.20.1",
47
+ "tsdown": "0.20.3",
48
48
  "typescript": "^5.9.3"
49
49
  },
50
50
  "dependencies": {