@globalart/zod-to-proto 1.0.4 → 1.0.6
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.cjs +49 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +50 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65,9 +65,10 @@ const traverseArray = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
65
65
|
}));
|
|
66
66
|
};
|
|
67
67
|
const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
68
|
+
const mapDef = value._def || value.def;
|
|
68
69
|
const keyType = traverseKey({
|
|
69
|
-
key:
|
|
70
|
-
value: value.
|
|
70
|
+
key: inflection.singularize(key),
|
|
71
|
+
value: mapDef.keyType || value.keyType,
|
|
71
72
|
messages,
|
|
72
73
|
enums,
|
|
73
74
|
isOptional: false,
|
|
@@ -75,8 +76,8 @@ const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
75
76
|
typePrefix
|
|
76
77
|
});
|
|
77
78
|
const valueType = traverseKey({
|
|
78
|
-
key:
|
|
79
|
-
value: value.
|
|
79
|
+
key: inflection.singularize(key),
|
|
80
|
+
value: mapDef.valueType || value.valueType,
|
|
80
81
|
messages,
|
|
81
82
|
enums,
|
|
82
83
|
isOptional: false,
|
|
@@ -91,6 +92,7 @@ const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
91
92
|
}];
|
|
92
93
|
};
|
|
93
94
|
const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typePrefix }) => {
|
|
95
|
+
if (!value) return [];
|
|
94
96
|
if (value instanceof zod.ZodOptional || value instanceof zod.ZodNullable) return traverseKey({
|
|
95
97
|
key,
|
|
96
98
|
value: value.unwrap(),
|
|
@@ -183,8 +185,9 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
|
|
|
183
185
|
throw new UnsupportedTypeException(typeof value);
|
|
184
186
|
};
|
|
185
187
|
const traverseSchema = ({ schema, messages, enums, typePrefix }) => {
|
|
186
|
-
if (!(schema
|
|
187
|
-
|
|
188
|
+
if (!schema || typeof schema !== "object" || !("_def" in schema) || schema.constructor.name !== "ZodObject" && schema._def.type !== "object") throw new UnsupportedTypeException(schema?.constructor?.name || typeof schema);
|
|
189
|
+
const zodObject = schema;
|
|
190
|
+
return Object.entries(zodObject.shape).flatMap(([key, value]) => {
|
|
188
191
|
return traverseKey({
|
|
189
192
|
key,
|
|
190
193
|
value,
|
|
@@ -199,6 +202,38 @@ const traverseSchema = ({ schema, messages, enums, typePrefix }) => {
|
|
|
199
202
|
|
|
200
203
|
//#endregion
|
|
201
204
|
//#region src/service-generator.ts
|
|
205
|
+
const parseZodServiceSchema = (name, schema) => {
|
|
206
|
+
const shape = schema.shape;
|
|
207
|
+
const methods = [];
|
|
208
|
+
for (const [methodName, methodSchema] of Object.entries(shape)) {
|
|
209
|
+
const methodDef = methodSchema._def;
|
|
210
|
+
if (methodDef.type === "function") {
|
|
211
|
+
const args = methodDef.input?.def?.items ?? [];
|
|
212
|
+
const output = methodDef.output;
|
|
213
|
+
if (args.length > 0 && args[0] && output) {
|
|
214
|
+
const request = args[0];
|
|
215
|
+
const response = output;
|
|
216
|
+
methods.push({
|
|
217
|
+
name: methodName,
|
|
218
|
+
request,
|
|
219
|
+
response
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
name,
|
|
226
|
+
methods
|
|
227
|
+
};
|
|
228
|
+
};
|
|
229
|
+
const normalizeServices = (services) => {
|
|
230
|
+
if (Array.isArray(services)) return services;
|
|
231
|
+
return Object.entries(services).map(([name, schema]) => parseZodServiceSchema(name, schema));
|
|
232
|
+
};
|
|
233
|
+
const ensureZodObject = (schema) => {
|
|
234
|
+
if ((schema._def.type || schema.constructor.name) === "object" || schema.constructor.name === "ZodObject") return schema;
|
|
235
|
+
return zod.z.object({ data: schema });
|
|
236
|
+
};
|
|
202
237
|
const generateRequestMessageName = (methodName, typePrefix) => {
|
|
203
238
|
const messageName = toPascalCase({ value: `${methodName}Request` });
|
|
204
239
|
return typePrefix ? `${typePrefix}${messageName}` : messageName;
|
|
@@ -213,7 +248,7 @@ const processServiceMethod = (method, context) => {
|
|
|
213
248
|
const responseName = generateResponseMessageName(method.name, typePrefix);
|
|
214
249
|
if (!messages.has(requestName)) {
|
|
215
250
|
const requestFields = traverseSchema({
|
|
216
|
-
schema: method.request,
|
|
251
|
+
schema: ensureZodObject(method.request),
|
|
217
252
|
messages,
|
|
218
253
|
enums,
|
|
219
254
|
typePrefix
|
|
@@ -222,7 +257,7 @@ const processServiceMethod = (method, context) => {
|
|
|
222
257
|
}
|
|
223
258
|
if (!messages.has(responseName)) {
|
|
224
259
|
const responseFields = traverseSchema({
|
|
225
|
-
schema: method.response,
|
|
260
|
+
schema: ensureZodObject(method.response),
|
|
226
261
|
messages,
|
|
227
262
|
enums,
|
|
228
263
|
typePrefix
|
|
@@ -235,23 +270,23 @@ const processServiceMethod = (method, context) => {
|
|
|
235
270
|
};
|
|
236
271
|
};
|
|
237
272
|
const generateServices = (services, context) => {
|
|
238
|
-
return services.map((service) => {
|
|
273
|
+
return normalizeServices(services).map((service) => {
|
|
239
274
|
const methods = service.methods.map((method) => {
|
|
240
275
|
const { requestName, responseName } = processServiceMethod(method, context);
|
|
241
276
|
const requestStreaming = method.streaming === "client" || method.streaming === "bidirectional";
|
|
242
277
|
const responseStreaming = method.streaming === "server" || method.streaming === "bidirectional";
|
|
243
278
|
const requestType = requestStreaming ? `stream ${requestName}` : requestName;
|
|
244
279
|
const responseType = responseStreaming ? `stream ${responseName}` : responseName;
|
|
245
|
-
return ` rpc ${method.name}(${requestType}) returns (${responseType});`;
|
|
280
|
+
return ` rpc ${toPascalCase({ value: method.name })}(${requestType}) returns (${responseType});`;
|
|
246
281
|
});
|
|
247
|
-
return `service ${service.name} {\n${methods.join("\n")}\n}`;
|
|
282
|
+
return `service ${toPascalCase({ value: service.name })} {\n${methods.join("\n")}\n}`;
|
|
248
283
|
});
|
|
249
284
|
};
|
|
250
285
|
|
|
251
286
|
//#endregion
|
|
252
287
|
//#region src/zod-to-protobuf.ts
|
|
253
288
|
const zodToProtobuf = (schema, options = {}) => {
|
|
254
|
-
const { packageName = "default", rootMessageName = "Message", typePrefix = "", services
|
|
289
|
+
const { packageName = "default", rootMessageName = "Message", typePrefix = "", services, skipRootMessage = false } = options;
|
|
255
290
|
const messages = /* @__PURE__ */ new Map();
|
|
256
291
|
const enums = /* @__PURE__ */ new Map();
|
|
257
292
|
if (schema && !skipRootMessage) {
|
|
@@ -271,7 +306,7 @@ const zodToProtobuf = (schema, options = {}) => {
|
|
|
271
306
|
enums,
|
|
272
307
|
typePrefix: typePrefix || null
|
|
273
308
|
};
|
|
274
|
-
|
|
309
|
+
const servicesString = services && (Array.isArray(services) ? services.length > 0 : Object.keys(services).length > 0) ? generateServices(services, context) : [];
|
|
275
310
|
return `
|
|
276
311
|
syntax = "proto3";
|
|
277
312
|
package ${packageName};
|
|
@@ -279,7 +314,7 @@ package ${packageName};
|
|
|
279
314
|
${[
|
|
280
315
|
Array.from(enums.values()).map((enumDef) => enumDef.join("\n")),
|
|
281
316
|
Array.from(messages.entries()).map(([name, fields]) => `message ${name} {\n${fields.map((field) => ` ${field}`).join("\n")}\n}`),
|
|
282
|
-
|
|
317
|
+
servicesString
|
|
283
318
|
].filter((strings) => !!strings.length).map((strings) => strings.join("\n\n")).join("\n\n")}
|
|
284
319
|
`.trim();
|
|
285
320
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ZodArray","ZodSet","ZodOptional","ZodNullable","ZodMap","ZodObject","ZodString","ZodNumber","ZodBoolean","ZodEnum","ZodDate","ZodBigInt","ZodTuple","tupleFields: ProtobufField[]","ZodType"],"sources":["../src/types.ts","../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import type { ZodTypeAny } from \"zod\";\n\nexport interface ZodToProtobufOptions {\n packageName?: string;\n rootMessageName?: string;\n typePrefix?: string;\n services?: ServiceDefinition[];\n skipRootMessage?: boolean;\n}\n\nexport interface ServiceMethod {\n name: string;\n request: ZodTypeAny;\n response: ZodTypeAny;\n streaming?: \"client\" | \"server\" | \"bidirectional\";\n}\n\nexport interface ServiceDefinition {\n name: string;\n methods: ServiceMethod[];\n}\n\nexport class UnsupportedTypeException extends Error {\n constructor(type: string) {\n super(`Unsupported type: ${type}`);\n this.name = \"UnsupportedTypeException\";\n }\n}\n\nexport interface ProtobufField {\n types: Array<string | null>;\n name: string;\n}\n","import { ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return value.isInt ? \"int32\" : \"double\";\n};\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\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\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 ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport { UnsupportedTypeException, type ProtobufField } from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\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}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value._def as { valueType: ZodTypeAny }).valueType\n : // @ts-expect-error\n (value._def as { element?: 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 });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): ProtobufField[] => {\n const keyType = traverseKey({\n key: `${key}Key`,\n value: value._def.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n const valueType = traverseKey({\n key: `${key}Value`,\n value: value._def.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map key`);\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map value`);\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\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\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}): ProtobufField[] => {\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 });\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 });\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 });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n let messageName = toPascalCase({ value: key });\n if (typePrefix) {\n messageName = `${typePrefix}${messageName}`;\n }\n const nestedMessageFields = traverseSchema({\n schema: value,\n messages,\n enums,\n typePrefix,\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 (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 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 });\n });\n\n const tupleMessageName = toPascalCase({ value: key });\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 throw new UnsupportedTypeException(value.constructor.name);\n }\n\n throw new UnsupportedTypeException(typeof value);\n};\n\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): string[] => {\n if (!(schema instanceof ZodObject)) {\n throw new UnsupportedTypeException(schema.constructor.name);\n }\n\n const fields = Object.entries(schema.shape).flatMap(([key, value]) => {\n return traverseKey({\n key,\n value,\n messages,\n enums,\n isOptional: false,\n isInArray: false,\n typePrefix,\n });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`,\n );\n};\n","import type { ZodTypeAny } from \"zod\";\nimport type { ServiceDefinition, ServiceMethod } from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\ninterface ServiceGenerationContext {\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}\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\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\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 requestFields = traverseSchema({\n schema: method.request,\n messages,\n enums,\n typePrefix,\n });\n messages.set(requestName, requestFields);\n }\n\n if (!messages.has(responseName)) {\n const responseFields = traverseSchema({\n schema: method.response,\n messages,\n enums,\n typePrefix,\n });\n messages.set(responseName, responseFields);\n }\n\n return { requestName, responseName };\n};\n\nexport const generateServices = (\n services: ServiceDefinition[],\n context: ServiceGenerationContext,\n): string[] => {\n return services.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 ${method.name}(${requestType}) returns (${responseType});`;\n });\n\n return `service ${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\";\n\nexport const zodToProtobuf = (\n schema?: ZodTypeAny,\n options: ZodToProtobufOptions = {},\n): string => {\n const {\n packageName = \"default\",\n rootMessageName = \"Message\",\n typePrefix = \"\",\n services = [],\n skipRootMessage = false,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n\n if (schema && !skipRootMessage) {\n const fields = traverseSchema({ schema, messages, enums, typePrefix });\n if (fields.length > 0) {\n const rootMessageKey = `${typePrefix}${rootMessageName}`;\n messages.set(rootMessageKey, fields);\n }\n }\n\n const context = {\n messages,\n enums,\n typePrefix: typePrefix || null,\n };\n\n if (services.length > 0) {\n generateServices(services, context);\n }\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 servicesString =\n services.length > 0 ? generateServices(services, context) : [];\n\n const content = [enumsString, messagesString, servicesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n const 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.cjs","names":["ZodArray","ZodSet","ZodOptional","ZodNullable","ZodMap","ZodObject","ZodString","ZodNumber","ZodBoolean","ZodEnum","ZodDate","ZodBigInt","ZodTuple","tupleFields: ProtobufField[]","ZodType","methods: ServiceMethod[]","z"],"sources":["../src/types.ts","../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import type { ZodObject, ZodTypeAny } from \"zod\";\n\nexport type ServicesInput =\n | ServiceDefinition[]\n | Record<string, ZodObject<any>>;\n\nexport interface ZodToProtobufOptions {\n packageName?: string;\n rootMessageName?: string;\n typePrefix?: string;\n services?: ServicesInput;\n skipRootMessage?: boolean;\n}\n\nexport interface ServiceMethod {\n name: string;\n request: ZodTypeAny;\n response: ZodTypeAny;\n streaming?: \"client\" | \"server\" | \"bidirectional\";\n}\n\nexport interface ServiceDefinition {\n name: string;\n methods: ServiceMethod[];\n}\n\nexport class UnsupportedTypeException extends Error {\n constructor(type: string) {\n super(`Unsupported type: ${type}`);\n this.name = \"UnsupportedTypeException\";\n }\n}\n\nexport interface ProtobufField {\n types: Array<string | null>;\n name: string;\n}\n","import { ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return value.isInt ? \"int32\" : \"double\";\n};\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\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\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 ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport { UnsupportedTypeException, type ProtobufField } from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\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}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value._def as { valueType: ZodTypeAny }).valueType\n : // @ts-expect-error\n (value._def as { element?: 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 });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): ProtobufField[] => {\n const mapDef = (value._def || value.def) as {\n keyType?: ZodTypeAny;\n valueType?: ZodTypeAny;\n };\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value:\n mapDef.keyType ||\n (value as ZodTypeAny & { keyType?: ZodTypeAny }).keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value:\n mapDef.valueType ||\n (value as ZodTypeAny & { valueType?: ZodTypeAny }).valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map key`);\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map value`);\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\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\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}): 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 });\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 });\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 });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n let messageName = toPascalCase({ value: key });\n if (typePrefix) {\n messageName = `${typePrefix}${messageName}`;\n }\n const nestedMessageFields = traverseSchema({\n schema: value,\n messages,\n enums,\n typePrefix,\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 (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 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 });\n });\n\n const tupleMessageName = toPascalCase({ value: key });\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 throw new UnsupportedTypeException(value.constructor.name);\n }\n\n throw new UnsupportedTypeException(typeof value);\n};\n\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\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 throw new UnsupportedTypeException(\n schema?.constructor?.name || typeof schema\n );\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 });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`\n );\n};\n","import { z, type ZodFunction, ZodObject, type ZodTypeAny } from \"zod\";\nimport type { ServiceDefinition, ServiceMethod, ServicesInput } from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\ninterface ServiceGenerationContext {\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}\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 {\n type?: string;\n input?: ZodTypeAny;\n output?: ZodTypeAny;\n };\n\n if (methodDef.type === \"function\") {\n const inputDef = methodDef.input as ZodTypeAny & {\n def?: { items?: ZodTypeAny[] };\n };\n\n const args = inputDef?.def?.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 methods.push({\n name: methodName,\n request,\n response,\n });\n }\n }\n }\n\n return {\n name,\n methods,\n };\n};\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\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\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\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\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 });\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 });\n messages.set(responseName, responseFields);\n }\n\n return { requestName, responseName };\n};\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\";\n\nexport const zodToProtobuf = (\n schema?: ZodTypeAny,\n options: ZodToProtobufOptions = {}\n): string => {\n const {\n packageName = \"default\",\n rootMessageName = \"Message\",\n typePrefix = \"\",\n services,\n skipRootMessage = false,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n\n if (schema && !skipRootMessage) {\n const fields = traverseSchema({ schema, messages, enums, typePrefix });\n if (fields.length > 0) {\n const rootMessageKey = `${typePrefix}${rootMessageName}`;\n messages.set(rootMessageKey, fields);\n }\n }\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 = [enumsString, messagesString, servicesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n const protoDefinition = `\nsyntax = \"proto3\";\npackage ${packageName};\n\n${content}\n`;\n\n return protoDefinition.trim();\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,IAAa,2BAAb,cAA8C,MAAM;CAClD,YAAY,MAAc;AACxB,QAAM,qBAAqB,OAAO;AAClC,OAAK,OAAO;;;;;;AC1BhB,MAAa,qBAAqB,EAAE,YAA0C;AAC5E,QAAO,MAAM,QAAQ,UAAU;;AAGjC,MAAa,gBAAgB,EAAE,YAAuC;AACpE,QAAO,MACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,MAAa,uBAAuB,EAClC,YAGY;AACZ,QAAO,MAAM,MAAM,OAAO,QAAQ,CAAC,KAAK,IAAI;;;;;ACE9C,MAAa,iBAAiB,EAC5B,KACA,OACA,UACA,OACA,iBAOqB;CACrB,MAAM,cACJ,iBAAiBA,eACb,MAAM,UACN,iBAAiBC,aACd,MAAM,KAAmC,YAEzC,MAAM,KAAkC;CAEjD,MAAM,cAAc,WAAW,YAAY,IAAI;AAU/C,QATsB,YAAY;EAChC,KAAK;EACL,OAAO;EACP;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC,CACmB,KAAK,WAAW;EACnC,GAAG;EACH,OAAO,CAAC,YAAY,GAAG,MAAM,MAAM;EACnC,MAAM,MAAM,KAAK,QAAQ,aAAa,IAAI;EAC3C,EAAE;;AAGL,MAAa,eAAe,EAC1B,KACA,OACA,UACA,OACA,iBAOqB;CACrB,MAAM,SAAU,MAAM,QAAQ,MAAM;CAKpC,MAAM,UAAU,YAAY;EAC1B,KAAK,WAAW,YAAY,IAAI;EAChC,OACE,OAAO,WACN,MAAgD;EACnD;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC;CACF,MAAM,YAAY,YAAY;EAC5B,KAAK,WAAW,YAAY,IAAI;EAChC,OACE,OAAO,aACN,MAAkD;EACrD;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC;AAEF,KAAI,CAAC,QAAQ,MAAM,QAAQ,WAAW,EACpC,OAAM,IAAI,yBAAyB,GAAG,IAAI,UAAU;AAGtD,KAAI,CAAC,UAAU,MAAM,UAAU,WAAW,EACxC,OAAM,IAAI,yBAAyB,GAAG,IAAI,YAAY;AAIxD,QAAO,CACL;EACE,OAAO,CAHK,OAAO,oBAAoB,EAAE,OAAO,QAAQ,IAAI,CAAC,CAAC,IAAI,oBAAoB,EAAE,OAAO,UAAU,IAAI,CAAC,CAAC,GAG/F;EAChB,MAAM;EACP,CACF;;AAGH,MAAa,eAAe,EAC1B,KACA,OACA,UACA,OACA,YACA,WACA,iBASqB;AACrB,KAAI,CAAC,MACH,QAAO,EAAE;AAGX,KAAI,iBAAiBC,mBAAe,iBAAiBC,gBACnD,QAAO,YAAY;EACjB;EACA,OAAO,MAAM,QAAQ;EACrB;EACA;EACA,YAAY;EACZ;EACA;EACD,CAAC;AAGJ,KAAI,iBAAiBH,gBAAY,iBAAiBC,WAChD,QAAO,cAAc;EACnB;EACO;EACP;EACA;EACA;EACD,CAAC;AAGJ,KAAI,iBAAiBG,WACnB,QAAO,YAAY;EACjB;EACO;EACP;EACA;EACA;EACD,CAAC;CAGJ,MAAM,WAAW,cAAc,CAAC,YAAY,aAAa;AAEzD,KAAI,iBAAiBC,eAAW;EAC9B,IAAI,cAAc,aAAa,EAAE,OAAO,KAAK,CAAC;AAC9C,MAAI,WACF,eAAc,GAAG,aAAa;EAEhC,MAAM,sBAAsB,eAAe;GACzC,QAAQ;GACR;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,aAAa,oBAAoB;AAC9C,SAAO,CACL;GACE,OAAO,CAAC,UAAU,YAAY;GAC9B,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiBC,cACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,SAAS;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiBC,cAEnB,QAAO,CACL;EACE,OAAO,CAAC,UAHK,kBAAkB,EAAE,OAAO,CAAC,CAGd;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiBC,eACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,OAAO;EACzB,MAAM;EACP,CACF;AAGH,KAAI,iBAAiBC,aAAS;EAC5B,MAAM,aAAa,MAAM,QACtB,KACE,QAAyB,UACxB,OAAO,OAAO,OAAO,CAAC,KAAK,MAAM,GACpC,CACA,KAAK,KAAK;EACb,IAAI,WAAW,aAAa,EAAE,OAAO,KAAK,CAAC;AAC3C,MAAI,WACF,YAAW,GAAG,aAAa;AAE7B,QAAM,IAAI,UAAU,CAAC,QAAQ,SAAS,MAAM,WAAW,KAAK,CAAC;AAC7D,SAAO,CACL;GACE,OAAO,CAAC,UAAU,SAAS;GAC3B,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiBC,YACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,SAAS;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiBC,cACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,QAAQ;EAC1B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiBC,cAAU;EAC7B,MAAMC,cACJ,MAAM,KAAK,MACX,SAAS,MAAkB,UAAkB;AAC7C,UAAO,YAAY;IACjB,KAAK,GAAG,IAAI,GAAG;IACf,OAAO;IACP;IACA;IACA,YAAY;IACZ;IACA;IACD,CAAC;IACF;EAEF,MAAM,mBAAmB,aAAa,EAAE,OAAO,KAAK,CAAC;AACrD,WAAS,IACP,kBACA,YAAY,KACT,OAAO,UACN,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC,GAAG,MAAM,KAAK,KAAK,QAAQ,EAAE,GAC3D,CACF;AACD,SAAO,CACL;GACE,OAAO,CAAC,UAAU,iBAAiB;GACnC,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiBC,YACnB,OAAM,IAAI,yBAAyB,MAAM,YAAY,KAAK;AAG5D,OAAM,IAAI,yBAAyB,OAAO,MAAM;;AAGlD,MAAa,kBAAkB,EAC7B,QACA,UACA,OACA,iBAMc;AACd,KACE,CAAC,UACD,OAAO,WAAW,YAClB,EAAE,UAAU,WACX,OAAO,YAAY,SAAS,eAC1B,OAAO,KAA2B,SAAS,SAE9C,OAAM,IAAI,yBACR,QAAQ,aAAa,QAAQ,OAAO,OACrC;CAGH,MAAM,YAAY;AAalB,QAZe,OAAO,QAAQ,UAAU,MAAM,CAAC,SAAS,CAAC,KAAK,WAAW;AACvE,SAAO,YAAY;GACjB;GACA;GACA;GACA;GACA,YAAY;GACZ,WAAW;GACX;GACD,CAAC;GACF,CAEY,KACX,OAAO,UACN,GAAG,oBAAoB,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,KAAK,KAAK,QAAQ,EAAE,GAClE;;;;;ACpUH,MAAM,yBACJ,MACA,WACsB;CACtB,MAAM,QAAQ,OAAO;CACrB,MAAMC,UAA2B,EAAE;AAEnC,MAAK,MAAM,CAAC,YAAY,iBAAiB,OAAO,QAAQ,MAAM,EAAE;EAC9D,MAAM,YAAa,aAA4B;AAM/C,MAAI,UAAU,SAAS,YAAY;GAKjC,MAAM,OAJW,UAAU,OAIJ,KAAK,SAAS,EAAE;GACvC,MAAM,SAAS,UAAU;AAEzB,OAAI,KAAK,SAAS,KAAK,KAAK,MAAM,QAAQ;IACxC,MAAM,UAAU,KAAK;IACrB,MAAM,WAAW;AAEjB,YAAQ,KAAK;KACX,MAAM;KACN;KACA;KACD,CAAC;;;;AAKR,QAAO;EACL;EACA;EACD;;AAGH,MAAM,qBAAqB,aAAiD;AAC1E,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO;AAGT,QAAO,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,MAAM,YAC1C,sBAAsB,MAAM,OAAO,CACpC;;AAGH,MAAM,mBACJ,WAC0C;AAI1C,MAFG,OAAO,KAA2B,QAAQ,OAAO,YAAY,UAE7C,YAAY,OAAO,YAAY,SAAS,YACzD,QAAO;AAGT,QAAOC,MAAE,OAAO,EACd,MAAM,QACP,CAAC;;AAGJ,MAAM,8BACJ,YACA,eACW;CACX,MAAM,cAAc,aAAa,EAAE,OAAO,GAAG,WAAW,UAAU,CAAC;AACnE,QAAO,aAAa,GAAG,aAAa,gBAAgB;;AAGtD,MAAM,+BACJ,YACA,eACW;CACX,MAAM,cAAc,aAAa,EAAE,OAAO,GAAG,WAAW,WAAW,CAAC;AACpE,QAAO,aAAa,GAAG,aAAa,gBAAgB;;AAGtD,MAAM,wBACJ,QACA,YACkD;CAClD,MAAM,EAAE,UAAU,OAAO,eAAe;CAExC,MAAM,cAAc,2BAA2B,OAAO,MAAM,WAAW;CACvE,MAAM,eAAe,4BAA4B,OAAO,MAAM,WAAW;AAEzE,KAAI,CAAC,SAAS,IAAI,YAAY,EAAE;EAE9B,MAAM,gBAAgB,eAAe;GACnC,QAFoB,gBAAgB,OAAO,QAAQ;GAGnD;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,aAAa,cAAc;;AAG1C,KAAI,CAAC,SAAS,IAAI,aAAa,EAAE;EAE/B,MAAM,iBAAiB,eAAe;GACpC,QAFqB,gBAAgB,OAAO,SAAS;GAGrD;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,cAAc,eAAe;;AAG5C,QAAO;EAAE;EAAa;EAAc;;AAGtC,MAAa,oBACX,UACA,YACa;AAGb,QAF2B,kBAAkB,SAAS,CAE5B,KAAK,YAAY;EACzC,MAAM,UAAU,QAAQ,QAAQ,KAAK,WAAW;GAC9C,MAAM,EAAE,aAAa,iBAAiB,qBACpC,QACA,QACD;GAED,MAAM,mBACJ,OAAO,cAAc,YAAY,OAAO,cAAc;GACxD,MAAM,oBACJ,OAAO,cAAc,YAAY,OAAO,cAAc;GAExD,MAAM,cAAc,mBAChB,UAAU,gBACV;GACJ,MAAM,eAAe,oBACjB,UAAU,iBACV;AAEJ,UAAO,WAAW,aAAa,EAAE,OAAO,OAAO,MAAM,CAAC,CAAC,GAAG,YAAY,aAAa,aAAa;IAChG;AAEF,SAAO,WAAW,aAAa,EAAE,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC;GACjF;;;;;ACvJJ,MAAa,iBACX,QACA,UAAgC,EAAE,KACvB;CACX,MAAM,EACJ,cAAc,WACd,kBAAkB,WAClB,aAAa,IACb,UACA,kBAAkB,UAChB;CAEJ,MAAM,2BAAW,IAAI,KAAuB;CAC5C,MAAM,wBAAQ,IAAI,KAAuB;AAEzC,KAAI,UAAU,CAAC,iBAAiB;EAC9B,MAAM,SAAS,eAAe;GAAE;GAAQ;GAAU;GAAO;GAAY,CAAC;AACtE,MAAI,OAAO,SAAS,GAAG;GACrB,MAAM,iBAAiB,GAAG,aAAa;AACvC,YAAS,IAAI,gBAAgB,OAAO;;;CAIxC,MAAM,UAAU;EACd;EACA;EACA,YAAY,cAAc;EAC3B;CAQD,MAAM,iBALJ,aACC,MAAM,QAAQ,SAAS,GACpB,SAAS,SAAS,IAClB,OAAO,KAAK,SAAS,CAAC,SAAS,KAEA,iBAAiB,UAAU,QAAQ,GAAG,EAAE;AAuB7E,QAPwB;;UAEhB,YAAY;;EAPJ;EATI,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK,YAClD,QAAQ,KAAK,KAAK,CACnB;EAEsB,MAAM,KAAK,SAAS,SAAS,CAAC,CAAC,KACnD,CAAC,MAAM,YACN,WAAW,KAAK,MAAM,OAAO,KAAK,UAAU,OAAO,QAAQ,CAAC,KAAK,KAAK,CAAC,KAC1E;EAE6C;EAAe,CAC1D,QAAQ,YAAY,CAAC,CAAC,QAAQ,OAAO,CACrC,KAAK,YAAY,QAAQ,KAAK,OAAO,CAAC,CACtC,KAAK,OAAO,CAMP;EAGe,MAAM"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { ZodTypeAny } from "zod";
|
|
1
|
+
import { ZodObject, ZodTypeAny } from "zod";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
+
type ServicesInput = ServiceDefinition[] | Record<string, ZodObject<any>>;
|
|
4
5
|
interface ZodToProtobufOptions {
|
|
5
6
|
packageName?: string;
|
|
6
7
|
rootMessageName?: string;
|
|
7
8
|
typePrefix?: string;
|
|
8
|
-
services?:
|
|
9
|
+
services?: ServicesInput;
|
|
9
10
|
skipRootMessage?: boolean;
|
|
10
11
|
}
|
|
11
12
|
interface ServiceMethod {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { ZodTypeAny } from "zod";
|
|
1
|
+
import { ZodObject, ZodTypeAny } from "zod";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
|
+
type ServicesInput = ServiceDefinition[] | Record<string, ZodObject<any>>;
|
|
4
5
|
interface ZodToProtobufOptions {
|
|
5
6
|
packageName?: string;
|
|
6
7
|
rootMessageName?: string;
|
|
7
8
|
typePrefix?: string;
|
|
8
|
-
services?:
|
|
9
|
+
services?: ServicesInput;
|
|
9
10
|
skipRootMessage?: boolean;
|
|
10
11
|
}
|
|
11
12
|
interface ServiceMethod {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as inflection from "inflection";
|
|
2
|
-
import { ZodArray, ZodBigInt, ZodBoolean, ZodDate, ZodEnum, ZodMap, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodSet, ZodString, ZodTuple, ZodType } from "zod";
|
|
2
|
+
import { ZodArray, ZodBigInt, ZodBoolean, ZodDate, ZodEnum, ZodMap, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodSet, ZodString, ZodTuple, ZodType, z } from "zod";
|
|
3
3
|
|
|
4
4
|
//#region src/types.ts
|
|
5
5
|
var UnsupportedTypeException = class extends Error {
|
|
@@ -41,9 +41,10 @@ const traverseArray = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
41
41
|
}));
|
|
42
42
|
};
|
|
43
43
|
const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
44
|
+
const mapDef = value._def || value.def;
|
|
44
45
|
const keyType = traverseKey({
|
|
45
|
-
key:
|
|
46
|
-
value: value.
|
|
46
|
+
key: inflection.singularize(key),
|
|
47
|
+
value: mapDef.keyType || value.keyType,
|
|
47
48
|
messages,
|
|
48
49
|
enums,
|
|
49
50
|
isOptional: false,
|
|
@@ -51,8 +52,8 @@ const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
51
52
|
typePrefix
|
|
52
53
|
});
|
|
53
54
|
const valueType = traverseKey({
|
|
54
|
-
key:
|
|
55
|
-
value: value.
|
|
55
|
+
key: inflection.singularize(key),
|
|
56
|
+
value: mapDef.valueType || value.valueType,
|
|
56
57
|
messages,
|
|
57
58
|
enums,
|
|
58
59
|
isOptional: false,
|
|
@@ -67,6 +68,7 @@ const traverseMap = ({ key, value, messages, enums, typePrefix }) => {
|
|
|
67
68
|
}];
|
|
68
69
|
};
|
|
69
70
|
const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typePrefix }) => {
|
|
71
|
+
if (!value) return [];
|
|
70
72
|
if (value instanceof ZodOptional || value instanceof ZodNullable) return traverseKey({
|
|
71
73
|
key,
|
|
72
74
|
value: value.unwrap(),
|
|
@@ -159,8 +161,9 @@ const traverseKey = ({ key, value, messages, enums, isOptional, isInArray, typeP
|
|
|
159
161
|
throw new UnsupportedTypeException(typeof value);
|
|
160
162
|
};
|
|
161
163
|
const traverseSchema = ({ schema, messages, enums, typePrefix }) => {
|
|
162
|
-
if (!(schema
|
|
163
|
-
|
|
164
|
+
if (!schema || typeof schema !== "object" || !("_def" in schema) || schema.constructor.name !== "ZodObject" && schema._def.type !== "object") throw new UnsupportedTypeException(schema?.constructor?.name || typeof schema);
|
|
165
|
+
const zodObject = schema;
|
|
166
|
+
return Object.entries(zodObject.shape).flatMap(([key, value]) => {
|
|
164
167
|
return traverseKey({
|
|
165
168
|
key,
|
|
166
169
|
value,
|
|
@@ -175,6 +178,38 @@ const traverseSchema = ({ schema, messages, enums, typePrefix }) => {
|
|
|
175
178
|
|
|
176
179
|
//#endregion
|
|
177
180
|
//#region src/service-generator.ts
|
|
181
|
+
const parseZodServiceSchema = (name, schema) => {
|
|
182
|
+
const shape = schema.shape;
|
|
183
|
+
const methods = [];
|
|
184
|
+
for (const [methodName, methodSchema] of Object.entries(shape)) {
|
|
185
|
+
const methodDef = methodSchema._def;
|
|
186
|
+
if (methodDef.type === "function") {
|
|
187
|
+
const args = methodDef.input?.def?.items ?? [];
|
|
188
|
+
const output = methodDef.output;
|
|
189
|
+
if (args.length > 0 && args[0] && output) {
|
|
190
|
+
const request = args[0];
|
|
191
|
+
const response = output;
|
|
192
|
+
methods.push({
|
|
193
|
+
name: methodName,
|
|
194
|
+
request,
|
|
195
|
+
response
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
methods
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
const normalizeServices = (services) => {
|
|
206
|
+
if (Array.isArray(services)) return services;
|
|
207
|
+
return Object.entries(services).map(([name, schema]) => parseZodServiceSchema(name, schema));
|
|
208
|
+
};
|
|
209
|
+
const ensureZodObject = (schema) => {
|
|
210
|
+
if ((schema._def.type || schema.constructor.name) === "object" || schema.constructor.name === "ZodObject") return schema;
|
|
211
|
+
return z.object({ data: schema });
|
|
212
|
+
};
|
|
178
213
|
const generateRequestMessageName = (methodName, typePrefix) => {
|
|
179
214
|
const messageName = toPascalCase({ value: `${methodName}Request` });
|
|
180
215
|
return typePrefix ? `${typePrefix}${messageName}` : messageName;
|
|
@@ -189,7 +224,7 @@ const processServiceMethod = (method, context) => {
|
|
|
189
224
|
const responseName = generateResponseMessageName(method.name, typePrefix);
|
|
190
225
|
if (!messages.has(requestName)) {
|
|
191
226
|
const requestFields = traverseSchema({
|
|
192
|
-
schema: method.request,
|
|
227
|
+
schema: ensureZodObject(method.request),
|
|
193
228
|
messages,
|
|
194
229
|
enums,
|
|
195
230
|
typePrefix
|
|
@@ -198,7 +233,7 @@ const processServiceMethod = (method, context) => {
|
|
|
198
233
|
}
|
|
199
234
|
if (!messages.has(responseName)) {
|
|
200
235
|
const responseFields = traverseSchema({
|
|
201
|
-
schema: method.response,
|
|
236
|
+
schema: ensureZodObject(method.response),
|
|
202
237
|
messages,
|
|
203
238
|
enums,
|
|
204
239
|
typePrefix
|
|
@@ -211,23 +246,23 @@ const processServiceMethod = (method, context) => {
|
|
|
211
246
|
};
|
|
212
247
|
};
|
|
213
248
|
const generateServices = (services, context) => {
|
|
214
|
-
return services.map((service) => {
|
|
249
|
+
return normalizeServices(services).map((service) => {
|
|
215
250
|
const methods = service.methods.map((method) => {
|
|
216
251
|
const { requestName, responseName } = processServiceMethod(method, context);
|
|
217
252
|
const requestStreaming = method.streaming === "client" || method.streaming === "bidirectional";
|
|
218
253
|
const responseStreaming = method.streaming === "server" || method.streaming === "bidirectional";
|
|
219
254
|
const requestType = requestStreaming ? `stream ${requestName}` : requestName;
|
|
220
255
|
const responseType = responseStreaming ? `stream ${responseName}` : responseName;
|
|
221
|
-
return ` rpc ${method.name}(${requestType}) returns (${responseType});`;
|
|
256
|
+
return ` rpc ${toPascalCase({ value: method.name })}(${requestType}) returns (${responseType});`;
|
|
222
257
|
});
|
|
223
|
-
return `service ${service.name} {\n${methods.join("\n")}\n}`;
|
|
258
|
+
return `service ${toPascalCase({ value: service.name })} {\n${methods.join("\n")}\n}`;
|
|
224
259
|
});
|
|
225
260
|
};
|
|
226
261
|
|
|
227
262
|
//#endregion
|
|
228
263
|
//#region src/zod-to-protobuf.ts
|
|
229
264
|
const zodToProtobuf = (schema, options = {}) => {
|
|
230
|
-
const { packageName = "default", rootMessageName = "Message", typePrefix = "", services
|
|
265
|
+
const { packageName = "default", rootMessageName = "Message", typePrefix = "", services, skipRootMessage = false } = options;
|
|
231
266
|
const messages = /* @__PURE__ */ new Map();
|
|
232
267
|
const enums = /* @__PURE__ */ new Map();
|
|
233
268
|
if (schema && !skipRootMessage) {
|
|
@@ -247,7 +282,7 @@ const zodToProtobuf = (schema, options = {}) => {
|
|
|
247
282
|
enums,
|
|
248
283
|
typePrefix: typePrefix || null
|
|
249
284
|
};
|
|
250
|
-
|
|
285
|
+
const servicesString = services && (Array.isArray(services) ? services.length > 0 : Object.keys(services).length > 0) ? generateServices(services, context) : [];
|
|
251
286
|
return `
|
|
252
287
|
syntax = "proto3";
|
|
253
288
|
package ${packageName};
|
|
@@ -255,7 +290,7 @@ package ${packageName};
|
|
|
255
290
|
${[
|
|
256
291
|
Array.from(enums.values()).map((enumDef) => enumDef.join("\n")),
|
|
257
292
|
Array.from(messages.entries()).map(([name, fields]) => `message ${name} {\n${fields.map((field) => ` ${field}`).join("\n")}\n}`),
|
|
258
|
-
|
|
293
|
+
servicesString
|
|
259
294
|
].filter((strings) => !!strings.length).map((strings) => strings.join("\n\n")).join("\n\n")}
|
|
260
295
|
`.trim();
|
|
261
296
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["tupleFields: ProtobufField[]"],"sources":["../src/types.ts","../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import type { ZodTypeAny } from \"zod\";\n\nexport interface ZodToProtobufOptions {\n packageName?: string;\n rootMessageName?: string;\n typePrefix?: string;\n services?: ServiceDefinition[];\n skipRootMessage?: boolean;\n}\n\nexport interface ServiceMethod {\n name: string;\n request: ZodTypeAny;\n response: ZodTypeAny;\n streaming?: \"client\" | \"server\" | \"bidirectional\";\n}\n\nexport interface ServiceDefinition {\n name: string;\n methods: ServiceMethod[];\n}\n\nexport class UnsupportedTypeException extends Error {\n constructor(type: string) {\n super(`Unsupported type: ${type}`);\n this.name = \"UnsupportedTypeException\";\n }\n}\n\nexport interface ProtobufField {\n types: Array<string | null>;\n name: string;\n}\n","import { ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return value.isInt ? \"int32\" : \"double\";\n};\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\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\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 ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport { UnsupportedTypeException, type ProtobufField } from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\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}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value._def as { valueType: ZodTypeAny }).valueType\n : // @ts-expect-error\n (value._def as { element?: 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 });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): ProtobufField[] => {\n const keyType = traverseKey({\n key: `${key}Key`,\n value: value._def.keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n const valueType = traverseKey({\n key: `${key}Value`,\n value: value._def.valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map key`);\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map value`);\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\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\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}): ProtobufField[] => {\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 });\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 });\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 });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n let messageName = toPascalCase({ value: key });\n if (typePrefix) {\n messageName = `${typePrefix}${messageName}`;\n }\n const nestedMessageFields = traverseSchema({\n schema: value,\n messages,\n enums,\n typePrefix,\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 (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 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 });\n });\n\n const tupleMessageName = toPascalCase({ value: key });\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 throw new UnsupportedTypeException(value.constructor.name);\n }\n\n throw new UnsupportedTypeException(typeof value);\n};\n\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): string[] => {\n if (!(schema instanceof ZodObject)) {\n throw new UnsupportedTypeException(schema.constructor.name);\n }\n\n const fields = Object.entries(schema.shape).flatMap(([key, value]) => {\n return traverseKey({\n key,\n value,\n messages,\n enums,\n isOptional: false,\n isInArray: false,\n typePrefix,\n });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`,\n );\n};\n","import type { ZodTypeAny } from \"zod\";\nimport type { ServiceDefinition, ServiceMethod } from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\ninterface ServiceGenerationContext {\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}\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\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\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 requestFields = traverseSchema({\n schema: method.request,\n messages,\n enums,\n typePrefix,\n });\n messages.set(requestName, requestFields);\n }\n\n if (!messages.has(responseName)) {\n const responseFields = traverseSchema({\n schema: method.response,\n messages,\n enums,\n typePrefix,\n });\n messages.set(responseName, responseFields);\n }\n\n return { requestName, responseName };\n};\n\nexport const generateServices = (\n services: ServiceDefinition[],\n context: ServiceGenerationContext,\n): string[] => {\n return services.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 ${method.name}(${requestType}) returns (${responseType});`;\n });\n\n return `service ${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\";\n\nexport const zodToProtobuf = (\n schema?: ZodTypeAny,\n options: ZodToProtobufOptions = {},\n): string => {\n const {\n packageName = \"default\",\n rootMessageName = \"Message\",\n typePrefix = \"\",\n services = [],\n skipRootMessage = false,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n\n if (schema && !skipRootMessage) {\n const fields = traverseSchema({ schema, messages, enums, typePrefix });\n if (fields.length > 0) {\n const rootMessageKey = `${typePrefix}${rootMessageName}`;\n messages.set(rootMessageKey, fields);\n }\n }\n\n const context = {\n messages,\n enums,\n typePrefix: typePrefix || null,\n };\n\n if (services.length > 0) {\n generateServices(services, context);\n }\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 servicesString =\n services.length > 0 ? generateServices(services, context) : [];\n\n const content = [enumsString, messagesString, servicesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n const 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":["tupleFields: ProtobufField[]","methods: ServiceMethod[]"],"sources":["../src/types.ts","../src/utils.ts","../src/traversers.ts","../src/service-generator.ts","../src/zod-to-protobuf.ts"],"sourcesContent":["import type { ZodObject, ZodTypeAny } from \"zod\";\n\nexport type ServicesInput =\n | ServiceDefinition[]\n | Record<string, ZodObject<any>>;\n\nexport interface ZodToProtobufOptions {\n packageName?: string;\n rootMessageName?: string;\n typePrefix?: string;\n services?: ServicesInput;\n skipRootMessage?: boolean;\n}\n\nexport interface ServiceMethod {\n name: string;\n request: ZodTypeAny;\n response: ZodTypeAny;\n streaming?: \"client\" | \"server\" | \"bidirectional\";\n}\n\nexport interface ServiceDefinition {\n name: string;\n methods: ServiceMethod[];\n}\n\nexport class UnsupportedTypeException extends Error {\n constructor(type: string) {\n super(`Unsupported type: ${type}`);\n this.name = \"UnsupportedTypeException\";\n }\n}\n\nexport interface ProtobufField {\n types: Array<string | null>;\n name: string;\n}\n","import { ZodNumber } from \"zod\";\nimport type { ProtobufField } from \"./types\";\n\nexport const getNumberTypeName = ({ value }: { value: ZodNumber }): string => {\n return value.isInt ? \"int32\" : \"double\";\n};\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\nexport const protobufFieldToType = ({\n field,\n}: {\n field: ProtobufField;\n}): string => {\n return field.types.filter(Boolean).join(\" \");\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 ZodSet,\n ZodString,\n ZodTuple,\n ZodType,\n type ZodTypeAny,\n} from \"zod\";\nimport { UnsupportedTypeException, type ProtobufField } from \"./types\";\nimport { getNumberTypeName, toPascalCase, protobufFieldToType } from \"./utils\";\n\nexport const traverseArray = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\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}): ProtobufField[] => {\n const nestedValue =\n value instanceof ZodArray\n ? value.element\n : value instanceof ZodSet\n ? (value._def as { valueType: ZodTypeAny }).valueType\n : // @ts-expect-error\n (value._def as { element?: 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 });\n return elementFields.map((field) => ({\n ...field,\n types: [\"repeated\", ...field.types],\n name: field.name.replace(singularKey, key),\n }));\n};\n\nexport const traverseMap = ({\n key,\n value,\n messages,\n enums,\n typePrefix,\n}: {\n key: string;\n value: ZodMap<ZodTypeAny, ZodTypeAny>;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}): ProtobufField[] => {\n const mapDef = (value._def || value.def) as {\n keyType?: ZodTypeAny;\n valueType?: ZodTypeAny;\n };\n\n const keyType = traverseKey({\n key: inflection.singularize(key),\n value:\n mapDef.keyType ||\n (value as ZodTypeAny & { keyType?: ZodTypeAny }).keyType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n const valueType = traverseKey({\n key: inflection.singularize(key),\n value:\n mapDef.valueType ||\n (value as ZodTypeAny & { valueType?: ZodTypeAny }).valueType,\n messages,\n enums,\n isOptional: false,\n isInArray: true,\n typePrefix,\n });\n\n if (!keyType[0] || keyType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map key`);\n }\n\n if (!valueType[0] || valueType.length !== 1) {\n throw new UnsupportedTypeException(`${key} map value`);\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\nexport const traverseKey = ({\n key,\n value,\n messages,\n enums,\n isOptional,\n isInArray,\n typePrefix,\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}): 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 });\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 });\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 });\n }\n\n const optional = isOptional && !isInArray ? \"optional\" : null;\n\n if (value instanceof ZodObject) {\n let messageName = toPascalCase({ value: key });\n if (typePrefix) {\n messageName = `${typePrefix}${messageName}`;\n }\n const nestedMessageFields = traverseSchema({\n schema: value,\n messages,\n enums,\n typePrefix,\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 (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 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 });\n });\n\n const tupleMessageName = toPascalCase({ value: key });\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 throw new UnsupportedTypeException(value.constructor.name);\n }\n\n throw new UnsupportedTypeException(typeof value);\n};\n\nexport const traverseSchema = ({\n schema,\n messages,\n enums,\n typePrefix,\n}: {\n schema: ZodTypeAny;\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\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 throw new UnsupportedTypeException(\n schema?.constructor?.name || typeof schema\n );\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 });\n });\n\n return fields.map(\n (field, index) =>\n `${protobufFieldToType({ field })} ${field.name} = ${index + 1};`\n );\n};\n","import { z, type ZodFunction, ZodObject, type ZodTypeAny } from \"zod\";\nimport type { ServiceDefinition, ServiceMethod, ServicesInput } from \"./types\";\nimport { toPascalCase } from \"./utils\";\nimport { traverseSchema } from \"./traversers\";\n\ninterface ServiceGenerationContext {\n messages: Map<string, string[]>;\n enums: Map<string, string[]>;\n typePrefix: string | null;\n}\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 {\n type?: string;\n input?: ZodTypeAny;\n output?: ZodTypeAny;\n };\n\n if (methodDef.type === \"function\") {\n const inputDef = methodDef.input as ZodTypeAny & {\n def?: { items?: ZodTypeAny[] };\n };\n\n const args = inputDef?.def?.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 methods.push({\n name: methodName,\n request,\n response,\n });\n }\n }\n }\n\n return {\n name,\n methods,\n };\n};\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\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\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\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\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 });\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 });\n messages.set(responseName, responseFields);\n }\n\n return { requestName, responseName };\n};\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\";\n\nexport const zodToProtobuf = (\n schema?: ZodTypeAny,\n options: ZodToProtobufOptions = {}\n): string => {\n const {\n packageName = \"default\",\n rootMessageName = \"Message\",\n typePrefix = \"\",\n services,\n skipRootMessage = false,\n } = options;\n\n const messages = new Map<string, string[]>();\n const enums = new Map<string, string[]>();\n\n if (schema && !skipRootMessage) {\n const fields = traverseSchema({ schema, messages, enums, typePrefix });\n if (fields.length > 0) {\n const rootMessageKey = `${typePrefix}${rootMessageName}`;\n messages.set(rootMessageKey, fields);\n }\n }\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 = [enumsString, messagesString, servicesString]\n .filter((strings) => !!strings.length)\n .map((strings) => strings.join(\"\\n\\n\"))\n .join(\"\\n\\n\");\n\n const protoDefinition = `\nsyntax = \"proto3\";\npackage ${packageName};\n\n${content}\n`;\n\n return protoDefinition.trim();\n};\n"],"mappings":";;;;AA0BA,IAAa,2BAAb,cAA8C,MAAM;CAClD,YAAY,MAAc;AACxB,QAAM,qBAAqB,OAAO;AAClC,OAAK,OAAO;;;;;;AC1BhB,MAAa,qBAAqB,EAAE,YAA0C;AAC5E,QAAO,MAAM,QAAQ,UAAU;;AAGjC,MAAa,gBAAgB,EAAE,YAAuC;AACpE,QAAO,MACJ,MAAM,IAAI,CACV,KAAK,SAAS,KAAK,OAAO,EAAE,CAAC,aAAa,GAAG,KAAK,MAAM,EAAE,CAAC,CAC3D,KAAK,GAAG;;AAGb,MAAa,uBAAuB,EAClC,YAGY;AACZ,QAAO,MAAM,MAAM,OAAO,QAAQ,CAAC,KAAK,IAAI;;;;;ACE9C,MAAa,iBAAiB,EAC5B,KACA,OACA,UACA,OACA,iBAOqB;CACrB,MAAM,cACJ,iBAAiB,WACb,MAAM,UACN,iBAAiB,SACd,MAAM,KAAmC,YAEzC,MAAM,KAAkC;CAEjD,MAAM,cAAc,WAAW,YAAY,IAAI;AAU/C,QATsB,YAAY;EAChC,KAAK;EACL,OAAO;EACP;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC,CACmB,KAAK,WAAW;EACnC,GAAG;EACH,OAAO,CAAC,YAAY,GAAG,MAAM,MAAM;EACnC,MAAM,MAAM,KAAK,QAAQ,aAAa,IAAI;EAC3C,EAAE;;AAGL,MAAa,eAAe,EAC1B,KACA,OACA,UACA,OACA,iBAOqB;CACrB,MAAM,SAAU,MAAM,QAAQ,MAAM;CAKpC,MAAM,UAAU,YAAY;EAC1B,KAAK,WAAW,YAAY,IAAI;EAChC,OACE,OAAO,WACN,MAAgD;EACnD;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC;CACF,MAAM,YAAY,YAAY;EAC5B,KAAK,WAAW,YAAY,IAAI;EAChC,OACE,OAAO,aACN,MAAkD;EACrD;EACA;EACA,YAAY;EACZ,WAAW;EACX;EACD,CAAC;AAEF,KAAI,CAAC,QAAQ,MAAM,QAAQ,WAAW,EACpC,OAAM,IAAI,yBAAyB,GAAG,IAAI,UAAU;AAGtD,KAAI,CAAC,UAAU,MAAM,UAAU,WAAW,EACxC,OAAM,IAAI,yBAAyB,GAAG,IAAI,YAAY;AAIxD,QAAO,CACL;EACE,OAAO,CAHK,OAAO,oBAAoB,EAAE,OAAO,QAAQ,IAAI,CAAC,CAAC,IAAI,oBAAoB,EAAE,OAAO,UAAU,IAAI,CAAC,CAAC,GAG/F;EAChB,MAAM;EACP,CACF;;AAGH,MAAa,eAAe,EAC1B,KACA,OACA,UACA,OACA,YACA,WACA,iBASqB;AACrB,KAAI,CAAC,MACH,QAAO,EAAE;AAGX,KAAI,iBAAiB,eAAe,iBAAiB,YACnD,QAAO,YAAY;EACjB;EACA,OAAO,MAAM,QAAQ;EACrB;EACA;EACA,YAAY;EACZ;EACA;EACD,CAAC;AAGJ,KAAI,iBAAiB,YAAY,iBAAiB,OAChD,QAAO,cAAc;EACnB;EACO;EACP;EACA;EACA;EACD,CAAC;AAGJ,KAAI,iBAAiB,OACnB,QAAO,YAAY;EACjB;EACO;EACP;EACA;EACA;EACD,CAAC;CAGJ,MAAM,WAAW,cAAc,CAAC,YAAY,aAAa;AAEzD,KAAI,iBAAiB,WAAW;EAC9B,IAAI,cAAc,aAAa,EAAE,OAAO,KAAK,CAAC;AAC9C,MAAI,WACF,eAAc,GAAG,aAAa;EAEhC,MAAM,sBAAsB,eAAe;GACzC,QAAQ;GACR;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,aAAa,oBAAoB;AAC9C,SAAO,CACL;GACE,OAAO,CAAC,UAAU,YAAY;GAC9B,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiB,UACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,SAAS;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiB,UAEnB,QAAO,CACL;EACE,OAAO,CAAC,UAHK,kBAAkB,EAAE,OAAO,CAAC,CAGd;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiB,WACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,OAAO;EACzB,MAAM;EACP,CACF;AAGH,KAAI,iBAAiB,SAAS;EAC5B,MAAM,aAAa,MAAM,QACtB,KACE,QAAyB,UACxB,OAAO,OAAO,OAAO,CAAC,KAAK,MAAM,GACpC,CACA,KAAK,KAAK;EACb,IAAI,WAAW,aAAa,EAAE,OAAO,KAAK,CAAC;AAC3C,MAAI,WACF,YAAW,GAAG,aAAa;AAE7B,QAAM,IAAI,UAAU,CAAC,QAAQ,SAAS,MAAM,WAAW,KAAK,CAAC;AAC7D,SAAO,CACL;GACE,OAAO,CAAC,UAAU,SAAS;GAC3B,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiB,QACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,SAAS;EAC3B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiB,UACnB,QAAO,CACL;EACE,OAAO,CAAC,UAAU,QAAQ;EAC1B,MAAM;EACP,CACF;AAGH,KAAI,iBAAiB,UAAU;EAC7B,MAAMA,cACJ,MAAM,KAAK,MACX,SAAS,MAAkB,UAAkB;AAC7C,UAAO,YAAY;IACjB,KAAK,GAAG,IAAI,GAAG;IACf,OAAO;IACP;IACA;IACA,YAAY;IACZ;IACA;IACD,CAAC;IACF;EAEF,MAAM,mBAAmB,aAAa,EAAE,OAAO,KAAK,CAAC;AACrD,WAAS,IACP,kBACA,YAAY,KACT,OAAO,UACN,KAAK,MAAM,MAAM,KAAK,IAAI,CAAC,GAAG,MAAM,KAAK,KAAK,QAAQ,EAAE,GAC3D,CACF;AACD,SAAO,CACL;GACE,OAAO,CAAC,UAAU,iBAAiB;GACnC,MAAM;GACP,CACF;;AAGH,KAAI,iBAAiB,QACnB,OAAM,IAAI,yBAAyB,MAAM,YAAY,KAAK;AAG5D,OAAM,IAAI,yBAAyB,OAAO,MAAM;;AAGlD,MAAa,kBAAkB,EAC7B,QACA,UACA,OACA,iBAMc;AACd,KACE,CAAC,UACD,OAAO,WAAW,YAClB,EAAE,UAAU,WACX,OAAO,YAAY,SAAS,eAC1B,OAAO,KAA2B,SAAS,SAE9C,OAAM,IAAI,yBACR,QAAQ,aAAa,QAAQ,OAAO,OACrC;CAGH,MAAM,YAAY;AAalB,QAZe,OAAO,QAAQ,UAAU,MAAM,CAAC,SAAS,CAAC,KAAK,WAAW;AACvE,SAAO,YAAY;GACjB;GACA;GACA;GACA;GACA,YAAY;GACZ,WAAW;GACX;GACD,CAAC;GACF,CAEY,KACX,OAAO,UACN,GAAG,oBAAoB,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,KAAK,KAAK,QAAQ,EAAE,GAClE;;;;;ACpUH,MAAM,yBACJ,MACA,WACsB;CACtB,MAAM,QAAQ,OAAO;CACrB,MAAMC,UAA2B,EAAE;AAEnC,MAAK,MAAM,CAAC,YAAY,iBAAiB,OAAO,QAAQ,MAAM,EAAE;EAC9D,MAAM,YAAa,aAA4B;AAM/C,MAAI,UAAU,SAAS,YAAY;GAKjC,MAAM,OAJW,UAAU,OAIJ,KAAK,SAAS,EAAE;GACvC,MAAM,SAAS,UAAU;AAEzB,OAAI,KAAK,SAAS,KAAK,KAAK,MAAM,QAAQ;IACxC,MAAM,UAAU,KAAK;IACrB,MAAM,WAAW;AAEjB,YAAQ,KAAK;KACX,MAAM;KACN;KACA;KACD,CAAC;;;;AAKR,QAAO;EACL;EACA;EACD;;AAGH,MAAM,qBAAqB,aAAiD;AAC1E,KAAI,MAAM,QAAQ,SAAS,CACzB,QAAO;AAGT,QAAO,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,MAAM,YAC1C,sBAAsB,MAAM,OAAO,CACpC;;AAGH,MAAM,mBACJ,WAC0C;AAI1C,MAFG,OAAO,KAA2B,QAAQ,OAAO,YAAY,UAE7C,YAAY,OAAO,YAAY,SAAS,YACzD,QAAO;AAGT,QAAO,EAAE,OAAO,EACd,MAAM,QACP,CAAC;;AAGJ,MAAM,8BACJ,YACA,eACW;CACX,MAAM,cAAc,aAAa,EAAE,OAAO,GAAG,WAAW,UAAU,CAAC;AACnE,QAAO,aAAa,GAAG,aAAa,gBAAgB;;AAGtD,MAAM,+BACJ,YACA,eACW;CACX,MAAM,cAAc,aAAa,EAAE,OAAO,GAAG,WAAW,WAAW,CAAC;AACpE,QAAO,aAAa,GAAG,aAAa,gBAAgB;;AAGtD,MAAM,wBACJ,QACA,YACkD;CAClD,MAAM,EAAE,UAAU,OAAO,eAAe;CAExC,MAAM,cAAc,2BAA2B,OAAO,MAAM,WAAW;CACvE,MAAM,eAAe,4BAA4B,OAAO,MAAM,WAAW;AAEzE,KAAI,CAAC,SAAS,IAAI,YAAY,EAAE;EAE9B,MAAM,gBAAgB,eAAe;GACnC,QAFoB,gBAAgB,OAAO,QAAQ;GAGnD;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,aAAa,cAAc;;AAG1C,KAAI,CAAC,SAAS,IAAI,aAAa,EAAE;EAE/B,MAAM,iBAAiB,eAAe;GACpC,QAFqB,gBAAgB,OAAO,SAAS;GAGrD;GACA;GACA;GACD,CAAC;AACF,WAAS,IAAI,cAAc,eAAe;;AAG5C,QAAO;EAAE;EAAa;EAAc;;AAGtC,MAAa,oBACX,UACA,YACa;AAGb,QAF2B,kBAAkB,SAAS,CAE5B,KAAK,YAAY;EACzC,MAAM,UAAU,QAAQ,QAAQ,KAAK,WAAW;GAC9C,MAAM,EAAE,aAAa,iBAAiB,qBACpC,QACA,QACD;GAED,MAAM,mBACJ,OAAO,cAAc,YAAY,OAAO,cAAc;GACxD,MAAM,oBACJ,OAAO,cAAc,YAAY,OAAO,cAAc;GAExD,MAAM,cAAc,mBAChB,UAAU,gBACV;GACJ,MAAM,eAAe,oBACjB,UAAU,iBACV;AAEJ,UAAO,WAAW,aAAa,EAAE,OAAO,OAAO,MAAM,CAAC,CAAC,GAAG,YAAY,aAAa,aAAa;IAChG;AAEF,SAAO,WAAW,aAAa,EAAE,OAAO,QAAQ,MAAM,CAAC,CAAC,MAAM,QAAQ,KAAK,KAAK,CAAC;GACjF;;;;;ACvJJ,MAAa,iBACX,QACA,UAAgC,EAAE,KACvB;CACX,MAAM,EACJ,cAAc,WACd,kBAAkB,WAClB,aAAa,IACb,UACA,kBAAkB,UAChB;CAEJ,MAAM,2BAAW,IAAI,KAAuB;CAC5C,MAAM,wBAAQ,IAAI,KAAuB;AAEzC,KAAI,UAAU,CAAC,iBAAiB;EAC9B,MAAM,SAAS,eAAe;GAAE;GAAQ;GAAU;GAAO;GAAY,CAAC;AACtE,MAAI,OAAO,SAAS,GAAG;GACrB,MAAM,iBAAiB,GAAG,aAAa;AACvC,YAAS,IAAI,gBAAgB,OAAO;;;CAIxC,MAAM,UAAU;EACd;EACA;EACA,YAAY,cAAc;EAC3B;CAQD,MAAM,iBALJ,aACC,MAAM,QAAQ,SAAS,GACpB,SAAS,SAAS,IAClB,OAAO,KAAK,SAAS,CAAC,SAAS,KAEA,iBAAiB,UAAU,QAAQ,GAAG,EAAE;AAuB7E,QAPwB;;UAEhB,YAAY;;EAPJ;EATI,MAAM,KAAK,MAAM,QAAQ,CAAC,CAAC,KAAK,YAClD,QAAQ,KAAK,KAAK,CACnB;EAEsB,MAAM,KAAK,SAAS,SAAS,CAAC,CAAC,KACnD,CAAC,MAAM,YACN,WAAW,KAAK,MAAM,OAAO,KAAK,UAAU,OAAO,QAAQ,CAAC,KAAK,KAAK,CAAC,KAC1E;EAE6C;EAAe,CAC1D,QAAQ,YAAY,CAAC,CAAC,QAAQ,OAAO,CACrC,KAAK,YAAY,QAAQ,KAAK,OAAO,CAAC,CACtC,KAAK,OAAO,CAMP;EAGe,MAAM"}
|