// Copyright 2021-2024 Buf Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { ScalarType, } from "./descriptors.js"; import { parseTextFormatEnumValue, parseTextFormatScalarValue, } from "./wire/text-format.js"; import { nestedTypes } from "./reflect/nested-types.js"; import { unsafeIsSetExplicit } from "./reflect/unsafe.js"; import { protoCamelCase, safeObjectProperty } from "./reflect/names.js"; /** * Create a registry from the given inputs. * * An input can be: * - Any message, enum, service, or extension descriptor, which adds just the * descriptor for this type. * - A file descriptor, which adds all typed defined in this file. * - A registry, which adds all types from the registry. * * For duplicate descriptors (same type name), the one given last wins. */ export function createRegistry(...input) { return initBaseRegistry(input); } /** * Create a registry that allows adding and removing descriptors. */ export function createMutableRegistry(...input) { const reg = initBaseRegistry(input); return Object.assign(Object.assign({}, reg), { remove(desc) { var _a; if (desc.kind == "extension") { (_a = reg.extendees.get(desc.extendee.typeName)) === null || _a === void 0 ? void 0 : _a.delete(desc.number); } reg.types.delete(desc.typeName); } }); } export function createFileRegistry(...args) { const registry = createBaseRegistry(); if (!args.length) { return registry; } if ("$typeName" in args[0] && args[0].$typeName == "google.protobuf.FileDescriptorSet") { for (const file of args[0].file) { addFile(file, registry); } return registry; } if ("$typeName" in args[0]) { const input = args[0]; const resolve = args[1]; const seen = new Set(); // eslint-disable-next-line no-inner-declarations function recurseDeps(file) { const deps = []; for (const protoFileName of file.dependency) { if (registry.getFile(protoFileName) != undefined) { continue; } if (seen.has(protoFileName)) { continue; } const dep = resolve(protoFileName); if (!dep) { throw new Error(`Unable to resolve ${protoFileName}, imported by ${file.name}`); } if ("kind" in dep) { registry.addFile(dep, false, true); } else { seen.add(dep.name); deps.push(dep); } } return deps.concat(...deps.map(recurseDeps)); } for (const file of [input, ...recurseDeps(input)].reverse()) { addFile(file, registry); } } else { for (const fileReg of args) { for (const file of fileReg.files) { registry.addFile(file); } } } return registry; } /** * @private */ function createBaseRegistry() { const types = new Map(); const extendees = new Map(); const files = new Map(); return { kind: "registry", types, extendees, [Symbol.iterator]() { return types.values(); }, get files() { return files.values(); }, addFile(file, skipTypes, withDeps) { files.set(file.proto.name, file); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!skipTypes) { for (const type of nestedTypes(file)) { this.add(type); } } // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (withDeps) { for (const f of file.dependencies) { this.addFile(f, skipTypes, withDeps); } } }, add(desc) { if (desc.kind == "extension") { let numberToExt = extendees.get(desc.extendee.typeName); if (!numberToExt) { extendees.set(desc.extendee.typeName, (numberToExt = new Map())); } numberToExt.set(desc.number, desc); } types.set(desc.typeName, desc); }, get(typeName) { return types.get(typeName); }, getFile(fileName) { return files.get(fileName); }, getMessage(typeName) { const t = types.get(typeName); return (t === null || t === void 0 ? void 0 : t.kind) == "message" ? t : undefined; }, getEnum(typeName) { const t = types.get(typeName); return (t === null || t === void 0 ? void 0 : t.kind) == "enum" ? t : undefined; }, getExtension(typeName) { const t = types.get(typeName); return (t === null || t === void 0 ? void 0 : t.kind) == "extension" ? t : undefined; }, getExtensionFor(extendee, no) { var _a; return (_a = extendees.get(extendee.typeName)) === null || _a === void 0 ? void 0 : _a.get(no); }, getService(typeName) { const t = types.get(typeName); return (t === null || t === void 0 ? void 0 : t.kind) == "service" ? t : undefined; }, }; } /** * @private */ function initBaseRegistry(inputs) { const registry = createBaseRegistry(); for (const input of inputs) { switch (input.kind) { case "registry": for (const n of input) { registry.add(n); } break; case "file": registry.addFile(input); break; default: registry.add(input); break; } } return registry; } // bootstrap-inject google.protobuf.Edition.EDITION_PROTO2: const $name: Edition.$localName = $number; const EDITION_PROTO2 = 998; // bootstrap-inject google.protobuf.Edition.EDITION_PROTO3: const $name: Edition.$localName = $number; const EDITION_PROTO3 = 999; // bootstrap-inject google.protobuf.FieldDescriptorProto.Type.TYPE_STRING: const $name: FieldDescriptorProto_Type.$localName = $number; const TYPE_STRING = 9; // bootstrap-inject google.protobuf.FieldDescriptorProto.Type.TYPE_GROUP: const $name: FieldDescriptorProto_Type.$localName = $number; const TYPE_GROUP = 10; // bootstrap-inject google.protobuf.FieldDescriptorProto.Type.TYPE_MESSAGE: const $name: FieldDescriptorProto_Type.$localName = $number; const TYPE_MESSAGE = 11; // bootstrap-inject google.protobuf.FieldDescriptorProto.Type.TYPE_BYTES: const $name: FieldDescriptorProto_Type.$localName = $number; const TYPE_BYTES = 12; // bootstrap-inject google.protobuf.FieldDescriptorProto.Type.TYPE_ENUM: const $name: FieldDescriptorProto_Type.$localName = $number; const TYPE_ENUM = 14; // bootstrap-inject google.protobuf.FieldDescriptorProto.Label.LABEL_REPEATED: const $name: FieldDescriptorProto_Label.$localName = $number; const LABEL_REPEATED = 3; // bootstrap-inject google.protobuf.FieldDescriptorProto.Label.LABEL_REQUIRED: const $name: FieldDescriptorProto_Label.$localName = $number; const LABEL_REQUIRED = 2; // bootstrap-inject google.protobuf.FieldOptions.JSType.JS_STRING: const $name: FieldOptions_JSType.$localName = $number; const JS_STRING = 1; // bootstrap-inject google.protobuf.MethodOptions.IdempotencyLevel.IDEMPOTENCY_UNKNOWN: const $name: MethodOptions_IdempotencyLevel.$localName = $number; const IDEMPOTENCY_UNKNOWN = 0; // bootstrap-inject google.protobuf.FeatureSet.FieldPresence.EXPLICIT: const $name: FeatureSet_FieldPresence.$localName = $number; const EXPLICIT = 1; // bootstrap-inject google.protobuf.FeatureSet.FieldPresence.IMPLICIT: const $name: FeatureSet_FieldPresence.$localName = $number; const IMPLICIT = 2; // bootstrap-inject google.protobuf.FeatureSet.FieldPresence.LEGACY_REQUIRED: const $name: FeatureSet_FieldPresence.$localName = $number; const LEGACY_REQUIRED = 3; // bootstrap-inject google.protobuf.FeatureSet.RepeatedFieldEncoding.PACKED: const $name: FeatureSet_RepeatedFieldEncoding.$localName = $number; const PACKED = 1; // bootstrap-inject google.protobuf.FeatureSet.MessageEncoding.DELIMITED: const $name: FeatureSet_MessageEncoding.$localName = $number; const DELIMITED = 2; // bootstrap-inject google.protobuf.FeatureSet.EnumType.OPEN: const $name: FeatureSet_EnumType.$localName = $number; const OPEN = 1; // prettier-ignore // bootstrap-inject defaults: EDITION_PROTO2 to EDITION_2023: export const minimumEdition: SupportedEdition = $minimumEdition, maximumEdition: SupportedEdition = $maximumEdition; // generated from protoc v28.0 export const minimumEdition = 998, maximumEdition = 1000; const featureDefaults = { // EDITION_PROTO2 998: { fieldPresence: 1, // EXPLICIT, enumType: 2, // CLOSED, repeatedFieldEncoding: 2, // EXPANDED, utf8Validation: 3, // NONE, messageEncoding: 1, // LENGTH_PREFIXED, jsonFormat: 2, // LEGACY_BEST_EFFORT, }, // EDITION_PROTO3 999: { fieldPresence: 2, // IMPLICIT, enumType: 1, // OPEN, repeatedFieldEncoding: 1, // PACKED, utf8Validation: 2, // VERIFY, messageEncoding: 1, // LENGTH_PREFIXED, jsonFormat: 1, // ALLOW, }, // EDITION_2023 1000: { fieldPresence: 1, // EXPLICIT, enumType: 1, // OPEN, repeatedFieldEncoding: 1, // PACKED, utf8Validation: 2, // VERIFY, messageEncoding: 1, // LENGTH_PREFIXED, jsonFormat: 1, // ALLOW, }, }; /** * Create a descriptor for a file, add it to the registry. */ function addFile(proto, reg) { var _a, _b; const file = { kind: "file", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, edition: getFileEdition(proto), name: proto.name.replace(/\.proto$/, ""), dependencies: findFileDependencies(proto, reg), enums: [], messages: [], extensions: [], services: [], toString() { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- we asserted above return `file ${proto.name}`; }, }; const mapEntriesStore = new Map(); const mapEntries = { get(typeName) { return mapEntriesStore.get(typeName); }, add(desc) { var _a; assert(((_a = desc.proto.options) === null || _a === void 0 ? void 0 : _a.mapEntry) === true); mapEntriesStore.set(desc.typeName, desc); }, }; for (const enumProto of proto.enumType) { addEnum(enumProto, file, undefined, reg); } for (const messageProto of proto.messageType) { addMessage(messageProto, file, undefined, reg, mapEntries); } for (const serviceProto of proto.service) { addService(serviceProto, file, reg); } addExtensions(file, reg); for (const mapEntry of mapEntriesStore.values()) { // to create a map field, we need access to the map entry's fields addFields(mapEntry, reg, mapEntries); } for (const message of file.messages) { addFields(message, reg, mapEntries); addExtensions(message, reg); } reg.addFile(file, true); } /** * Create descriptors for extensions, and add them to the message / file, * and to our cart. * Recurses into nested types. */ function addExtensions(desc, reg) { switch (desc.kind) { case "file": for (const proto of desc.proto.extension) { const ext = newField(proto, desc, reg); desc.extensions.push(ext); reg.add(ext); } break; case "message": for (const proto of desc.proto.extension) { const ext = newField(proto, desc, reg); desc.nestedExtensions.push(ext); reg.add(ext); } for (const message of desc.nestedMessages) { addExtensions(message, reg); } break; } } /** * Create descriptors for fields and oneof groups, and add them to the message. * Recurses into nested types. */ function addFields(message, reg, mapEntries) { const allOneofs = message.proto.oneofDecl.map((proto) => newOneof(proto, message)); const oneofsSeen = new Set(); for (const proto of message.proto.field) { const oneof = findOneof(proto, allOneofs); const field = newField(proto, message, reg, oneof, mapEntries); message.fields.push(field); message.field[field.localName] = field; if (oneof === undefined) { message.members.push(field); } else { oneof.fields.push(field); if (!oneofsSeen.has(oneof)) { oneofsSeen.add(oneof); message.members.push(oneof); } } } for (const oneof of allOneofs.filter((o) => oneofsSeen.has(o))) { message.oneofs.push(oneof); } for (const child of message.nestedMessages) { addFields(child, reg, mapEntries); } } /** * Create a descriptor for an enumeration, and add it our cart and to the * parent type, if any. */ function addEnum(proto, file, parent, reg) { var _a, _b, _c; const sharedPrefix = findEnumSharedPrefix(proto.name, proto.value); const desc = { kind: "enum", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, file, parent, open: true, name: proto.name, typeName: makeTypeName(proto, parent, file), value: {}, values: [], sharedPrefix, toString() { return `enum ${this.typeName}`; }, }; desc.open = isEnumOpen(desc); reg.add(desc); proto.value.forEach((proto) => { var _a, _b; const name = proto.name; desc.values.push((desc.value[proto.number] = { kind: "enum_value", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, parent: desc, name, localName: safeObjectProperty(sharedPrefix == undefined ? name : name.substring(sharedPrefix.length)), number: proto.number, toString() { return `enum value ${desc.typeName}.${name}`; }, })); }); ((_c = parent === null || parent === void 0 ? void 0 : parent.nestedEnums) !== null && _c !== void 0 ? _c : file.enums).push(desc); } /** * Create a descriptor for a message, including nested types, and add it to our * cart. Note that this does not create descriptors fields. */ function addMessage(proto, file, parent, reg, mapEntries) { var _a, _b, _c, _d; const desc = { kind: "message", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, file, parent, name: proto.name, typeName: makeTypeName(proto, parent, file), fields: [], field: {}, oneofs: [], members: [], nestedEnums: [], nestedMessages: [], nestedExtensions: [], toString() { return `message ${this.typeName}`; }, }; if (((_c = proto.options) === null || _c === void 0 ? void 0 : _c.mapEntry) === true) { mapEntries.add(desc); } else { ((_d = parent === null || parent === void 0 ? void 0 : parent.nestedMessages) !== null && _d !== void 0 ? _d : file.messages).push(desc); reg.add(desc); } for (const enumProto of proto.enumType) { addEnum(enumProto, file, desc, reg); } for (const messageProto of proto.nestedType) { addMessage(messageProto, file, desc, reg, mapEntries); } } /** * Create a descriptor for a service, including methods, and add it to our * cart. */ function addService(proto, file, reg) { var _a, _b; const desc = { kind: "service", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, file, name: proto.name, typeName: makeTypeName(proto, undefined, file), methods: [], method: {}, toString() { return `service ${this.typeName}`; }, }; file.services.push(desc); reg.add(desc); for (const methodProto of proto.method) { const method = newMethod(methodProto, desc, reg); desc.methods.push(method); desc.method[method.localName] = method; } } /** * Create a descriptor for a method. */ function newMethod(proto, parent, reg) { var _a, _b, _c, _d; let methodKind; if (proto.clientStreaming && proto.serverStreaming) { methodKind = "bidi_streaming"; } else if (proto.clientStreaming) { methodKind = "client_streaming"; } else if (proto.serverStreaming) { methodKind = "server_streaming"; } else { methodKind = "unary"; } const input = reg.getMessage(trimLeadingDot(proto.inputType)); const output = reg.getMessage(trimLeadingDot(proto.outputType)); assert(input, `invalid MethodDescriptorProto: input_type ${proto.inputType} not found`); assert(output, `invalid MethodDescriptorProto: output_type ${proto.inputType} not found`); const name = proto.name; return { kind: "rpc", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, parent, name, localName: safeObjectProperty(name.length ? safeObjectProperty(name[0].toLowerCase() + name.substring(1)) : name), methodKind, input, output, idempotency: (_d = (_c = proto.options) === null || _c === void 0 ? void 0 : _c.idempotencyLevel) !== null && _d !== void 0 ? _d : IDEMPOTENCY_UNKNOWN, toString() { return `rpc ${parent.typeName}.${name}`; }, }; } /** * Create a descriptor for a oneof group. */ function newOneof(proto, parent) { return { kind: "oneof", proto, deprecated: false, parent, fields: [], name: proto.name, localName: safeObjectProperty(protoCamelCase(proto.name)), toString() { return `oneof ${parent.typeName}.${this.name}`; }, }; } function newField(proto, parentOrFile, reg, oneof, mapEntries) { var _a, _b, _c; const isExtension = mapEntries === undefined; const field = { kind: "field", proto, deprecated: (_b = (_a = proto.options) === null || _a === void 0 ? void 0 : _a.deprecated) !== null && _b !== void 0 ? _b : false, name: proto.name, number: proto.number, scalar: undefined, message: undefined, enum: undefined, presence: getFieldPresence(proto, oneof, isExtension, parentOrFile), listKind: undefined, mapKind: undefined, mapKey: undefined, delimitedEncoding: undefined, packed: undefined, longAsString: false, getDefaultValue: undefined, }; if (isExtension) { // extension field const file = parentOrFile.kind == "file" ? parentOrFile : parentOrFile.file; const parent = parentOrFile.kind == "file" ? undefined : parentOrFile; const typeName = makeTypeName(proto, parent, file); field.kind = "extension"; field.file = file; field.parent = parent; field.oneof = undefined; field.typeName = typeName; field.jsonName = `[${typeName}]`; // option json_name is not allowed on extension fields field.toString = () => `extension ${typeName}`; const extendee = reg.getMessage(trimLeadingDot(proto.extendee)); assert(extendee, `invalid FieldDescriptorProto: extendee ${proto.extendee} not found`); field.extendee = extendee; } else { // regular field const parent = parentOrFile; assert(parent.kind == "message"); field.parent = parent; field.oneof = oneof; field.localName = oneof ? protoCamelCase(proto.name) : safeObjectProperty(protoCamelCase(proto.name)); field.jsonName = proto.jsonName; field.toString = () => `field ${parent.typeName}.${proto.name}`; } const label = proto.label; const type = proto.type; const jstype = (_c = proto.options) === null || _c === void 0 ? void 0 : _c.jstype; if (label === LABEL_REPEATED) { // list or map field const mapEntry = type == TYPE_MESSAGE ? mapEntries === null || mapEntries === void 0 ? void 0 : mapEntries.get(trimLeadingDot(proto.typeName)) : undefined; if (mapEntry) { // map field field.fieldKind = "map"; const { key, value } = findMapEntryFields(mapEntry); field.mapKey = key.scalar; field.mapKind = value.fieldKind; field.message = value.message; field.delimitedEncoding = false; // map fields are always LENGTH_PREFIXED field.enum = value.enum; field.scalar = value.scalar; return field; } // list field field.fieldKind = "list"; switch (type) { case TYPE_MESSAGE: case TYPE_GROUP: field.listKind = "message"; field.message = reg.getMessage(trimLeadingDot(proto.typeName)); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions assert(field.message); field.delimitedEncoding = isDelimitedEncoding(proto, parentOrFile); break; case TYPE_ENUM: field.listKind = "enum"; field.enum = reg.getEnum(trimLeadingDot(proto.typeName)); // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions assert(field.enum); break; default: field.listKind = "scalar"; field.scalar = type; field.longAsString = jstype == JS_STRING; break; } field.packed = isPackedField(proto, parentOrFile); return field; } // singular switch (type) { case TYPE_MESSAGE: case TYPE_GROUP: field.fieldKind = "message"; field.message = reg.getMessage(trimLeadingDot(proto.typeName)); assert( // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions field.message, `invalid FieldDescriptorProto: type_name ${proto.typeName} not found`); field.delimitedEncoding = isDelimitedEncoding(proto, parentOrFile); field.getDefaultValue = () => undefined; break; case TYPE_ENUM: { const enumeration = reg.getEnum(trimLeadingDot(proto.typeName)); assert(enumeration !== undefined, `invalid FieldDescriptorProto: type_name ${proto.typeName} not found`); field.fieldKind = "enum"; field.enum = reg.getEnum(trimLeadingDot(proto.typeName)); field.getDefaultValue = () => { return unsafeIsSetExplicit(proto, "defaultValue") ? parseTextFormatEnumValue(enumeration, proto.defaultValue) : undefined; }; break; } default: { field.fieldKind = "scalar"; field.scalar = type; field.longAsString = jstype == JS_STRING; field.getDefaultValue = () => { return unsafeIsSetExplicit(proto, "defaultValue") ? parseTextFormatScalarValue(type, proto.defaultValue) : undefined; }; break; } } return field; } /** * Parse the "syntax" and "edition" fields, returning one of the supported * editions. */ function getFileEdition(proto) { switch (proto.syntax) { case "": case "proto2": return EDITION_PROTO2; case "proto3": return EDITION_PROTO3; case "editions": if (proto.edition in featureDefaults) { return proto.edition; } throw new Error(`${proto.name}: unsupported edition`); default: throw new Error(`${proto.name}: unsupported syntax "${proto.syntax}"`); } } /** * Resolve dependencies of FileDescriptorProto to DescFile. */ function findFileDependencies(proto, reg) { return proto.dependency.map((wantName) => { const dep = reg.getFile(wantName); if (!dep) { throw new Error(`Cannot find ${wantName}, imported by ${proto.name}`); } return dep; }); } /** * Finds a prefix shared by enum values, for example `my_enum_` for * `enum MyEnum {MY_ENUM_A=0; MY_ENUM_B=1;}`. */ function findEnumSharedPrefix(enumName, values) { const prefix = camelToSnakeCase(enumName) + "_"; for (const value of values) { if (!value.name.toLowerCase().startsWith(prefix)) { return undefined; } const shortName = value.name.substring(prefix.length); if (shortName.length == 0) { return undefined; } if (/^\d/.test(shortName)) { // identifiers must not start with numbers return undefined; } } return prefix; } /** * Converts lowerCamelCase or UpperCamelCase into lower_snake_case. * This is used to find shared prefixes in an enum. */ function camelToSnakeCase(camel) { return (camel.substring(0, 1) + camel.substring(1).replace(/[A-Z]/g, (c) => "_" + c)).toLowerCase(); } /** * Create a fully qualified name for a protobuf type or extension field. * * The fully qualified name for messages, enumerations, and services is * constructed by concatenating the package name (if present), parent * message names (for nested types), and the type name. We omit the leading * dot added by protobuf compilers. Examples: * - mypackage.MyMessage * - mypackage.MyMessage.NestedMessage * * The fully qualified name for extension fields is constructed by * concatenating the package name (if present), parent message names (for * extensions declared within a message), and the field name. Examples: * - mypackage.extfield * - mypackage.MyMessage.extfield */ function makeTypeName(proto, parent, file) { let typeName; if (parent) { typeName = `${parent.typeName}.${proto.name}`; } else if (file.proto.package.length > 0) { typeName = `${file.proto.package}.${proto.name}`; } else { typeName = `${proto.name}`; } return typeName; } /** * Remove the leading dot from a fully qualified type name. */ function trimLeadingDot(typeName) { return typeName.startsWith(".") ? typeName.substring(1) : typeName; } /** * Did the user put the field in a oneof group? * Synthetic oneofs for proto3 optionals are ignored. */ function findOneof(proto, allOneofs) { if (!unsafeIsSetExplicit(proto, "oneofIndex")) { return undefined; } if (proto.proto3Optional) { return undefined; } const oneof = allOneofs[proto.oneofIndex]; assert( // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions oneof, `invalid FieldDescriptorProto: oneof #${proto.oneofIndex} for field #${proto.number} not found`); return oneof; } /** * Presence of the field. * See https://protobuf.dev/programming-guides/field_presence/ */ function getFieldPresence(proto, oneof, isExtension, parent) { if (proto.label == LABEL_REQUIRED) { // proto2 required is LEGACY_REQUIRED return LEGACY_REQUIRED; } if (proto.label == LABEL_REPEATED) { // repeated fields (including maps) do not track presence return IMPLICIT; } if (!!oneof || proto.proto3Optional) { // oneof is always explicit return EXPLICIT; } if (proto.type == TYPE_MESSAGE) { // singular message field cannot be implicit return EXPLICIT; } if (isExtension) { // extensions always track presence return EXPLICIT; } return resolveFeature("fieldPresence", { proto, parent }); } /** * Pack this repeated field? */ function isPackedField(proto, parent) { if (proto.label != LABEL_REPEATED) { return false; } // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check switch (proto.type) { case TYPE_STRING: case TYPE_BYTES: case TYPE_GROUP: case TYPE_MESSAGE: // length-delimited types cannot be packed return false; } const o = proto.options; if (o && unsafeIsSetExplicit(o, "packed")) { // prefer the field option over edition features return o.packed; } return (PACKED == resolveFeature("repeatedFieldEncoding", { proto, parent, })); } /** * Find the key and value fields of a synthetic map entry message. */ function findMapEntryFields(mapEntry) { const key = mapEntry.fields.find((f) => f.number === 1); const value = mapEntry.fields.find((f) => f.number === 2); assert(key && key.fieldKind == "scalar" && key.scalar != ScalarType.BYTES && key.scalar != ScalarType.FLOAT && key.scalar != ScalarType.DOUBLE && value && value.fieldKind != "list" && value.fieldKind != "map"); return { key, value }; } /** * Enumerations can be open or closed. * See https://protobuf.dev/programming-guides/enum/ */ function isEnumOpen(desc) { var _a; return (OPEN == resolveFeature("enumType", { proto: desc.proto, parent: (_a = desc.parent) !== null && _a !== void 0 ? _a : desc.file, })); } /** * Encode the message delimited (a.k.a. proto2 group encoding), or * length-prefixed? */ function isDelimitedEncoding(proto, parent) { if (proto.type == TYPE_GROUP) { return true; } return (DELIMITED == resolveFeature("messageEncoding", { proto, parent, })); } function resolveFeature(name, ref) { var _a, _b; const featureSet = (_a = ref.proto.options) === null || _a === void 0 ? void 0 : _a.features; if (featureSet) { const val = featureSet[name]; if (val != 0) { return val; } } if ("kind" in ref) { if (ref.kind == "message") { return resolveFeature(name, (_b = ref.parent) !== null && _b !== void 0 ? _b : ref.file); } const editionDefaults = featureDefaults[ref.edition]; if (!editionDefaults) { throw new Error(`feature default for edition ${ref.edition} not found`); } return editionDefaults[name]; } return resolveFeature(name, ref.parent); } /** * Assert that condition is truthy or throw error (with message) */ function assert(condition, msg) { // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions -- we want the implicit conversion to boolean if (!condition) { throw new Error(msg); } }