@alepha/protobuf 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import * as alepha from "alepha";
2
2
  import { Alepha, SchemaCodec, Static, TObject, TSchema } from "alepha";
3
3
  import protobufjs, { Type } from "protobufjs";
4
- import "alepha/datetime";
5
-
6
4
  //#region src/providers/ProtobufProvider.d.ts
7
5
  declare class ProtobufProvider {
8
6
  protected readonly alepha: Alepha;
9
7
  protected readonly schemas: Map<string | TObject, Type>;
10
8
  protected readonly protobuf: typeof protobufjs;
9
+ /**
10
+ * @deprecated Use local enumDefinitions passed through method parameters.
11
+ */
11
12
  protected readonly enumDefinitions: Map<string, string[]>;
12
13
  /**
13
14
  * Encode an object to a Uint8Array.
@@ -28,7 +29,7 @@ declare class ProtobufProvider {
28
29
  /**
29
30
  * Parse an object schema with dependencies (sub-messages).
30
31
  */
31
- protected parseObjectWithDependencies(obj: TSchema, parentName: string): {
32
+ protected parseObjectWithDependencies(obj: TSchema, parentName: string, enumDefinitions?: Map<string, string[]>): {
32
33
  message: string;
33
34
  subMessages: string[];
34
35
  };
@@ -49,7 +50,7 @@ declare class ProtobufProvider {
49
50
  * Register an enum and return its type name.
50
51
  * Generates a PascalCase name from the field name.
51
52
  */
52
- protected registerEnum(fieldName: string, values: string[]): string;
53
+ protected registerEnum(fieldName: string, values: string[], enumDefinitions?: Map<string, string[]>): string;
53
54
  /**
54
55
  * Generate a protobuf enum definition.
55
56
  */
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import { $inject, $module, Alepha, AlephaError, SchemaCodec, t } from "alepha";
2
2
  import protobufjs from "protobufjs";
3
3
  import "alepha/datetime";
4
-
5
4
  //#region src/providers/ProtobufProvider.ts
6
5
  var ProtobufProvider = class {
7
6
  alepha = $inject(Alepha);
8
7
  schemas = /* @__PURE__ */ new Map();
9
8
  protobuf = protobufjs;
9
+ /**
10
+ * @deprecated Use local enumDefinitions passed through method parameters.
11
+ */
10
12
  enumDefinitions = /* @__PURE__ */ new Map();
11
13
  /**
12
14
  * Encode an object to a Uint8Array.
@@ -35,14 +37,14 @@ var ProtobufProvider = class {
35
37
  */
36
38
  createProtobufSchema(schema, options = {}) {
37
39
  const { rootName = "root", mainMessageName = "Target" } = options;
38
- this.enumDefinitions.clear();
40
+ const enumDefinitions = /* @__PURE__ */ new Map();
39
41
  const context = {
40
42
  proto: `package ${rootName};\nsyntax = "proto3";\n\n`,
41
43
  fieldIndex: 1
42
44
  };
43
45
  if (t.schema.isObject(schema)) {
44
- const { message, subMessages } = this.parseObjectWithDependencies(schema, mainMessageName);
45
- for (const [enumName, values] of this.enumDefinitions) context.proto += this.generateEnumDefinition(enumName, values);
46
+ const { message, subMessages } = this.parseObjectWithDependencies(schema, mainMessageName, enumDefinitions);
47
+ for (const [enumName, values] of enumDefinitions) context.proto += this.generateEnumDefinition(enumName, values);
46
48
  context.proto += subMessages.join("");
47
49
  context.proto += message;
48
50
  }
@@ -51,7 +53,7 @@ var ProtobufProvider = class {
51
53
  /**
52
54
  * Parse an object schema with dependencies (sub-messages).
53
55
  */
54
- parseObjectWithDependencies(obj, parentName) {
56
+ parseObjectWithDependencies(obj, parentName, enumDefinitions = this.enumDefinitions) {
55
57
  if (!t.schema.isObject(obj)) return {
56
58
  message: "",
57
59
  subMessages: []
@@ -63,13 +65,13 @@ var ProtobufProvider = class {
63
65
  if (t.schema.isArray(value)) {
64
66
  if (this.isEnum(value.items)) {
65
67
  const enumValues = this.getEnumValues(value.items);
66
- const enumName = this.registerEnum(key, enumValues);
68
+ const enumName = this.registerEnum(key, enumValues, enumDefinitions);
67
69
  fields.push(` repeated ${enumName} ${key} = ${fieldIndex++};`);
68
70
  continue;
69
71
  }
70
72
  if (t.schema.isObject(value.items)) {
71
73
  const subMessageName = "title" in value.items && typeof value.items.title === "string" ? value.items.title : `${parentName}_${key}`;
72
- const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value.items, subMessageName);
74
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value.items, subMessageName, enumDefinitions);
73
75
  subMessages.push(...nestedSubMessages);
74
76
  subMessages.push(subMessage);
75
77
  fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -81,7 +83,7 @@ var ProtobufProvider = class {
81
83
  }
82
84
  if (t.schema.isObject(value)) {
83
85
  const subMessageName = "title" in value && typeof value.title === "string" ? value.title : `${parentName}_${key}`;
84
- const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value, subMessageName);
86
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value, subMessageName, enumDefinitions);
85
87
  subMessages.push(...nestedSubMessages);
86
88
  subMessages.push(subMessage);
87
89
  fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -92,13 +94,13 @@ var ProtobufProvider = class {
92
94
  if (nonNullType) {
93
95
  if (this.isEnum(nonNullType)) {
94
96
  const enumValues = this.getEnumValues(nonNullType);
95
- const enumName = this.registerEnum(key, enumValues);
97
+ const enumName = this.registerEnum(key, enumValues, enumDefinitions);
96
98
  fields.push(` ${enumName} ${key} = ${fieldIndex++};`);
97
99
  continue;
98
100
  }
99
101
  if (t.schema.isObject(nonNullType)) {
100
102
  const subMessageName = "title" in nonNullType && typeof nonNullType.title === "string" ? nonNullType.title : `${parentName}_${key}`;
101
- const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(nonNullType, subMessageName);
103
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(nonNullType, subMessageName, enumDefinitions);
102
104
  subMessages.push(...nestedSubMessages);
103
105
  subMessages.push(subMessage);
104
106
  fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -124,7 +126,7 @@ var ProtobufProvider = class {
124
126
  }
125
127
  if (this.isEnum(value)) {
126
128
  const enumValues = this.getEnumValues(value);
127
- const enumName = this.registerEnum(key, enumValues);
129
+ const enumName = this.registerEnum(key, enumValues, enumDefinitions);
128
130
  fields.push(` ${enumName} ${key} = ${fieldIndex++};`);
129
131
  continue;
130
132
  }
@@ -172,12 +174,12 @@ var ProtobufProvider = class {
172
174
  * Register an enum and return its type name.
173
175
  * Generates a PascalCase name from the field name.
174
176
  */
175
- registerEnum(fieldName, values) {
177
+ registerEnum(fieldName, values, enumDefinitions = this.enumDefinitions) {
176
178
  const enumName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
177
179
  const valueKey = values.join(",");
178
- const existingEnum = Array.from(this.enumDefinitions.entries()).find(([_, enumValues]) => enumValues.join(",") === valueKey);
180
+ const existingEnum = Array.from(enumDefinitions.entries()).find(([_, enumValues]) => enumValues.join(",") === valueKey);
179
181
  if (existingEnum) return existingEnum[0];
180
- this.enumDefinitions.set(enumName, values);
182
+ enumDefinitions.set(enumName, values);
181
183
  return enumName;
182
184
  }
183
185
  /**
@@ -187,7 +189,6 @@ var ProtobufProvider = class {
187
189
  return `enum ${enumName} {\n${values.map((value, index) => ` ${value} = ${index};`).join("\n")}\n}\n`;
188
190
  }
189
191
  };
190
-
191
192
  //#endregion
192
193
  //#region src/providers/ProtobufSchemaCodec.ts
193
194
  /**
@@ -262,7 +263,6 @@ var ProtobufSchemaCodec = class extends SchemaCodec {
262
263
  if (t.schema.isObject(schema)) return {};
263
264
  }
264
265
  };
265
-
266
266
  //#endregion
267
267
  //#region src/index.ts
268
268
  /**
@@ -286,7 +286,7 @@ const AlephaProtobuf = $module({
286
286
  });
287
287
  }
288
288
  });
289
-
290
289
  //#endregion
291
290
  export { AlephaProtobuf, ProtobufProvider, ProtobufSchemaCodec };
291
+
292
292
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/providers/ProtobufProvider.ts","../src/providers/ProtobufSchemaCodec.ts","../src/index.ts"],"sourcesContent":["import { $inject, Alepha, type TObject, type TSchema, t } from \"alepha\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly schemas: Map<string | TObject, Type> = new Map();\n protected readonly protobuf: typeof protobufjs = protobufjs;\n protected readonly enumDefinitions: Map<string, string[]> = new Map();\n\n /**\n * Encode an object to a Uint8Array.\n */\n public encode(schema: ProtobufSchema, message: any): Uint8Array {\n return this.parse(schema).encode(message).finish();\n }\n\n /**\n * Decode a Uint8Array to an object.\n */\n public decode<T = any>(schema: ProtobufSchema, data: Uint8Array): T {\n return this.parse(schema).decode(data) as T;\n }\n\n /**\n * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n */\n public parse(schema: ProtobufSchema, typeName = \"root.Target\"): Type {\n const exists = this.schemas.get(schema);\n if (exists) {\n return exists;\n }\n\n const result = this.protobuf.parse(schema);\n const type = result.root.lookupType(typeName);\n this.schemas.set(schema, type);\n return type;\n }\n\n /**\n * Convert a TypeBox schema to a Protobuf schema as a string.\n */\n public createProtobufSchema(\n schema: TSchema,\n options: CreateProtobufSchemaOptions = {},\n ): string {\n const { rootName = \"root\", mainMessageName = \"Target\" } = options;\n // Clear enum definitions for this schema generation\n this.enumDefinitions.clear();\n\n const context = {\n proto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n fieldIndex: 1,\n };\n\n if (t.schema.isObject(schema)) {\n const { message, subMessages } = this.parseObjectWithDependencies(\n schema,\n mainMessageName,\n );\n\n // Add all enum definitions first\n for (const [enumName, values] of this.enumDefinitions) {\n context.proto += this.generateEnumDefinition(enumName, values);\n }\n\n // Add all sub-messages\n context.proto += subMessages.join(\"\");\n // Then add the main message\n context.proto += message;\n }\n\n return context.proto;\n }\n\n /**\n * Parse an object schema with dependencies (sub-messages).\n */\n protected parseObjectWithDependencies(\n obj: TSchema,\n parentName: string,\n ): { message: string; subMessages: string[] } {\n if (!t.schema.isObject(obj)) {\n return { message: \"\", subMessages: [] };\n }\n\n const fields: string[] = [];\n const subMessages: string[] = [];\n let fieldIndex = 1;\n\n for (const [key, value] of Object.entries(obj.properties)) {\n // Handle arrays\n if (t.schema.isArray(value)) {\n // Check if array items are enums\n if (this.isEnum(value.items)) {\n const enumValues = this.getEnumValues(value.items);\n const enumName = this.registerEnum(key, enumValues);\n fields.push(` repeated ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n if (t.schema.isObject(value.items)) {\n const subMessageName =\n \"title\" in value.items && typeof value.items.title === \"string\"\n ? value.items.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(value.items, subMessageName);\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n const itemType = this.convertType(value.items);\n fields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle nested objects\n if (t.schema.isObject(value)) {\n const subMessageName =\n \"title\" in value && typeof value.title === \"string\"\n ? value.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(value, subMessageName);\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle union types (nullable fields)\n if (t.schema.isUnion(value)) {\n const nonNullType = value.anyOf.find(\n (type: TSchema) => !t.schema.isNull(type),\n );\n if (nonNullType) {\n // Check if it's an enum\n if (this.isEnum(nonNullType)) {\n const enumValues = this.getEnumValues(nonNullType);\n const enumName = this.registerEnum(key, enumValues);\n fields.push(` ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n if (t.schema.isObject(nonNullType)) {\n const subMessageName =\n \"title\" in nonNullType && typeof nonNullType.title === \"string\"\n ? nonNullType.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(nonNullType, subMessageName);\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n const fieldType = this.convertType(nonNullType);\n fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n continue;\n }\n }\n\n // Handle records (maps)\n if (t.schema.isRecord(value)) {\n // TypeBox records use additionalProperties or patternProperties for the value type\n let valueSchema: TSchema | undefined;\n if (\n \"additionalProperties\" in value &&\n value.additionalProperties &&\n typeof value.additionalProperties === \"object\"\n ) {\n valueSchema = value.additionalProperties;\n } else if (\n value.patternProperties &&\n typeof value.patternProperties === \"object\"\n ) {\n // Get the first pattern property (usually \"^(.*)$\" or similar)\n const patterns = Object.values(value.patternProperties);\n if (patterns.length > 0 && typeof patterns[0] === \"object\") {\n valueSchema = patterns[0] as TSchema;\n }\n }\n\n if (valueSchema) {\n const valueType = this.convertType(valueSchema);\n fields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);\n continue;\n }\n }\n\n // Handle enum fields\n if (this.isEnum(value)) {\n const enumValues = this.getEnumValues(value);\n const enumName = this.registerEnum(key, enumValues);\n fields.push(` ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle regular fields\n const fieldType = this.convertType(value);\n fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n }\n\n const message = `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n return { message, subMessages };\n }\n\n /**\n * Convert a primitive TypeBox schema type to a Protobuf spec type.\n */\n protected convertType(schema: TSchema): string {\n if (t.schema.isBoolean(schema)) return \"bool\";\n if (t.schema.isNumber(schema) && schema.format === \"int64\") return \"int64\";\n if (t.schema.isNumber(schema)) return \"double\";\n if (t.schema.isInteger(schema)) return \"int32\";\n if (t.schema.isBigInt(schema)) return \"int64\";\n if (t.schema.isString(schema)) return \"string\";\n\n // Handle union types (nullable)\n if (t.schema.isUnion(schema)) {\n // Find the non-null type in the union\n const nonNullType = schema.anyOf.find(\n (type: TSchema) => !t.schema.isNull(type),\n );\n if (nonNullType) {\n return this.convertType(nonNullType);\n }\n }\n\n // Handle optional types\n if (t.schema.isOptional(schema)) {\n return this.convertType(schema);\n }\n\n // Handle unsafe types (like enums)\n if (t.schema.isUnsafe(schema)) {\n // if it's an enum or other unsafe types, default to string\n return \"string\";\n }\n\n throw new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n }\n\n /**\n * Check if a schema is an enum type.\n * TypeBox enums have an \"enum\" property with an array of values.\n */\n protected isEnum(schema: TSchema): boolean {\n return \"enum\" in schema && Array.isArray(schema.enum);\n }\n\n /**\n * Extract enum values from a TypeBox enum schema.\n */\n protected getEnumValues(schema: TSchema): string[] {\n if (\"enum\" in schema && Array.isArray(schema.enum)) {\n return schema.enum.map(String);\n }\n return [];\n }\n\n /**\n * Register an enum and return its type name.\n * Generates a PascalCase name from the field name.\n */\n protected registerEnum(fieldName: string, values: string[]): string {\n // Capitalize first letter of field name for enum type name\n const enumName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);\n\n // Check if we already have this exact enum registered\n const valueKey = values.join(\",\");\n const existingEnum = Array.from(this.enumDefinitions.entries()).find(\n ([_, enumValues]) => enumValues.join(\",\") === valueKey,\n );\n\n if (existingEnum) {\n // Reuse existing enum with same values\n return existingEnum[0];\n }\n\n // Register new enum\n this.enumDefinitions.set(enumName, values);\n return enumName;\n }\n\n /**\n * Generate a protobuf enum definition.\n */\n protected generateEnumDefinition(enumName: string, values: string[]): string {\n const enumValues = values\n .map((value, index) => ` ${value} = ${index};`)\n .join(\"\\n\");\n return `enum ${enumName} {\\n${enumValues}\\n}\\n`;\n }\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n rootName?: string;\n mainMessageName?: string;\n}\n","import {\n $inject,\n AlephaError,\n SchemaCodec,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport \"alepha/datetime\";\nimport { ProtobufProvider } from \"./ProtobufProvider.ts\";\n\n/**\n * ProtobufSchemaCodec handles encoding/decoding for Protobuf format.\n *\n * Key differences from JSON codec:\n * - BigInt values are kept as BigInt (not converted to string)\n * - Date values are converted to ISO strings for protobuf compatibility\n * - Binary data (Uint8Array) is kept as-is\n * - Proto3 default values are applied when decoding (to handle omitted fields)\n */\nexport class ProtobufSchemaCodec extends SchemaCodec {\n protected protobufProvider = $inject(ProtobufProvider);\n protected decoder = new TextDecoder();\n\n public encodeToString<T extends TSchema>(\n schema: T,\n value: Static<T>,\n ): string {\n const binary = this.encodeToBinary(schema, value);\n // convert binary to base64 string for text representation\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(binary).toString(\"base64\");\n } else {\n return btoa(String.fromCharCode(...binary));\n }\n }\n\n public encodeToBinary<T extends TSchema>(\n schema: T,\n value: Static<T>,\n ): Uint8Array {\n const proto = this.protobufProvider.createProtobufSchema(schema);\n return this.protobufProvider.encode(proto, value);\n }\n\n public decode<T>(schema: TSchema, value: unknown): T {\n // First decode from protobuf binary to object\n const proto = this.protobufProvider.createProtobufSchema(schema);\n\n if (value instanceof Uint8Array) {\n return this.applyProto3Defaults(\n schema,\n this.protobufProvider.decode(proto, value),\n );\n }\n\n if (typeof value === \"string\") {\n return this.applyProto3Defaults(\n schema,\n this.protobufProvider.decode(\n proto,\n typeof Buffer !== \"undefined\"\n ? Uint8Array.from(Buffer.from(value, \"base64\"))\n : Uint8Array.from(\n atob(value)\n .split(\"\")\n .map((c) => c.charCodeAt(0)),\n ),\n ),\n );\n }\n\n throw new AlephaError(\n `Unsupported value type for Protobuf decoding: ${typeof value}`,\n );\n }\n\n /**\n * Apply proto3 default values for fields that were omitted during encoding.\n * Proto3 omits fields with default values, so we need to restore them.\n * Also converts enum integers back to their string values.\n */\n protected applyProto3Defaults(schema: TSchema, value: any): any {\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n if (t.schema.isObject(schema)) {\n const result: any = { ...value };\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n if (!(key in result) || result[key] === undefined) {\n // Apply proto3 default values based on type\n result[key] = this.getProto3Default(propSchema);\n } else {\n // Convert enum integers to strings\n if (this.isEnum(propSchema)) {\n result[key] = this.convertEnumValue(propSchema, result[key]);\n } else if (typeof result[key] === \"object\" && result[key] !== null) {\n // Recursively apply defaults to nested objects\n result[key] = this.applyProto3Defaults(propSchema, result[key]);\n }\n }\n }\n\n return result;\n }\n\n if (t.schema.isArray(schema) && Array.isArray(value)) {\n return value.map((item) => this.applyProto3Defaults(schema.items, item));\n }\n\n return value;\n }\n\n /**\n * Check if a schema is an enum type.\n */\n protected isEnum(schema: TSchema): boolean {\n return \"enum\" in schema && Array.isArray(schema.enum);\n }\n\n /**\n * Convert an enum value from protobuf integer to TypeBox string.\n */\n protected convertEnumValue(schema: TSchema, value: any): any {\n if (\n typeof value === \"number\" &&\n \"enum\" in schema &&\n Array.isArray(schema.enum)\n ) {\n // Protobuf encodes enums as integers, convert back to string\n return schema.enum[value];\n }\n return value;\n }\n\n /**\n * Get the proto3 default value for a schema type.\n */\n protected getProto3Default(schema: TSchema): any {\n // Handle nullable/optional types - they can be undefined\n if (t.schema.isOptional(schema) || t.schema.isUnion(schema)) {\n return undefined;\n }\n\n // Handle arrays - default is empty array\n if (t.schema.isArray(schema)) {\n return [];\n }\n\n // Handle records (maps) - default is empty object\n if (t.schema.isRecord(schema)) {\n return {};\n }\n\n // Handle primitive types\n if (t.schema.isString(schema)) return \"\";\n if (t.schema.isNumber(schema)) return 0;\n if (t.schema.isInteger(schema)) return 0;\n if (t.schema.isBigInt(schema)) return BigInt(0);\n if (t.schema.isBoolean(schema)) return false;\n\n // For objects, return empty object (will be filled in recursively)\n if (t.schema.isObject(schema)) {\n return {};\n }\n\n return undefined;\n }\n}\n","import { $module } from \"alepha\";\nimport { ProtobufProvider } from \"./providers/ProtobufProvider.ts\";\nimport { ProtobufSchemaCodec } from \"./providers/ProtobufSchemaCodec.ts\";\n\nexport * from \"./providers/ProtobufProvider.ts\";\nexport * from \"./providers/ProtobufSchemaCodec.ts\";\n\n/**\n * Protocol Buffers support.\n *\n * **Features:**\n * - Message serialization/deserialization\n * - TypeBox integration\n * - Compression support\n *\n * @module alepha.protobuf\n */\nexport const AlephaProtobuf = $module({\n name: \"alepha.protobuf\",\n services: [ProtobufProvider, ProtobufSchemaCodec],\n register: (alepha) => {\n alepha.with(ProtobufProvider);\n alepha.codec.register({\n name: \"protobuf\",\n codec: alepha.inject(ProtobufSchemaCodec),\n });\n },\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/providers/ProtobufProvider.ts","../src/providers/ProtobufSchemaCodec.ts","../src/index.ts"],"sourcesContent":["import { $inject, Alepha, type TObject, type TSchema, t } from \"alepha\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly schemas: Map<string | TObject, Type> = new Map();\n protected readonly protobuf: typeof protobufjs = protobufjs;\n /**\n * @deprecated Use local enumDefinitions passed through method parameters.\n */\n protected readonly enumDefinitions: Map<string, string[]> = new Map();\n\n /**\n * Encode an object to a Uint8Array.\n */\n public encode(schema: ProtobufSchema, message: any): Uint8Array {\n return this.parse(schema).encode(message).finish();\n }\n\n /**\n * Decode a Uint8Array to an object.\n */\n public decode<T = any>(schema: ProtobufSchema, data: Uint8Array): T {\n return this.parse(schema).decode(data) as T;\n }\n\n /**\n * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n */\n public parse(schema: ProtobufSchema, typeName = \"root.Target\"): Type {\n const exists = this.schemas.get(schema);\n if (exists) {\n return exists;\n }\n\n const result = this.protobuf.parse(schema);\n const type = result.root.lookupType(typeName);\n this.schemas.set(schema, type);\n return type;\n }\n\n /**\n * Convert a TypeBox schema to a Protobuf schema as a string.\n */\n public createProtobufSchema(\n schema: TSchema,\n options: CreateProtobufSchemaOptions = {},\n ): string {\n const { rootName = \"root\", mainMessageName = \"Target\" } = options;\n // Use local enum definitions to avoid race conditions with concurrent calls\n const enumDefinitions = new Map<string, string[]>();\n\n const context = {\n proto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n fieldIndex: 1,\n };\n\n if (t.schema.isObject(schema)) {\n const { message, subMessages } = this.parseObjectWithDependencies(\n schema,\n mainMessageName,\n enumDefinitions,\n );\n\n // Add all enum definitions first\n for (const [enumName, values] of enumDefinitions) {\n context.proto += this.generateEnumDefinition(enumName, values);\n }\n\n // Add all sub-messages\n context.proto += subMessages.join(\"\");\n // Then add the main message\n context.proto += message;\n }\n\n return context.proto;\n }\n\n /**\n * Parse an object schema with dependencies (sub-messages).\n */\n protected parseObjectWithDependencies(\n obj: TSchema,\n parentName: string,\n enumDefinitions: Map<string, string[]> = this.enumDefinitions,\n ): { message: string; subMessages: string[] } {\n if (!t.schema.isObject(obj)) {\n return { message: \"\", subMessages: [] };\n }\n\n const fields: string[] = [];\n const subMessages: string[] = [];\n let fieldIndex = 1;\n\n for (const [key, value] of Object.entries(obj.properties)) {\n // Handle arrays\n if (t.schema.isArray(value)) {\n // Check if array items are enums\n if (this.isEnum(value.items)) {\n const enumValues = this.getEnumValues(value.items);\n const enumName = this.registerEnum(key, enumValues, enumDefinitions);\n fields.push(` repeated ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n if (t.schema.isObject(value.items)) {\n const subMessageName =\n \"title\" in value.items && typeof value.items.title === \"string\"\n ? value.items.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(\n value.items,\n subMessageName,\n enumDefinitions,\n );\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n const itemType = this.convertType(value.items);\n fields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle nested objects\n if (t.schema.isObject(value)) {\n const subMessageName =\n \"title\" in value && typeof value.title === \"string\"\n ? value.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(\n value,\n subMessageName,\n enumDefinitions,\n );\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle union types (nullable fields)\n if (t.schema.isUnion(value)) {\n const nonNullType = value.anyOf.find(\n (type: TSchema) => !t.schema.isNull(type),\n );\n if (nonNullType) {\n // Check if it's an enum\n if (this.isEnum(nonNullType)) {\n const enumValues = this.getEnumValues(nonNullType);\n const enumName = this.registerEnum(\n key,\n enumValues,\n enumDefinitions,\n );\n fields.push(` ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n if (t.schema.isObject(nonNullType)) {\n const subMessageName =\n \"title\" in nonNullType && typeof nonNullType.title === \"string\"\n ? nonNullType.title\n : `${parentName}_${key}`;\n const { message: subMessage, subMessages: nestedSubMessages } =\n this.parseObjectWithDependencies(\n nonNullType,\n subMessageName,\n enumDefinitions,\n );\n subMessages.push(...nestedSubMessages);\n subMessages.push(subMessage);\n fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n continue;\n }\n const fieldType = this.convertType(nonNullType);\n fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n continue;\n }\n }\n\n // Handle records (maps)\n if (t.schema.isRecord(value)) {\n // TypeBox records use additionalProperties or patternProperties for the value type\n let valueSchema: TSchema | undefined;\n if (\n \"additionalProperties\" in value &&\n value.additionalProperties &&\n typeof value.additionalProperties === \"object\"\n ) {\n valueSchema = value.additionalProperties;\n } else if (\n value.patternProperties &&\n typeof value.patternProperties === \"object\"\n ) {\n // Get the first pattern property (usually \"^(.*)$\" or similar)\n const patterns = Object.values(value.patternProperties);\n if (patterns.length > 0 && typeof patterns[0] === \"object\") {\n valueSchema = patterns[0] as TSchema;\n }\n }\n\n if (valueSchema) {\n const valueType = this.convertType(valueSchema);\n fields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);\n continue;\n }\n }\n\n // Handle enum fields\n if (this.isEnum(value)) {\n const enumValues = this.getEnumValues(value);\n const enumName = this.registerEnum(key, enumValues, enumDefinitions);\n fields.push(` ${enumName} ${key} = ${fieldIndex++};`);\n continue;\n }\n\n // Handle regular fields\n const fieldType = this.convertType(value);\n fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n }\n\n const message = `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n return { message, subMessages };\n }\n\n /**\n * Convert a primitive TypeBox schema type to a Protobuf spec type.\n */\n protected convertType(schema: TSchema): string {\n if (t.schema.isBoolean(schema)) return \"bool\";\n if (t.schema.isNumber(schema) && schema.format === \"int64\") return \"int64\";\n if (t.schema.isNumber(schema)) return \"double\";\n if (t.schema.isInteger(schema)) return \"int32\";\n if (t.schema.isBigInt(schema)) return \"int64\";\n if (t.schema.isString(schema)) return \"string\";\n\n // Handle union types (nullable)\n if (t.schema.isUnion(schema)) {\n // Find the non-null type in the union\n const nonNullType = schema.anyOf.find(\n (type: TSchema) => !t.schema.isNull(type),\n );\n if (nonNullType) {\n return this.convertType(nonNullType);\n }\n }\n\n // Handle optional types — this branch is effectively dead code because TypeBox\n // optional schemas retain their inner `type` field, so isString/isNumber/etc.\n // match first. Kept as a safety net.\n if (t.schema.isOptional(schema)) {\n return this.convertType(schema);\n }\n\n // Handle unsafe types (like enums)\n if (t.schema.isUnsafe(schema)) {\n // if it's an enum or other unsafe types, default to string\n return \"string\";\n }\n\n throw new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n }\n\n /**\n * Check if a schema is an enum type.\n * TypeBox enums have an \"enum\" property with an array of values.\n */\n protected isEnum(schema: TSchema): boolean {\n return \"enum\" in schema && Array.isArray(schema.enum);\n }\n\n /**\n * Extract enum values from a TypeBox enum schema.\n */\n protected getEnumValues(schema: TSchema): string[] {\n if (\"enum\" in schema && Array.isArray(schema.enum)) {\n return schema.enum.map(String);\n }\n return [];\n }\n\n /**\n * Register an enum and return its type name.\n * Generates a PascalCase name from the field name.\n */\n protected registerEnum(\n fieldName: string,\n values: string[],\n enumDefinitions: Map<string, string[]> = this.enumDefinitions,\n ): string {\n // Capitalize first letter of field name for enum type name\n const enumName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);\n\n // Check if we already have this exact enum registered\n const valueKey = values.join(\",\");\n const existingEnum = Array.from(enumDefinitions.entries()).find(\n ([_, enumValues]) => enumValues.join(\",\") === valueKey,\n );\n\n if (existingEnum) {\n // Reuse existing enum with same values\n return existingEnum[0];\n }\n\n // Register new enum\n enumDefinitions.set(enumName, values);\n return enumName;\n }\n\n /**\n * Generate a protobuf enum definition.\n */\n protected generateEnumDefinition(enumName: string, values: string[]): string {\n const enumValues = values\n .map((value, index) => ` ${value} = ${index};`)\n .join(\"\\n\");\n return `enum ${enumName} {\\n${enumValues}\\n}\\n`;\n }\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n rootName?: string;\n mainMessageName?: string;\n}\n","import {\n $inject,\n AlephaError,\n SchemaCodec,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport \"alepha/datetime\";\nimport { ProtobufProvider } from \"./ProtobufProvider.ts\";\n\n/**\n * ProtobufSchemaCodec handles encoding/decoding for Protobuf format.\n *\n * Key differences from JSON codec:\n * - BigInt values are kept as BigInt (not converted to string)\n * - Date values are converted to ISO strings for protobuf compatibility\n * - Binary data (Uint8Array) is kept as-is\n * - Proto3 default values are applied when decoding (to handle omitted fields)\n */\nexport class ProtobufSchemaCodec extends SchemaCodec {\n protected protobufProvider = $inject(ProtobufProvider);\n protected decoder = new TextDecoder();\n\n public encodeToString<T extends TSchema>(\n schema: T,\n value: Static<T>,\n ): string {\n const binary = this.encodeToBinary(schema, value);\n // convert binary to base64 string for text representation\n if (typeof Buffer !== \"undefined\") {\n return Buffer.from(binary).toString(\"base64\");\n } else {\n return btoa(String.fromCharCode(...binary));\n }\n }\n\n public encodeToBinary<T extends TSchema>(\n schema: T,\n value: Static<T>,\n ): Uint8Array {\n const proto = this.protobufProvider.createProtobufSchema(schema);\n return this.protobufProvider.encode(proto, value);\n }\n\n public decode<T>(schema: TSchema, value: unknown): T {\n // First decode from protobuf binary to object\n const proto = this.protobufProvider.createProtobufSchema(schema);\n\n if (value instanceof Uint8Array) {\n return this.applyProto3Defaults(\n schema,\n this.protobufProvider.decode(proto, value),\n );\n }\n\n if (typeof value === \"string\") {\n return this.applyProto3Defaults(\n schema,\n this.protobufProvider.decode(\n proto,\n typeof Buffer !== \"undefined\"\n ? Uint8Array.from(Buffer.from(value, \"base64\"))\n : Uint8Array.from(\n atob(value)\n .split(\"\")\n .map((c) => c.charCodeAt(0)),\n ),\n ),\n );\n }\n\n throw new AlephaError(\n `Unsupported value type for Protobuf decoding: ${typeof value}`,\n );\n }\n\n /**\n * Apply proto3 default values for fields that were omitted during encoding.\n * Proto3 omits fields with default values, so we need to restore them.\n * Also converts enum integers back to their string values.\n */\n protected applyProto3Defaults(schema: TSchema, value: any): any {\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n if (t.schema.isObject(schema)) {\n const result: any = { ...value };\n\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n if (!(key in result) || result[key] === undefined) {\n // Apply proto3 default values based on type\n result[key] = this.getProto3Default(propSchema);\n } else {\n // Convert enum integers to strings\n if (this.isEnum(propSchema)) {\n result[key] = this.convertEnumValue(propSchema, result[key]);\n } else if (typeof result[key] === \"object\" && result[key] !== null) {\n // Recursively apply defaults to nested objects\n result[key] = this.applyProto3Defaults(propSchema, result[key]);\n }\n }\n }\n\n return result;\n }\n\n if (t.schema.isArray(schema) && Array.isArray(value)) {\n return value.map((item) => this.applyProto3Defaults(schema.items, item));\n }\n\n return value;\n }\n\n /**\n * Check if a schema is an enum type.\n */\n protected isEnum(schema: TSchema): boolean {\n return \"enum\" in schema && Array.isArray(schema.enum);\n }\n\n /**\n * Convert an enum value from protobuf integer to TypeBox string.\n */\n protected convertEnumValue(schema: TSchema, value: any): any {\n if (\n typeof value === \"number\" &&\n \"enum\" in schema &&\n Array.isArray(schema.enum)\n ) {\n // Protobuf encodes enums as integers, convert back to string\n return schema.enum[value];\n }\n return value;\n }\n\n /**\n * Get the proto3 default value for a schema type.\n */\n protected getProto3Default(schema: TSchema): any {\n // Handle nullable/optional types - they can be undefined\n if (t.schema.isOptional(schema) || t.schema.isUnion(schema)) {\n return undefined;\n }\n\n // Handle arrays - default is empty array\n if (t.schema.isArray(schema)) {\n return [];\n }\n\n // Handle records (maps) - default is empty object\n if (t.schema.isRecord(schema)) {\n return {};\n }\n\n // Handle primitive types\n if (t.schema.isString(schema)) return \"\";\n if (t.schema.isNumber(schema)) return 0;\n if (t.schema.isInteger(schema)) return 0;\n if (t.schema.isBigInt(schema)) return BigInt(0);\n if (t.schema.isBoolean(schema)) return false;\n\n // For objects, return empty object (will be filled in recursively)\n if (t.schema.isObject(schema)) {\n return {};\n }\n\n return undefined;\n }\n}\n","import { $module } from \"alepha\";\nimport { ProtobufProvider } from \"./providers/ProtobufProvider.ts\";\nimport { ProtobufSchemaCodec } from \"./providers/ProtobufSchemaCodec.ts\";\n\nexport * from \"./providers/ProtobufProvider.ts\";\nexport * from \"./providers/ProtobufSchemaCodec.ts\";\n\n/**\n * Protocol Buffers support.\n *\n * **Features:**\n * - Message serialization/deserialization\n * - TypeBox integration\n * - Compression support\n *\n * @module alepha.protobuf\n */\nexport const AlephaProtobuf = $module({\n name: \"alepha.protobuf\",\n services: [ProtobufProvider, ProtobufSchemaCodec],\n register: (alepha) => {\n alepha.with(ProtobufProvider);\n alepha.codec.register({\n name: \"protobuf\",\n codec: alepha.inject(ProtobufSchemaCodec),\n });\n },\n});\n"],"mappings":"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"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@alepha/protobuf",
3
3
  "description": "Protocol Buffers (Protobuf) codec support for Alepha framework.",
4
4
  "author": "Nicolas Foures",
5
- "version": "0.18.2",
5
+ "version": "0.19.0",
6
6
  "type": "module",
7
7
  "engines": {
8
8
  "node": ">=22.0.0"
@@ -18,13 +18,13 @@
18
18
  "protobufjs": "^8.0.0"
19
19
  },
20
20
  "devDependencies": {
21
- "alepha": "0.18.2",
22
- "tsdown": "^0.20.3",
21
+ "alepha": "0.19.0",
22
+ "tsdown": "^0.21.4",
23
23
  "typescript": "^5.9.3",
24
- "vitest": "^4.0.18"
24
+ "vitest": "^4.1.0"
25
25
  },
26
26
  "peerDependencies": {
27
- "alepha": "0.18.2"
27
+ "alepha": "0.19.0"
28
28
  },
29
29
  "scripts": {
30
30
  "lint": "alepha lint",
@@ -6,6 +6,9 @@ export class ProtobufProvider {
6
6
  protected readonly alepha = $inject(Alepha);
7
7
  protected readonly schemas: Map<string | TObject, Type> = new Map();
8
8
  protected readonly protobuf: typeof protobufjs = protobufjs;
9
+ /**
10
+ * @deprecated Use local enumDefinitions passed through method parameters.
11
+ */
9
12
  protected readonly enumDefinitions: Map<string, string[]> = new Map();
10
13
 
11
14
  /**
@@ -45,8 +48,8 @@ export class ProtobufProvider {
45
48
  options: CreateProtobufSchemaOptions = {},
46
49
  ): string {
47
50
  const { rootName = "root", mainMessageName = "Target" } = options;
48
- // Clear enum definitions for this schema generation
49
- this.enumDefinitions.clear();
51
+ // Use local enum definitions to avoid race conditions with concurrent calls
52
+ const enumDefinitions = new Map<string, string[]>();
50
53
 
51
54
  const context = {
52
55
  proto: `package ${rootName};\nsyntax = "proto3";\n\n`,
@@ -57,10 +60,11 @@ export class ProtobufProvider {
57
60
  const { message, subMessages } = this.parseObjectWithDependencies(
58
61
  schema,
59
62
  mainMessageName,
63
+ enumDefinitions,
60
64
  );
61
65
 
62
66
  // Add all enum definitions first
63
- for (const [enumName, values] of this.enumDefinitions) {
67
+ for (const [enumName, values] of enumDefinitions) {
64
68
  context.proto += this.generateEnumDefinition(enumName, values);
65
69
  }
66
70
 
@@ -79,6 +83,7 @@ export class ProtobufProvider {
79
83
  protected parseObjectWithDependencies(
80
84
  obj: TSchema,
81
85
  parentName: string,
86
+ enumDefinitions: Map<string, string[]> = this.enumDefinitions,
82
87
  ): { message: string; subMessages: string[] } {
83
88
  if (!t.schema.isObject(obj)) {
84
89
  return { message: "", subMessages: [] };
@@ -94,7 +99,7 @@ export class ProtobufProvider {
94
99
  // Check if array items are enums
95
100
  if (this.isEnum(value.items)) {
96
101
  const enumValues = this.getEnumValues(value.items);
97
- const enumName = this.registerEnum(key, enumValues);
102
+ const enumName = this.registerEnum(key, enumValues, enumDefinitions);
98
103
  fields.push(` repeated ${enumName} ${key} = ${fieldIndex++};`);
99
104
  continue;
100
105
  }
@@ -105,7 +110,11 @@ export class ProtobufProvider {
105
110
  ? value.items.title
106
111
  : `${parentName}_${key}`;
107
112
  const { message: subMessage, subMessages: nestedSubMessages } =
108
- this.parseObjectWithDependencies(value.items, subMessageName);
113
+ this.parseObjectWithDependencies(
114
+ value.items,
115
+ subMessageName,
116
+ enumDefinitions,
117
+ );
109
118
  subMessages.push(...nestedSubMessages);
110
119
  subMessages.push(subMessage);
111
120
  fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -124,7 +133,11 @@ export class ProtobufProvider {
124
133
  ? value.title
125
134
  : `${parentName}_${key}`;
126
135
  const { message: subMessage, subMessages: nestedSubMessages } =
127
- this.parseObjectWithDependencies(value, subMessageName);
136
+ this.parseObjectWithDependencies(
137
+ value,
138
+ subMessageName,
139
+ enumDefinitions,
140
+ );
128
141
  subMessages.push(...nestedSubMessages);
129
142
  subMessages.push(subMessage);
130
143
  fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -140,7 +153,11 @@ export class ProtobufProvider {
140
153
  // Check if it's an enum
141
154
  if (this.isEnum(nonNullType)) {
142
155
  const enumValues = this.getEnumValues(nonNullType);
143
- const enumName = this.registerEnum(key, enumValues);
156
+ const enumName = this.registerEnum(
157
+ key,
158
+ enumValues,
159
+ enumDefinitions,
160
+ );
144
161
  fields.push(` ${enumName} ${key} = ${fieldIndex++};`);
145
162
  continue;
146
163
  }
@@ -151,7 +168,11 @@ export class ProtobufProvider {
151
168
  ? nonNullType.title
152
169
  : `${parentName}_${key}`;
153
170
  const { message: subMessage, subMessages: nestedSubMessages } =
154
- this.parseObjectWithDependencies(nonNullType, subMessageName);
171
+ this.parseObjectWithDependencies(
172
+ nonNullType,
173
+ subMessageName,
174
+ enumDefinitions,
175
+ );
155
176
  subMessages.push(...nestedSubMessages);
156
177
  subMessages.push(subMessage);
157
178
  fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
@@ -194,7 +215,7 @@ export class ProtobufProvider {
194
215
  // Handle enum fields
195
216
  if (this.isEnum(value)) {
196
217
  const enumValues = this.getEnumValues(value);
197
- const enumName = this.registerEnum(key, enumValues);
218
+ const enumName = this.registerEnum(key, enumValues, enumDefinitions);
198
219
  fields.push(` ${enumName} ${key} = ${fieldIndex++};`);
199
220
  continue;
200
221
  }
@@ -230,7 +251,9 @@ export class ProtobufProvider {
230
251
  }
231
252
  }
232
253
 
233
- // Handle optional types
254
+ // Handle optional types — this branch is effectively dead code because TypeBox
255
+ // optional schemas retain their inner `type` field, so isString/isNumber/etc.
256
+ // match first. Kept as a safety net.
234
257
  if (t.schema.isOptional(schema)) {
235
258
  return this.convertType(schema);
236
259
  }
@@ -266,13 +289,17 @@ export class ProtobufProvider {
266
289
  * Register an enum and return its type name.
267
290
  * Generates a PascalCase name from the field name.
268
291
  */
269
- protected registerEnum(fieldName: string, values: string[]): string {
292
+ protected registerEnum(
293
+ fieldName: string,
294
+ values: string[],
295
+ enumDefinitions: Map<string, string[]> = this.enumDefinitions,
296
+ ): string {
270
297
  // Capitalize first letter of field name for enum type name
271
298
  const enumName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
272
299
 
273
300
  // Check if we already have this exact enum registered
274
301
  const valueKey = values.join(",");
275
- const existingEnum = Array.from(this.enumDefinitions.entries()).find(
302
+ const existingEnum = Array.from(enumDefinitions.entries()).find(
276
303
  ([_, enumValues]) => enumValues.join(",") === valueKey,
277
304
  );
278
305
 
@@ -282,7 +309,7 @@ export class ProtobufProvider {
282
309
  }
283
310
 
284
311
  // Register new enum
285
- this.enumDefinitions.set(enumName, values);
312
+ enumDefinitions.set(enumName, values);
286
313
  return enumName;
287
314
  }
288
315