Skip to main content
Glama
ProtoLoader.js6.48 kB
import * as _protoLoader from "@grpc/proto-loader"; import { getFileAsync, separator } from "../../Common/File.js"; import { obj2Hash } from "../../Common/Hash.js"; import { Logger } from "../../Common/Logger.js"; const proto_ext = ".proto"; // https://github.com/grpc/grpc-node/blob/master/packages/proto-loader/golden-generated/google/protobuf/FieldDescriptorProto.ts const protocolBufferMessageTypeMap = new Map([ ["TYPE_DOUBLE", "number"], ["TYPE_FLOAT", "number"], ["TYPE_INT64", "long"], ["TYPE_UINT64", "long"], ["TYPE_INT32", "number"], ["TYPE_FIXED64", "long"], ["TYPE_FIXED32", "number"], ["TYPE_BOOL", "boolean"], ["TYPE_STRING", "string"], ["TYPE_GROUP", undefined], ["TYPE_MESSAGE", undefined], ["TYPE_BYTES", "string"], ["TYPE_UINT32", "number"], ["TYPE_ENUM", undefined], ["TYPE_SFIXED32", "number"], ["TYPE_SFIXED64", "long"], ["TYPE_SINT32", "number"], ["TYPE_SINT64", "long"], ]); class ProtoLoader { async loadAsync(fileOrDir) { const files = await getFileAsync(fileOrDir, { extensions: [proto_ext] }); const protoTask = files.map((file) => this.loadProtoAsync(file)); return Promise.all(protoTask); } async loadProtoAsync(path) { let packageDefinition; try { packageDefinition = await _protoLoader.load(path, { keepCase: true, defaults: true, arrays: true, objects: true, longs: String, enums: String, }); // console.log(); // console.log(obj2String(packageDefinition)); // console.log(); } catch (e) { Logger.error(e); throw e; } // const proto = grpc.loadPackageDefinition(packageDefinition); // console.log(); // console.log(obj2String(proto)); // console.log(); const protoFileName = path.replace(proto_ext, "").split(separator).pop() + "." + path.split(".").pop(); const metadata = { protoFileName, protoPath: path, }; const { packageName, services, messages, enums } = this.getProtocolBufferServiceMessageEnum(packageDefinition); const serial = obj2Hash({ // metadata, packageName, services, messages, enums, }).toString(); return { protocolBuffer: { serial, metadata, packageName, services, messages, enums, }, packageDefinition, }; } getProtocolBufferServiceMessageEnum(packageDefinition) { const services = []; const messages = []; const enums = []; const keys = Object.keys(packageDefinition); keys.forEach((key) => { const proto = packageDefinition[key]; const result = this.getServiceMessageEnum(key, proto); if (result.service) { services.push(result.service); } else if (result.message) { messages.push(...result.message.messages); enums.push(...result.message.enums); } else if (result.en) { enums.push(result.en); } }); const packageName = services[0].packageName; return { packageName, services, messages, enums, }; } getServiceMessageEnum(key, proto) { if (proto.format === "Protocol Buffer 3 DescriptorProto") { const messageType = proto.type; return { message: this.getTypeMessage(messageType), }; } else if (proto.format === "Protocol Buffer 3 EnumDescriptorProto") { const enumType = proto.type; return { en: this.getEnum(enumType), }; } else { const methods = []; Object.keys(proto).forEach((mk) => { const method = proto[mk]; const requestType = method.requestType.type; const responseType = method.responseType.type; methods.push({ name: method.originalName ? method.originalName.charAt(0).toUpperCase() + method.originalName.slice(1) : "", path: method.path, requestMessageName: requestType.name, responseMessageName: responseType.name, }); }); return { service: { packageName: key.slice(0, key.lastIndexOf(".")), name: key.split(".").pop() ?? "", methods, }, }; } } getTypeMessage(messageType, parentMessageName) { const messages = []; const fields = messageType.field.map((field) => { return { name: field.name, type: this.getProtocolBufferMessageType(field), repeated: field.label.indexOf("LABEL_REPEATED") > -1 ? true : undefined, }; }); messages.push({ name: messageType.name, fields, parentMessageName, }); const enums = []; if (messageType.nestedType) { messageType.nestedType .flatMap((nst) => this.getTypeMessage(nst, messageType.name)) .forEach((tm) => { messages.push(...tm.messages); enums.push(...tm.enums); }); } if (messageType.enumType) { messageType.enumType.forEach((en) => { enums.push(this.getEnum(en, messageType.name)); }); } return { messages, enums }; } getEnum(enumType, parentMessageName) { return { name: enumType.name, values: enumType.value.map((e) => e.name), parentMessageName, }; } getProtocolBufferMessageType(field) { const type = protocolBufferMessageTypeMap.get(field.type); if (type) { return type; } else { return field.typeName; } } } export const protoLoader = new ProtoLoader();

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yuki4-dev/grpc-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server