@alepha/protobuf 0.9.4 → 0.10.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.cjs CHANGED
@@ -31,27 +31,18 @@ var ProtobufProvider = class {
31
31
  protobuf = protobufjs.default;
32
32
  /**
33
33
  * Encode an object to a Uint8Array.
34
- *
35
- * @param schema - TypeBox schema used to generate the Protobuf schema.
36
- * @param data - Object to encode. Can be any object or string.
37
34
  */
38
35
  encode(schema, data) {
39
36
  return this.parse(schema).encode(this.alepha.parse(schema, data)).finish();
40
37
  }
41
38
  /**
42
39
  * Decode a Uint8Array to an object.
43
- *
44
- * @param schema
45
- * @param data
46
40
  */
47
41
  decode(schema, data) {
48
42
  return this.alepha.parse(schema, this.parse(schema).decode(data));
49
43
  }
50
44
  /**
51
45
  * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.
52
- *
53
- * @param schema
54
- * @param typeName
55
46
  */
56
47
  parse(schema, typeName = "root.Target") {
57
48
  const exists = this.schemas.get(schema);
@@ -64,9 +55,6 @@ var ProtobufProvider = class {
64
55
  }
65
56
  /**
66
57
  * Convert a TypeBox schema to a Protobuf schema as a string.
67
- *
68
- * @param schema
69
- * @param options
70
58
  */
71
59
  createProtobufSchema(schema, options = {}) {
72
60
  const { rootName = "root", mainMessageName = "Target" } = options;
@@ -74,59 +62,100 @@ var ProtobufProvider = class {
74
62
  proto: `package ${rootName};\nsyntax = "proto3";\n\n`,
75
63
  fieldIndex: 1
76
64
  };
77
- if (__alepha_core.TypeGuard.IsObject(schema)) {
78
- const proto = this.parseObject(schema, mainMessageName, context);
79
- context.proto += proto;
65
+ if (__alepha_core.t.schema.isObject(schema)) {
66
+ const { message, subMessages } = this.parseObjectWithDependencies(schema, mainMessageName);
67
+ context.proto += subMessages.join("");
68
+ context.proto += message;
80
69
  }
81
70
  return context.proto;
82
71
  }
83
72
  /**
84
- * Parse an object schema to a Protobuf message.
85
- *
86
- * @param obj
87
- * @param parentName
88
- * @param context
89
- * @protected
73
+ * Parse an object schema with dependencies (sub-messages).
90
74
  */
91
- parseObject(obj, parentName, context) {
92
- if (!__alepha_core.TypeGuard.IsObject(obj)) return "";
75
+ parseObjectWithDependencies(obj, parentName) {
76
+ if (!__alepha_core.t.schema.isObject(obj)) return {
77
+ message: "",
78
+ subMessages: []
79
+ };
93
80
  const fields = [];
81
+ const subMessages = [];
82
+ let fieldIndex = 1;
94
83
  for (const [key, value] of Object.entries(obj.properties)) {
95
- if (__alepha_core.TypeGuard.IsArray(value)) {
96
- if (__alepha_core.TypeGuard.IsObject(value.items)) {
97
- const subMessageName = value.items.title ?? `${parentName}_${key}`;
98
- context.proto += this.parseObject(value.items, subMessageName, {
99
- ...context,
100
- fieldIndex: 1
101
- });
102
- fields.push(` repeated ${subMessageName} ${key} = ${context.fieldIndex++};`);
84
+ if (__alepha_core.t.schema.isArray(value)) {
85
+ if (__alepha_core.t.schema.isObject(value.items)) {
86
+ const subMessageName = "title" in value.items && typeof value.items.title === "string" ? value.items.title : `${parentName}_${key}`;
87
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value.items, subMessageName);
88
+ subMessages.push(...nestedSubMessages);
89
+ subMessages.push(subMessage);
90
+ fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);
103
91
  continue;
104
92
  }
105
93
  const itemType = this.convertType(value.items);
106
- fields.push(` repeated ${itemType} ${key} = ${context.fieldIndex++};`);
94
+ fields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);
107
95
  continue;
108
96
  }
109
- if (__alepha_core.TypeGuard.IsObject(value)) {
110
- const subMessageName = `${parentName}_${key}`;
111
- context.proto += this.parseObject(value, subMessageName, context);
112
- fields.push(` ${subMessageName} ${key} = ${context.fieldIndex++};`);
97
+ if (__alepha_core.t.schema.isObject(value)) {
98
+ const subMessageName = "title" in value && typeof value.title === "string" ? value.title : `${parentName}_${key}`;
99
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value, subMessageName);
100
+ subMessages.push(...nestedSubMessages);
101
+ subMessages.push(subMessage);
102
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
113
103
  continue;
114
104
  }
115
- fields.push(` ${this.convertType(value)} ${key} = ${context.fieldIndex++};`);
105
+ if (__alepha_core.t.schema.isUnion(value)) {
106
+ const nonNullType = value.anyOf.find((type) => !__alepha_core.t.schema.isNull(type));
107
+ if (nonNullType) {
108
+ if (__alepha_core.t.schema.isObject(nonNullType)) {
109
+ const subMessageName = "title" in nonNullType && typeof nonNullType.title === "string" ? nonNullType.title : `${parentName}_${key}`;
110
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(nonNullType, subMessageName);
111
+ subMessages.push(...nestedSubMessages);
112
+ subMessages.push(subMessage);
113
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
114
+ continue;
115
+ }
116
+ const fieldType$1 = this.convertType(nonNullType);
117
+ fields.push(` ${fieldType$1} ${key} = ${fieldIndex++};`);
118
+ continue;
119
+ }
120
+ }
121
+ if (__alepha_core.t.schema.isRecord(value)) {
122
+ let valueSchema;
123
+ if ("additionalProperties" in value && value.additionalProperties && typeof value.additionalProperties === "object") valueSchema = value.additionalProperties;
124
+ else if (value.patternProperties && typeof value.patternProperties === "object") {
125
+ const patterns = Object.values(value.patternProperties);
126
+ if (patterns.length > 0 && typeof patterns[0] === "object") valueSchema = patterns[0];
127
+ }
128
+ if (valueSchema) {
129
+ const valueType = this.convertType(valueSchema);
130
+ fields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);
131
+ continue;
132
+ }
133
+ }
134
+ const fieldType = this.convertType(value);
135
+ fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);
116
136
  }
117
- return `message ${parentName} {\n${fields.join("\n")}\n}\n`;
137
+ const message = `message ${parentName} {\n${fields.join("\n")}\n}\n`;
138
+ return {
139
+ message,
140
+ subMessages
141
+ };
118
142
  }
119
143
  /**
120
144
  * Convert a primitive TypeBox schema type to a Protobuf spec type.
121
- *
122
- * @param schema
123
- * @protected
124
145
  */
125
146
  convertType(schema) {
126
- if (__alepha_core.TypeGuard.IsInteger(schema)) return "int32";
127
- if (__alepha_core.TypeGuard.IsNumber(schema)) return "double";
128
- if (__alepha_core.TypeGuard.IsString(schema)) return "string";
129
- if (__alepha_core.TypeGuard.IsBoolean(schema)) return "bool";
147
+ if (__alepha_core.t.schema.isBoolean(schema)) return "bool";
148
+ if (__alepha_core.t.schema.isNumber(schema) && schema.format === "int64") return "int64";
149
+ if (__alepha_core.t.schema.isNumber(schema)) return "double";
150
+ if (__alepha_core.t.schema.isInteger(schema)) return "int32";
151
+ if (__alepha_core.t.schema.isBigInt(schema)) return "int64";
152
+ if (__alepha_core.t.schema.isString(schema)) return "string";
153
+ if (__alepha_core.t.schema.isUnion(schema)) {
154
+ const nonNullType = schema.anyOf.find((type) => !__alepha_core.t.schema.isNull(type));
155
+ if (nonNullType) return this.convertType(nonNullType);
156
+ }
157
+ if (__alepha_core.t.schema.isOptional(schema)) return this.convertType(schema);
158
+ if (__alepha_core.t.schema.isUnsafe(schema)) return "string";
130
159
  throw new Error(`Unsupported type: ${JSON.stringify(schema)}`);
131
160
  }
132
161
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Alepha","schema: TObject","data: any","schema: T","data: Uint8Array","schema: ProtobufSchema | TObject","schema: TSchema","options: CreateProtobufSchemaOptions","TypeGuard","obj: TSchema","parentName: string","context: { proto: string; fieldIndex: number }","fields: string[]"],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":["import type { Static, TObject, TProperties, TSchema } from \"@alepha/core\";\nimport { $inject, Alepha, TypeGuard } from \"@alepha/core\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly schemas: Map<string | TObject<TProperties>, Type> =\n\t\tnew Map();\n\tprotected readonly protobuf: typeof protobufjs = protobufjs;\n\n\t/**\n\t * Encode an object to a Uint8Array.\n\t *\n\t * @param schema - TypeBox schema used to generate the Protobuf schema.\n\t * @param data - Object to encode. Can be any object or string.\n\t */\n\tpublic encode(schema: TObject, data: any): Uint8Array {\n\t\treturn this.parse(schema).encode(this.alepha.parse(schema, data)).finish();\n\t}\n\n\t/**\n\t * Decode a Uint8Array to an object.\n\t *\n\t * @param schema\n\t * @param data\n\t */\n\tpublic decode<T extends TObject>(schema: T, data: Uint8Array): Static<T> {\n\t\treturn this.alepha.parse(schema, this.parse(schema).decode(data));\n\t}\n\n\t/**\n\t * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n\t *\n\t * @param schema\n\t * @param typeName\n\t */\n\tpublic parse(\n\t\tschema: ProtobufSchema | TObject,\n\t\ttypeName = \"root.Target\",\n\t): Type {\n\t\tconst exists = this.schemas.get(schema);\n\t\tif (exists) return exists;\n\n\t\tconst pbSchema =\n\t\t\ttypeof schema === \"string\" ? schema : this.createProtobufSchema(schema);\n\t\tconst result = this.protobuf.parse(pbSchema);\n\t\tconst type = result.root.lookupType(typeName);\n\t\tthis.schemas.set(schema, type);\n\t\treturn type;\n\t}\n\n\t/**\n\t * Convert a TypeBox schema to a Protobuf schema as a string.\n\t *\n\t * @param schema\n\t * @param options\n\t */\n\tpublic createProtobufSchema(\n\t\tschema: TSchema,\n\t\toptions: CreateProtobufSchemaOptions = {},\n\t): string {\n\t\tconst { rootName = \"root\", mainMessageName = \"Target\" } = options;\n\t\tconst context = {\n\t\t\tproto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n\t\t\tfieldIndex: 1,\n\t\t};\n\n\t\tif (TypeGuard.IsObject(schema)) {\n\t\t\tconst proto = this.parseObject(schema, mainMessageName, context);\n\t\t\tcontext.proto += proto;\n\t\t}\n\n\t\treturn context.proto;\n\t}\n\n\t/**\n\t * Parse an object schema to a Protobuf message.\n\t *\n\t * @param obj\n\t * @param parentName\n\t * @param context\n\t * @protected\n\t */\n\tprotected parseObject(\n\t\tobj: TSchema,\n\t\tparentName: string,\n\t\tcontext: { proto: string; fieldIndex: number },\n\t): string {\n\t\tif (!TypeGuard.IsObject(obj)) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst fields: string[] = [];\n\n\t\tfor (const [key, value] of Object.entries(obj.properties)) {\n\t\t\tif (TypeGuard.IsArray(value)) {\n\t\t\t\tif (TypeGuard.IsObject(value.items)) {\n\t\t\t\t\tconst subMessageName = value.items.title ?? `${parentName}_${key}`;\n\t\t\t\t\tcontext.proto += this.parseObject(value.items, subMessageName, {\n\t\t\t\t\t\t...context,\n\t\t\t\t\t\tfieldIndex: 1,\n\t\t\t\t\t});\n\t\t\t\t\tfields.push(\n\t\t\t\t\t\t` repeated ${subMessageName} ${key} = ${context.fieldIndex++};`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst itemType = this.convertType(value.items);\n\t\t\t\tfields.push(` repeated ${itemType} ${key} = ${context.fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (TypeGuard.IsObject(value)) {\n\t\t\t\tconst subMessageName = `${parentName}_${key}`;\n\t\t\t\tcontext.proto += this.parseObject(value, subMessageName, context);\n\t\t\t\tfields.push(` ${subMessageName} ${key} = ${context.fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfields.push(\n\t\t\t\t` ${this.convertType(value)} ${key} = ${context.fieldIndex++};`,\n\t\t\t);\n\t\t}\n\n\t\treturn `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n\t}\n\n\t/**\n\t * Convert a primitive TypeBox schema type to a Protobuf spec type.\n\t *\n\t * @param schema\n\t * @protected\n\t */\n\tprotected convertType(schema: TSchema): string {\n\t\tif (TypeGuard.IsInteger(schema)) return \"int32\";\n\t\tif (TypeGuard.IsNumber(schema)) return \"double\";\n\t\tif (TypeGuard.IsString(schema)) return \"string\";\n\t\tif (TypeGuard.IsBoolean(schema)) return \"bool\";\n\n\t\tthrow new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n\t}\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n\trootName?: string;\n\tmainMessageName?: string;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Alepha","t","fields: string[]","subMessages: string[]","fieldType","valueSchema: TSchema | undefined"],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":["import {\n\t$inject,\n\tAlepha,\n\ttype Static,\n\ttype TObject,\n\ttype TSchema,\n\tt,\n} from \"@alepha/core\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly schemas: Map<string | TObject, Type> = new Map();\n\tprotected readonly protobuf: typeof protobufjs = protobufjs;\n\n\t/**\n\t * Encode an object to a Uint8Array.\n\t */\n\tpublic encode(schema: TObject, data: any): Uint8Array {\n\t\treturn this.parse(schema).encode(this.alepha.parse(schema, data)).finish();\n\t}\n\n\t/**\n\t * Decode a Uint8Array to an object.\n\t */\n\tpublic decode<T extends TObject>(schema: T, data: Uint8Array): Static<T> {\n\t\treturn this.alepha.parse(schema, this.parse(schema).decode(data));\n\t}\n\n\t/**\n\t * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n\t */\n\tpublic parse(\n\t\tschema: ProtobufSchema | TObject,\n\t\ttypeName = \"root.Target\",\n\t): Type {\n\t\tconst exists = this.schemas.get(schema);\n\t\tif (exists) return exists;\n\n\t\tconst pbSchema =\n\t\t\ttypeof schema === \"string\" ? schema : this.createProtobufSchema(schema);\n\t\tconst result = this.protobuf.parse(pbSchema);\n\t\tconst type = result.root.lookupType(typeName);\n\t\tthis.schemas.set(schema, type);\n\t\treturn type;\n\t}\n\n\t/**\n\t * Convert a TypeBox schema to a Protobuf schema as a string.\n\t */\n\tpublic createProtobufSchema(\n\t\tschema: TSchema,\n\t\toptions: CreateProtobufSchemaOptions = {},\n\t): string {\n\t\tconst { rootName = \"root\", mainMessageName = \"Target\" } = options;\n\t\tconst context = {\n\t\t\tproto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n\t\t\tfieldIndex: 1,\n\t\t};\n\n\t\tif (t.schema.isObject(schema)) {\n\t\t\tconst { message, subMessages } = this.parseObjectWithDependencies(\n\t\t\t\tschema,\n\t\t\t\tmainMessageName,\n\t\t\t);\n\t\t\t// Add all sub-messages first\n\t\t\tcontext.proto += subMessages.join(\"\");\n\t\t\t// Then add the main message\n\t\t\tcontext.proto += message;\n\t\t}\n\n\t\treturn context.proto;\n\t}\n\n\t/**\n\t * Parse an object schema with dependencies (sub-messages).\n\t */\n\tprotected parseObjectWithDependencies(\n\t\tobj: TSchema,\n\t\tparentName: string,\n\t): { message: string; subMessages: string[] } {\n\t\tif (!t.schema.isObject(obj)) {\n\t\t\treturn { message: \"\", subMessages: [] };\n\t\t}\n\n\t\tconst fields: string[] = [];\n\t\tconst subMessages: string[] = [];\n\t\tlet fieldIndex = 1;\n\n\t\tfor (const [key, value] of Object.entries(obj.properties)) {\n\t\t\t// Handle arrays\n\t\t\tif (t.schema.isArray(value)) {\n\t\t\t\tif (t.schema.isObject(value.items)) {\n\t\t\t\t\tconst subMessageName =\n\t\t\t\t\t\t\"title\" in value.items && typeof value.items.title === \"string\"\n\t\t\t\t\t\t\t? value.items.title\n\t\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\t\tthis.parseObjectWithDependencies(value.items, subMessageName);\n\t\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\t\tfields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst itemType = this.convertType(value.items);\n\t\t\t\tfields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Handle nested objects\n\t\t\tif (t.schema.isObject(value)) {\n\t\t\t\tconst subMessageName =\n\t\t\t\t\t\"title\" in value && typeof value.title === \"string\"\n\t\t\t\t\t\t? value.title\n\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\tthis.parseObjectWithDependencies(value, subMessageName);\n\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\tfields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Handle union types (nullable fields)\n\t\t\tif (t.schema.isUnion(value)) {\n\t\t\t\tconst nonNullType = value.anyOf.find(\n\t\t\t\t\t(type: TSchema) => !t.schema.isNull(type),\n\t\t\t\t);\n\t\t\t\tif (nonNullType) {\n\t\t\t\t\tif (t.schema.isObject(nonNullType)) {\n\t\t\t\t\t\tconst subMessageName =\n\t\t\t\t\t\t\t\"title\" in nonNullType && typeof nonNullType.title === \"string\"\n\t\t\t\t\t\t\t\t? nonNullType.title\n\t\t\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\t\t\tthis.parseObjectWithDependencies(nonNullType, subMessageName);\n\t\t\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\t\t\tfields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst fieldType = this.convertType(nonNullType);\n\t\t\t\t\tfields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle records (maps)\n\t\t\tif (t.schema.isRecord(value)) {\n\t\t\t\t// TypeBox records use additionalProperties or patternProperties for the value type\n\t\t\t\tlet valueSchema: TSchema | undefined;\n\t\t\t\tif (\n\t\t\t\t\t\"additionalProperties\" in value &&\n\t\t\t\t\tvalue.additionalProperties &&\n\t\t\t\t\ttypeof value.additionalProperties === \"object\"\n\t\t\t\t) {\n\t\t\t\t\tvalueSchema = value.additionalProperties;\n\t\t\t\t} else if (\n\t\t\t\t\tvalue.patternProperties &&\n\t\t\t\t\ttypeof value.patternProperties === \"object\"\n\t\t\t\t) {\n\t\t\t\t\t// Get the first pattern property (usually \"^(.*)$\" or similar)\n\t\t\t\t\tconst patterns = Object.values(value.patternProperties);\n\t\t\t\t\tif (patterns.length > 0 && typeof patterns[0] === \"object\") {\n\t\t\t\t\t\tvalueSchema = patterns[0] as TSchema;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (valueSchema) {\n\t\t\t\t\tconst valueType = this.convertType(valueSchema);\n\t\t\t\t\tfields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle regular fields\n\t\t\tconst fieldType = this.convertType(value);\n\t\t\tfields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n\t\t}\n\n\t\tconst message = `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n\t\treturn { message, subMessages };\n\t}\n\n\t/**\n\t * Convert a primitive TypeBox schema type to a Protobuf spec type.\n\t */\n\tprotected convertType(schema: TSchema): string {\n\t\tif (t.schema.isBoolean(schema)) return \"bool\";\n\t\tif (t.schema.isNumber(schema) && schema.format === \"int64\") return \"int64\";\n\t\tif (t.schema.isNumber(schema)) return \"double\";\n\t\tif (t.schema.isInteger(schema)) return \"int32\";\n\t\tif (t.schema.isBigInt(schema)) return \"int64\";\n\t\tif (t.schema.isString(schema)) return \"string\";\n\n\t\t// Handle union types (nullable)\n\t\tif (t.schema.isUnion(schema)) {\n\t\t\t// Find the non-null type in the union\n\t\t\tconst nonNullType = schema.anyOf.find(\n\t\t\t\t(type: TSchema) => !t.schema.isNull(type),\n\t\t\t);\n\t\t\tif (nonNullType) {\n\t\t\t\treturn this.convertType(nonNullType);\n\t\t\t}\n\t\t}\n\n\t\t// Handle optional types\n\t\tif (t.schema.isOptional(schema)) {\n\t\t\treturn this.convertType(schema);\n\t\t}\n\n\t\t// Handle unsafe types (like enums)\n\t\tif (t.schema.isUnsafe(schema)) {\n\t\t\t// if it's an enum or other unsafe types, default to string\n\t\t\treturn \"string\";\n\t\t}\n\n\t\tthrow new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n\t}\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n\trootName?: string;\n\tmainMessageName?: string;\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,56 +1,36 @@
1
- import { Alepha, Static, TObject, TProperties, TSchema } from "@alepha/core";
1
+ import { Alepha, Static, TObject, TSchema } from "@alepha/core";
2
2
  import protobufjs, { Type } from "protobufjs";
3
3
 
4
4
  //#region src/providers/ProtobufProvider.d.ts
5
5
  declare class ProtobufProvider {
6
6
  protected readonly alepha: Alepha;
7
- protected readonly schemas: Map<string | TObject<TProperties>, Type>;
7
+ protected readonly schemas: Map<string | TObject, Type>;
8
8
  protected readonly protobuf: typeof protobufjs;
9
9
  /**
10
10
  * Encode an object to a Uint8Array.
11
- *
12
- * @param schema - TypeBox schema used to generate the Protobuf schema.
13
- * @param data - Object to encode. Can be any object or string.
14
11
  */
15
12
  encode(schema: TObject, data: any): Uint8Array;
16
13
  /**
17
14
  * Decode a Uint8Array to an object.
18
- *
19
- * @param schema
20
- * @param data
21
15
  */
22
16
  decode<T extends TObject>(schema: T, data: Uint8Array): Static<T>;
23
17
  /**
24
18
  * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.
25
- *
26
- * @param schema
27
- * @param typeName
28
19
  */
29
20
  parse(schema: ProtobufSchema | TObject, typeName?: string): Type;
30
21
  /**
31
22
  * Convert a TypeBox schema to a Protobuf schema as a string.
32
- *
33
- * @param schema
34
- * @param options
35
23
  */
36
24
  createProtobufSchema(schema: TSchema, options?: CreateProtobufSchemaOptions): string;
37
25
  /**
38
- * Parse an object schema to a Protobuf message.
39
- *
40
- * @param obj
41
- * @param parentName
42
- * @param context
43
- * @protected
26
+ * Parse an object schema with dependencies (sub-messages).
44
27
  */
45
- protected parseObject(obj: TSchema, parentName: string, context: {
46
- proto: string;
47
- fieldIndex: number;
48
- }): string;
28
+ protected parseObjectWithDependencies(obj: TSchema, parentName: string): {
29
+ message: string;
30
+ subMessages: string[];
31
+ };
49
32
  /**
50
33
  * Convert a primitive TypeBox schema type to a Protobuf spec type.
51
- *
52
- * @param schema
53
- * @protected
54
34
  */
55
35
  protected convertType(schema: TSchema): string;
56
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":[],"mappings":";;;;AAKa,cAAA,gBAAA,CAAgB;EAAA,mBAAA,MAAA,EACH,MADG;EAAA,mBACH,OAAA,EACG,GADH,CAAA,MAAA,GACgB,OADhB,CACwB,WADxB,CAAA,EACsC,IADtC,CAAA;EAAA,mBACwB,QAAA,EAAA,OAEb,UAFa;EAAW;;;;;;EAUP,MAU7B,CAAA,MAAA,EAVF,OAUE,EAAA,IAAA,EAAA,GAAA,CAAA,EAVmB,UAUnB;EAAO;;;;;;EAWE,MAE9B,CAAA,UAbqB,OAarB,CAAA,CAAA,MAAA,EAbsC,CAatC,EAAA,IAAA,EAb+C,UAa/C,CAAA,EAb4D,MAa5D,CAbmE,CAanE,CAAA;EAAI;;;;AA+F8B;AAUtC;EAEiB,KAAA,CAAA,MAAA,EA7GP,cA6GO,GA7GU,OA6GiB,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EA3GxC,IA2GwC;;;;;;;+BAxFlC,mBACC;;;;;;;;;6BAyBJ;;;;;;;;;;gCAkDwB;;KAUnB,cAAA;UAEK,2BAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":[],"mappings":";;;;cAWa,gBAAA;EAAA,mBAAgB,MAAA,EACH,MADG;EAAA,mBAAA,OAAA,EAEA,GAFA,CAAA,MAAA,GAEa,OAFb,EAEsB,IAFtB,CAAA;qBACH,QAAA,EAAA,OAEW,UAFX;;;;QAEW,CAAA,MAAA,EAKd,OALc,EAAA,IAAA,EAAA,GAAA,CAAA,EAKO,UALP;;;;QAYK,CAAA,UAAjB,OAAiB,CAAA,CAAA,MAAA,EAAA,CAAA,EAAA,IAAA,EAAS,UAAT,CAAA,EAAsB,MAAtB,CAA6B,CAA7B,CAAA;;;;OAQhC,CAAA,MAAA,EAAA,cAAA,GAAiB,OAAjB,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EAEN,IAFM;;;;sBAmBC,CAAA,MAAA,EADD,OACC,EAAA,OAAA,CAAA,EAAA,2BAAA,CAAA,EAAA,MAAA;;;;EA0KC,UAAA,2BAAc,CAAA,GAAA,EAhJnB,OAgJmB,EAAA,UAAA,EAAA,MAAA,CAAA,EAAA;IAET,OAAA,EAAA,MAAA;;;;;;gCApCc;;KAkCnB,cAAA;UAEK,2BAAA"}
package/dist/index.d.ts CHANGED
@@ -1,56 +1,36 @@
1
- import { Alepha, Static, TObject, TProperties, TSchema } from "@alepha/core";
1
+ import { Alepha, Static, TObject, TSchema } from "@alepha/core";
2
2
  import protobufjs, { Type } from "protobufjs";
3
3
 
4
4
  //#region src/providers/ProtobufProvider.d.ts
5
5
  declare class ProtobufProvider {
6
6
  protected readonly alepha: Alepha;
7
- protected readonly schemas: Map<string | TObject<TProperties>, Type>;
7
+ protected readonly schemas: Map<string | TObject, Type>;
8
8
  protected readonly protobuf: typeof protobufjs;
9
9
  /**
10
10
  * Encode an object to a Uint8Array.
11
- *
12
- * @param schema - TypeBox schema used to generate the Protobuf schema.
13
- * @param data - Object to encode. Can be any object or string.
14
11
  */
15
12
  encode(schema: TObject, data: any): Uint8Array;
16
13
  /**
17
14
  * Decode a Uint8Array to an object.
18
- *
19
- * @param schema
20
- * @param data
21
15
  */
22
16
  decode<T extends TObject>(schema: T, data: Uint8Array): Static<T>;
23
17
  /**
24
18
  * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.
25
- *
26
- * @param schema
27
- * @param typeName
28
19
  */
29
20
  parse(schema: ProtobufSchema | TObject, typeName?: string): Type;
30
21
  /**
31
22
  * Convert a TypeBox schema to a Protobuf schema as a string.
32
- *
33
- * @param schema
34
- * @param options
35
23
  */
36
24
  createProtobufSchema(schema: TSchema, options?: CreateProtobufSchemaOptions): string;
37
25
  /**
38
- * Parse an object schema to a Protobuf message.
39
- *
40
- * @param obj
41
- * @param parentName
42
- * @param context
43
- * @protected
26
+ * Parse an object schema with dependencies (sub-messages).
44
27
  */
45
- protected parseObject(obj: TSchema, parentName: string, context: {
46
- proto: string;
47
- fieldIndex: number;
48
- }): string;
28
+ protected parseObjectWithDependencies(obj: TSchema, parentName: string): {
29
+ message: string;
30
+ subMessages: string[];
31
+ };
49
32
  /**
50
33
  * Convert a primitive TypeBox schema type to a Protobuf spec type.
51
- *
52
- * @param schema
53
- * @protected
54
34
  */
55
35
  protected convertType(schema: TSchema): string;
56
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":[],"mappings":";;;;AAKa,cAAA,gBAAA,CAAgB;EAAA,mBAAA,MAAA,EACH,MADG;EAAA,mBACH,OAAA,EACG,GADH,CAAA,MAAA,GACgB,OADhB,CACwB,WADxB,CAAA,EACsC,IADtC,CAAA;EAAA,mBACwB,QAAA,EAAA,OAEb,UAFa;EAAW;;;;;;EAUP,MAU7B,CAAA,MAAA,EAVF,OAUE,EAAA,IAAA,EAAA,GAAA,CAAA,EAVmB,UAUnB;EAAO;;;;;;EAWE,MAE9B,CAAA,UAbqB,OAarB,CAAA,CAAA,MAAA,EAbsC,CAatC,EAAA,IAAA,EAb+C,UAa/C,CAAA,EAb4D,MAa5D,CAbmE,CAanE,CAAA;EAAI;;;;AA+F8B;AAUtC;EAEiB,KAAA,CAAA,MAAA,EA7GP,cA6GO,GA7GU,OA6GiB,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EA3GxC,IA2GwC;;;;;;;+BAxFlC,mBACC;;;;;;;;;6BAyBJ;;;;;;;;;;gCAkDwB;;KAUnB,cAAA;UAEK,2BAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":[],"mappings":";;;;cAWa,gBAAA;EAAA,mBAAgB,MAAA,EACH,MADG;EAAA,mBAAA,OAAA,EAEA,GAFA,CAAA,MAAA,GAEa,OAFb,EAEsB,IAFtB,CAAA;qBACH,QAAA,EAAA,OAEW,UAFX;;;;QAEW,CAAA,MAAA,EAKd,OALc,EAAA,IAAA,EAAA,GAAA,CAAA,EAKO,UALP;;;;QAYK,CAAA,UAAjB,OAAiB,CAAA,CAAA,MAAA,EAAA,CAAA,EAAA,IAAA,EAAS,UAAT,CAAA,EAAsB,MAAtB,CAA6B,CAA7B,CAAA;;;;OAQhC,CAAA,MAAA,EAAA,cAAA,GAAiB,OAAjB,EAAA,QAAA,CAAA,EAAA,MAAA,CAAA,EAEN,IAFM;;;;sBAmBC,CAAA,MAAA,EADD,OACC,EAAA,OAAA,CAAA,EAAA,2BAAA,CAAA,EAAA,MAAA;;;;EA0KC,UAAA,2BAAc,CAAA,GAAA,EAhJnB,OAgJmB,EAAA,UAAA,EAAA,MAAA,CAAA,EAAA;IAET,OAAA,EAAA,MAAA;;;;;;gCApCc;;KAkCnB,cAAA;UAEK,2BAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $inject, Alepha, TypeGuard } from "@alepha/core";
1
+ import { $inject, Alepha, t } from "@alepha/core";
2
2
  import protobufjs from "protobufjs";
3
3
 
4
4
  //#region src/providers/ProtobufProvider.ts
@@ -8,27 +8,18 @@ var ProtobufProvider = class {
8
8
  protobuf = protobufjs;
9
9
  /**
10
10
  * Encode an object to a Uint8Array.
11
- *
12
- * @param schema - TypeBox schema used to generate the Protobuf schema.
13
- * @param data - Object to encode. Can be any object or string.
14
11
  */
15
12
  encode(schema, data) {
16
13
  return this.parse(schema).encode(this.alepha.parse(schema, data)).finish();
17
14
  }
18
15
  /**
19
16
  * Decode a Uint8Array to an object.
20
- *
21
- * @param schema
22
- * @param data
23
17
  */
24
18
  decode(schema, data) {
25
19
  return this.alepha.parse(schema, this.parse(schema).decode(data));
26
20
  }
27
21
  /**
28
22
  * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.
29
- *
30
- * @param schema
31
- * @param typeName
32
23
  */
33
24
  parse(schema, typeName = "root.Target") {
34
25
  const exists = this.schemas.get(schema);
@@ -41,9 +32,6 @@ var ProtobufProvider = class {
41
32
  }
42
33
  /**
43
34
  * Convert a TypeBox schema to a Protobuf schema as a string.
44
- *
45
- * @param schema
46
- * @param options
47
35
  */
48
36
  createProtobufSchema(schema, options = {}) {
49
37
  const { rootName = "root", mainMessageName = "Target" } = options;
@@ -51,59 +39,100 @@ var ProtobufProvider = class {
51
39
  proto: `package ${rootName};\nsyntax = "proto3";\n\n`,
52
40
  fieldIndex: 1
53
41
  };
54
- if (TypeGuard.IsObject(schema)) {
55
- const proto = this.parseObject(schema, mainMessageName, context);
56
- context.proto += proto;
42
+ if (t.schema.isObject(schema)) {
43
+ const { message, subMessages } = this.parseObjectWithDependencies(schema, mainMessageName);
44
+ context.proto += subMessages.join("");
45
+ context.proto += message;
57
46
  }
58
47
  return context.proto;
59
48
  }
60
49
  /**
61
- * Parse an object schema to a Protobuf message.
62
- *
63
- * @param obj
64
- * @param parentName
65
- * @param context
66
- * @protected
50
+ * Parse an object schema with dependencies (sub-messages).
67
51
  */
68
- parseObject(obj, parentName, context) {
69
- if (!TypeGuard.IsObject(obj)) return "";
52
+ parseObjectWithDependencies(obj, parentName) {
53
+ if (!t.schema.isObject(obj)) return {
54
+ message: "",
55
+ subMessages: []
56
+ };
70
57
  const fields = [];
58
+ const subMessages = [];
59
+ let fieldIndex = 1;
71
60
  for (const [key, value] of Object.entries(obj.properties)) {
72
- if (TypeGuard.IsArray(value)) {
73
- if (TypeGuard.IsObject(value.items)) {
74
- const subMessageName = value.items.title ?? `${parentName}_${key}`;
75
- context.proto += this.parseObject(value.items, subMessageName, {
76
- ...context,
77
- fieldIndex: 1
78
- });
79
- fields.push(` repeated ${subMessageName} ${key} = ${context.fieldIndex++};`);
61
+ if (t.schema.isArray(value)) {
62
+ if (t.schema.isObject(value.items)) {
63
+ const subMessageName = "title" in value.items && typeof value.items.title === "string" ? value.items.title : `${parentName}_${key}`;
64
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value.items, subMessageName);
65
+ subMessages.push(...nestedSubMessages);
66
+ subMessages.push(subMessage);
67
+ fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);
80
68
  continue;
81
69
  }
82
70
  const itemType = this.convertType(value.items);
83
- fields.push(` repeated ${itemType} ${key} = ${context.fieldIndex++};`);
71
+ fields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);
84
72
  continue;
85
73
  }
86
- if (TypeGuard.IsObject(value)) {
87
- const subMessageName = `${parentName}_${key}`;
88
- context.proto += this.parseObject(value, subMessageName, context);
89
- fields.push(` ${subMessageName} ${key} = ${context.fieldIndex++};`);
74
+ if (t.schema.isObject(value)) {
75
+ const subMessageName = "title" in value && typeof value.title === "string" ? value.title : `${parentName}_${key}`;
76
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(value, subMessageName);
77
+ subMessages.push(...nestedSubMessages);
78
+ subMessages.push(subMessage);
79
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
90
80
  continue;
91
81
  }
92
- fields.push(` ${this.convertType(value)} ${key} = ${context.fieldIndex++};`);
82
+ if (t.schema.isUnion(value)) {
83
+ const nonNullType = value.anyOf.find((type) => !t.schema.isNull(type));
84
+ if (nonNullType) {
85
+ if (t.schema.isObject(nonNullType)) {
86
+ const subMessageName = "title" in nonNullType && typeof nonNullType.title === "string" ? nonNullType.title : `${parentName}_${key}`;
87
+ const { message: subMessage, subMessages: nestedSubMessages } = this.parseObjectWithDependencies(nonNullType, subMessageName);
88
+ subMessages.push(...nestedSubMessages);
89
+ subMessages.push(subMessage);
90
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
91
+ continue;
92
+ }
93
+ const fieldType$1 = this.convertType(nonNullType);
94
+ fields.push(` ${fieldType$1} ${key} = ${fieldIndex++};`);
95
+ continue;
96
+ }
97
+ }
98
+ if (t.schema.isRecord(value)) {
99
+ let valueSchema;
100
+ if ("additionalProperties" in value && value.additionalProperties && typeof value.additionalProperties === "object") valueSchema = value.additionalProperties;
101
+ else if (value.patternProperties && typeof value.patternProperties === "object") {
102
+ const patterns = Object.values(value.patternProperties);
103
+ if (patterns.length > 0 && typeof patterns[0] === "object") valueSchema = patterns[0];
104
+ }
105
+ if (valueSchema) {
106
+ const valueType = this.convertType(valueSchema);
107
+ fields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);
108
+ continue;
109
+ }
110
+ }
111
+ const fieldType = this.convertType(value);
112
+ fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);
93
113
  }
94
- return `message ${parentName} {\n${fields.join("\n")}\n}\n`;
114
+ const message = `message ${parentName} {\n${fields.join("\n")}\n}\n`;
115
+ return {
116
+ message,
117
+ subMessages
118
+ };
95
119
  }
96
120
  /**
97
121
  * Convert a primitive TypeBox schema type to a Protobuf spec type.
98
- *
99
- * @param schema
100
- * @protected
101
122
  */
102
123
  convertType(schema) {
103
- if (TypeGuard.IsInteger(schema)) return "int32";
104
- if (TypeGuard.IsNumber(schema)) return "double";
105
- if (TypeGuard.IsString(schema)) return "string";
106
- if (TypeGuard.IsBoolean(schema)) return "bool";
124
+ if (t.schema.isBoolean(schema)) return "bool";
125
+ if (t.schema.isNumber(schema) && schema.format === "int64") return "int64";
126
+ if (t.schema.isNumber(schema)) return "double";
127
+ if (t.schema.isInteger(schema)) return "int32";
128
+ if (t.schema.isBigInt(schema)) return "int64";
129
+ if (t.schema.isString(schema)) return "string";
130
+ if (t.schema.isUnion(schema)) {
131
+ const nonNullType = schema.anyOf.find((type) => !t.schema.isNull(type));
132
+ if (nonNullType) return this.convertType(nonNullType);
133
+ }
134
+ if (t.schema.isOptional(schema)) return this.convertType(schema);
135
+ if (t.schema.isUnsafe(schema)) return "string";
107
136
  throw new Error(`Unsupported type: ${JSON.stringify(schema)}`);
108
137
  }
109
138
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["schema: TObject","data: any","schema: T","data: Uint8Array","schema: ProtobufSchema | TObject","schema: TSchema","options: CreateProtobufSchemaOptions","obj: TSchema","parentName: string","context: { proto: string; fieldIndex: number }","fields: string[]"],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":["import type { Static, TObject, TProperties, TSchema } from \"@alepha/core\";\nimport { $inject, Alepha, TypeGuard } from \"@alepha/core\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly schemas: Map<string | TObject<TProperties>, Type> =\n\t\tnew Map();\n\tprotected readonly protobuf: typeof protobufjs = protobufjs;\n\n\t/**\n\t * Encode an object to a Uint8Array.\n\t *\n\t * @param schema - TypeBox schema used to generate the Protobuf schema.\n\t * @param data - Object to encode. Can be any object or string.\n\t */\n\tpublic encode(schema: TObject, data: any): Uint8Array {\n\t\treturn this.parse(schema).encode(this.alepha.parse(schema, data)).finish();\n\t}\n\n\t/**\n\t * Decode a Uint8Array to an object.\n\t *\n\t * @param schema\n\t * @param data\n\t */\n\tpublic decode<T extends TObject>(schema: T, data: Uint8Array): Static<T> {\n\t\treturn this.alepha.parse(schema, this.parse(schema).decode(data));\n\t}\n\n\t/**\n\t * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n\t *\n\t * @param schema\n\t * @param typeName\n\t */\n\tpublic parse(\n\t\tschema: ProtobufSchema | TObject,\n\t\ttypeName = \"root.Target\",\n\t): Type {\n\t\tconst exists = this.schemas.get(schema);\n\t\tif (exists) return exists;\n\n\t\tconst pbSchema =\n\t\t\ttypeof schema === \"string\" ? schema : this.createProtobufSchema(schema);\n\t\tconst result = this.protobuf.parse(pbSchema);\n\t\tconst type = result.root.lookupType(typeName);\n\t\tthis.schemas.set(schema, type);\n\t\treturn type;\n\t}\n\n\t/**\n\t * Convert a TypeBox schema to a Protobuf schema as a string.\n\t *\n\t * @param schema\n\t * @param options\n\t */\n\tpublic createProtobufSchema(\n\t\tschema: TSchema,\n\t\toptions: CreateProtobufSchemaOptions = {},\n\t): string {\n\t\tconst { rootName = \"root\", mainMessageName = \"Target\" } = options;\n\t\tconst context = {\n\t\t\tproto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n\t\t\tfieldIndex: 1,\n\t\t};\n\n\t\tif (TypeGuard.IsObject(schema)) {\n\t\t\tconst proto = this.parseObject(schema, mainMessageName, context);\n\t\t\tcontext.proto += proto;\n\t\t}\n\n\t\treturn context.proto;\n\t}\n\n\t/**\n\t * Parse an object schema to a Protobuf message.\n\t *\n\t * @param obj\n\t * @param parentName\n\t * @param context\n\t * @protected\n\t */\n\tprotected parseObject(\n\t\tobj: TSchema,\n\t\tparentName: string,\n\t\tcontext: { proto: string; fieldIndex: number },\n\t): string {\n\t\tif (!TypeGuard.IsObject(obj)) {\n\t\t\treturn \"\";\n\t\t}\n\n\t\tconst fields: string[] = [];\n\n\t\tfor (const [key, value] of Object.entries(obj.properties)) {\n\t\t\tif (TypeGuard.IsArray(value)) {\n\t\t\t\tif (TypeGuard.IsObject(value.items)) {\n\t\t\t\t\tconst subMessageName = value.items.title ?? `${parentName}_${key}`;\n\t\t\t\t\tcontext.proto += this.parseObject(value.items, subMessageName, {\n\t\t\t\t\t\t...context,\n\t\t\t\t\t\tfieldIndex: 1,\n\t\t\t\t\t});\n\t\t\t\t\tfields.push(\n\t\t\t\t\t\t` repeated ${subMessageName} ${key} = ${context.fieldIndex++};`,\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst itemType = this.convertType(value.items);\n\t\t\t\tfields.push(` repeated ${itemType} ${key} = ${context.fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (TypeGuard.IsObject(value)) {\n\t\t\t\tconst subMessageName = `${parentName}_${key}`;\n\t\t\t\tcontext.proto += this.parseObject(value, subMessageName, context);\n\t\t\t\tfields.push(` ${subMessageName} ${key} = ${context.fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tfields.push(\n\t\t\t\t` ${this.convertType(value)} ${key} = ${context.fieldIndex++};`,\n\t\t\t);\n\t\t}\n\n\t\treturn `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n\t}\n\n\t/**\n\t * Convert a primitive TypeBox schema type to a Protobuf spec type.\n\t *\n\t * @param schema\n\t * @protected\n\t */\n\tprotected convertType(schema: TSchema): string {\n\t\tif (TypeGuard.IsInteger(schema)) return \"int32\";\n\t\tif (TypeGuard.IsNumber(schema)) return \"double\";\n\t\tif (TypeGuard.IsString(schema)) return \"string\";\n\t\tif (TypeGuard.IsBoolean(schema)) return \"bool\";\n\n\t\tthrow new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n\t}\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n\trootName?: string;\n\tmainMessageName?: string;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["fields: string[]","subMessages: string[]","fieldType","valueSchema: TSchema | undefined"],"sources":["../src/providers/ProtobufProvider.ts"],"sourcesContent":["import {\n\t$inject,\n\tAlepha,\n\ttype Static,\n\ttype TObject,\n\ttype TSchema,\n\tt,\n} from \"@alepha/core\";\nimport type { Type } from \"protobufjs\";\nimport protobufjs from \"protobufjs\";\n\nexport class ProtobufProvider {\n\tprotected readonly alepha = $inject(Alepha);\n\tprotected readonly schemas: Map<string | TObject, Type> = new Map();\n\tprotected readonly protobuf: typeof protobufjs = protobufjs;\n\n\t/**\n\t * Encode an object to a Uint8Array.\n\t */\n\tpublic encode(schema: TObject, data: any): Uint8Array {\n\t\treturn this.parse(schema).encode(this.alepha.parse(schema, data)).finish();\n\t}\n\n\t/**\n\t * Decode a Uint8Array to an object.\n\t */\n\tpublic decode<T extends TObject>(schema: T, data: Uint8Array): Static<T> {\n\t\treturn this.alepha.parse(schema, this.parse(schema).decode(data));\n\t}\n\n\t/**\n\t * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.\n\t */\n\tpublic parse(\n\t\tschema: ProtobufSchema | TObject,\n\t\ttypeName = \"root.Target\",\n\t): Type {\n\t\tconst exists = this.schemas.get(schema);\n\t\tif (exists) return exists;\n\n\t\tconst pbSchema =\n\t\t\ttypeof schema === \"string\" ? schema : this.createProtobufSchema(schema);\n\t\tconst result = this.protobuf.parse(pbSchema);\n\t\tconst type = result.root.lookupType(typeName);\n\t\tthis.schemas.set(schema, type);\n\t\treturn type;\n\t}\n\n\t/**\n\t * Convert a TypeBox schema to a Protobuf schema as a string.\n\t */\n\tpublic createProtobufSchema(\n\t\tschema: TSchema,\n\t\toptions: CreateProtobufSchemaOptions = {},\n\t): string {\n\t\tconst { rootName = \"root\", mainMessageName = \"Target\" } = options;\n\t\tconst context = {\n\t\t\tproto: `package ${rootName};\\nsyntax = \"proto3\";\\n\\n`,\n\t\t\tfieldIndex: 1,\n\t\t};\n\n\t\tif (t.schema.isObject(schema)) {\n\t\t\tconst { message, subMessages } = this.parseObjectWithDependencies(\n\t\t\t\tschema,\n\t\t\t\tmainMessageName,\n\t\t\t);\n\t\t\t// Add all sub-messages first\n\t\t\tcontext.proto += subMessages.join(\"\");\n\t\t\t// Then add the main message\n\t\t\tcontext.proto += message;\n\t\t}\n\n\t\treturn context.proto;\n\t}\n\n\t/**\n\t * Parse an object schema with dependencies (sub-messages).\n\t */\n\tprotected parseObjectWithDependencies(\n\t\tobj: TSchema,\n\t\tparentName: string,\n\t): { message: string; subMessages: string[] } {\n\t\tif (!t.schema.isObject(obj)) {\n\t\t\treturn { message: \"\", subMessages: [] };\n\t\t}\n\n\t\tconst fields: string[] = [];\n\t\tconst subMessages: string[] = [];\n\t\tlet fieldIndex = 1;\n\n\t\tfor (const [key, value] of Object.entries(obj.properties)) {\n\t\t\t// Handle arrays\n\t\t\tif (t.schema.isArray(value)) {\n\t\t\t\tif (t.schema.isObject(value.items)) {\n\t\t\t\t\tconst subMessageName =\n\t\t\t\t\t\t\"title\" in value.items && typeof value.items.title === \"string\"\n\t\t\t\t\t\t\t? value.items.title\n\t\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\t\tthis.parseObjectWithDependencies(value.items, subMessageName);\n\t\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\t\tfields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst itemType = this.convertType(value.items);\n\t\t\t\tfields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Handle nested objects\n\t\t\tif (t.schema.isObject(value)) {\n\t\t\t\tconst subMessageName =\n\t\t\t\t\t\"title\" in value && typeof value.title === \"string\"\n\t\t\t\t\t\t? value.title\n\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\tthis.parseObjectWithDependencies(value, subMessageName);\n\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\tfields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Handle union types (nullable fields)\n\t\t\tif (t.schema.isUnion(value)) {\n\t\t\t\tconst nonNullType = value.anyOf.find(\n\t\t\t\t\t(type: TSchema) => !t.schema.isNull(type),\n\t\t\t\t);\n\t\t\t\tif (nonNullType) {\n\t\t\t\t\tif (t.schema.isObject(nonNullType)) {\n\t\t\t\t\t\tconst subMessageName =\n\t\t\t\t\t\t\t\"title\" in nonNullType && typeof nonNullType.title === \"string\"\n\t\t\t\t\t\t\t\t? nonNullType.title\n\t\t\t\t\t\t\t\t: `${parentName}_${key}`;\n\t\t\t\t\t\tconst { message: subMessage, subMessages: nestedSubMessages } =\n\t\t\t\t\t\t\tthis.parseObjectWithDependencies(nonNullType, subMessageName);\n\t\t\t\t\t\tsubMessages.push(...nestedSubMessages);\n\t\t\t\t\t\tsubMessages.push(subMessage);\n\t\t\t\t\t\tfields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tconst fieldType = this.convertType(nonNullType);\n\t\t\t\t\tfields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle records (maps)\n\t\t\tif (t.schema.isRecord(value)) {\n\t\t\t\t// TypeBox records use additionalProperties or patternProperties for the value type\n\t\t\t\tlet valueSchema: TSchema | undefined;\n\t\t\t\tif (\n\t\t\t\t\t\"additionalProperties\" in value &&\n\t\t\t\t\tvalue.additionalProperties &&\n\t\t\t\t\ttypeof value.additionalProperties === \"object\"\n\t\t\t\t) {\n\t\t\t\t\tvalueSchema = value.additionalProperties;\n\t\t\t\t} else if (\n\t\t\t\t\tvalue.patternProperties &&\n\t\t\t\t\ttypeof value.patternProperties === \"object\"\n\t\t\t\t) {\n\t\t\t\t\t// Get the first pattern property (usually \"^(.*)$\" or similar)\n\t\t\t\t\tconst patterns = Object.values(value.patternProperties);\n\t\t\t\t\tif (patterns.length > 0 && typeof patterns[0] === \"object\") {\n\t\t\t\t\t\tvalueSchema = patterns[0] as TSchema;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (valueSchema) {\n\t\t\t\t\tconst valueType = this.convertType(valueSchema);\n\t\t\t\t\tfields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle regular fields\n\t\t\tconst fieldType = this.convertType(value);\n\t\t\tfields.push(` ${fieldType} ${key} = ${fieldIndex++};`);\n\t\t}\n\n\t\tconst message = `message ${parentName} {\\n${fields.join(\"\\n\")}\\n}\\n`;\n\t\treturn { message, subMessages };\n\t}\n\n\t/**\n\t * Convert a primitive TypeBox schema type to a Protobuf spec type.\n\t */\n\tprotected convertType(schema: TSchema): string {\n\t\tif (t.schema.isBoolean(schema)) return \"bool\";\n\t\tif (t.schema.isNumber(schema) && schema.format === \"int64\") return \"int64\";\n\t\tif (t.schema.isNumber(schema)) return \"double\";\n\t\tif (t.schema.isInteger(schema)) return \"int32\";\n\t\tif (t.schema.isBigInt(schema)) return \"int64\";\n\t\tif (t.schema.isString(schema)) return \"string\";\n\n\t\t// Handle union types (nullable)\n\t\tif (t.schema.isUnion(schema)) {\n\t\t\t// Find the non-null type in the union\n\t\t\tconst nonNullType = schema.anyOf.find(\n\t\t\t\t(type: TSchema) => !t.schema.isNull(type),\n\t\t\t);\n\t\t\tif (nonNullType) {\n\t\t\t\treturn this.convertType(nonNullType);\n\t\t\t}\n\t\t}\n\n\t\t// Handle optional types\n\t\tif (t.schema.isOptional(schema)) {\n\t\t\treturn this.convertType(schema);\n\t\t}\n\n\t\t// Handle unsafe types (like enums)\n\t\tif (t.schema.isUnsafe(schema)) {\n\t\t\t// if it's an enum or other unsafe types, default to string\n\t\t\treturn \"string\";\n\t\t}\n\n\t\tthrow new Error(`Unsupported type: ${JSON.stringify(schema)}`);\n\t}\n}\n\nexport type ProtobufSchema = string;\n\nexport interface CreateProtobufSchemaOptions {\n\trootName?: string;\n\tmainMessageName?: string;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alepha/protobuf",
3
- "version": "0.9.4",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=22.0.0"
@@ -13,16 +13,18 @@
13
13
  "src"
14
14
  ],
15
15
  "dependencies": {
16
- "@alepha/core": "0.9.4",
16
+ "@alepha/core": "0.10.0",
17
17
  "protobufjs": "^7.5.4"
18
18
  },
19
19
  "devDependencies": {
20
- "tsdown": "^0.14.1",
20
+ "@biomejs/biome": "^2.2.4",
21
+ "tsdown": "^0.15.3",
21
22
  "typescript": "^5.9.2",
22
23
  "vitest": "^3.2.4"
23
24
  },
24
25
  "scripts": {
25
26
  "test": "vitest run",
27
+ "lint": "biome check --write --unsafe",
26
28
  "build": "tsdown -c ../../tsdown.config.ts",
27
29
  "check": "tsc"
28
30
  },
@@ -1,19 +1,21 @@
1
- import type { Static, TObject, TProperties, TSchema } from "@alepha/core";
2
- import { $inject, Alepha, TypeGuard } from "@alepha/core";
1
+ import {
2
+ $inject,
3
+ Alepha,
4
+ type Static,
5
+ type TObject,
6
+ type TSchema,
7
+ t,
8
+ } from "@alepha/core";
3
9
  import type { Type } from "protobufjs";
4
10
  import protobufjs from "protobufjs";
5
11
 
6
12
  export class ProtobufProvider {
7
13
  protected readonly alepha = $inject(Alepha);
8
- protected readonly schemas: Map<string | TObject<TProperties>, Type> =
9
- new Map();
14
+ protected readonly schemas: Map<string | TObject, Type> = new Map();
10
15
  protected readonly protobuf: typeof protobufjs = protobufjs;
11
16
 
12
17
  /**
13
18
  * Encode an object to a Uint8Array.
14
- *
15
- * @param schema - TypeBox schema used to generate the Protobuf schema.
16
- * @param data - Object to encode. Can be any object or string.
17
19
  */
18
20
  public encode(schema: TObject, data: any): Uint8Array {
19
21
  return this.parse(schema).encode(this.alepha.parse(schema, data)).finish();
@@ -21,9 +23,6 @@ export class ProtobufProvider {
21
23
 
22
24
  /**
23
25
  * Decode a Uint8Array to an object.
24
- *
25
- * @param schema
26
- * @param data
27
26
  */
28
27
  public decode<T extends TObject>(schema: T, data: Uint8Array): Static<T> {
29
28
  return this.alepha.parse(schema, this.parse(schema).decode(data));
@@ -31,9 +30,6 @@ export class ProtobufProvider {
31
30
 
32
31
  /**
33
32
  * Parse a TypeBox schema to a Protobuf Type schema ready for encoding/decoding.
34
- *
35
- * @param schema
36
- * @param typeName
37
33
  */
38
34
  public parse(
39
35
  schema: ProtobufSchema | TObject,
@@ -52,9 +48,6 @@ export class ProtobufProvider {
52
48
 
53
49
  /**
54
50
  * Convert a TypeBox schema to a Protobuf schema as a string.
55
- *
56
- * @param schema
57
- * @param options
58
51
  */
59
52
  public createProtobufSchema(
60
53
  schema: TSchema,
@@ -66,78 +59,163 @@ export class ProtobufProvider {
66
59
  fieldIndex: 1,
67
60
  };
68
61
 
69
- if (TypeGuard.IsObject(schema)) {
70
- const proto = this.parseObject(schema, mainMessageName, context);
71
- context.proto += proto;
62
+ if (t.schema.isObject(schema)) {
63
+ const { message, subMessages } = this.parseObjectWithDependencies(
64
+ schema,
65
+ mainMessageName,
66
+ );
67
+ // Add all sub-messages first
68
+ context.proto += subMessages.join("");
69
+ // Then add the main message
70
+ context.proto += message;
72
71
  }
73
72
 
74
73
  return context.proto;
75
74
  }
76
75
 
77
76
  /**
78
- * Parse an object schema to a Protobuf message.
79
- *
80
- * @param obj
81
- * @param parentName
82
- * @param context
83
- * @protected
77
+ * Parse an object schema with dependencies (sub-messages).
84
78
  */
85
- protected parseObject(
79
+ protected parseObjectWithDependencies(
86
80
  obj: TSchema,
87
81
  parentName: string,
88
- context: { proto: string; fieldIndex: number },
89
- ): string {
90
- if (!TypeGuard.IsObject(obj)) {
91
- return "";
82
+ ): { message: string; subMessages: string[] } {
83
+ if (!t.schema.isObject(obj)) {
84
+ return { message: "", subMessages: [] };
92
85
  }
93
86
 
94
87
  const fields: string[] = [];
88
+ const subMessages: string[] = [];
89
+ let fieldIndex = 1;
95
90
 
96
91
  for (const [key, value] of Object.entries(obj.properties)) {
97
- if (TypeGuard.IsArray(value)) {
98
- if (TypeGuard.IsObject(value.items)) {
99
- const subMessageName = value.items.title ?? `${parentName}_${key}`;
100
- context.proto += this.parseObject(value.items, subMessageName, {
101
- ...context,
102
- fieldIndex: 1,
103
- });
104
- fields.push(
105
- ` repeated ${subMessageName} ${key} = ${context.fieldIndex++};`,
106
- );
92
+ // Handle arrays
93
+ if (t.schema.isArray(value)) {
94
+ if (t.schema.isObject(value.items)) {
95
+ const subMessageName =
96
+ "title" in value.items && typeof value.items.title === "string"
97
+ ? value.items.title
98
+ : `${parentName}_${key}`;
99
+ const { message: subMessage, subMessages: nestedSubMessages } =
100
+ this.parseObjectWithDependencies(value.items, subMessageName);
101
+ subMessages.push(...nestedSubMessages);
102
+ subMessages.push(subMessage);
103
+ fields.push(` repeated ${subMessageName} ${key} = ${fieldIndex++};`);
107
104
  continue;
108
105
  }
109
106
 
110
107
  const itemType = this.convertType(value.items);
111
- fields.push(` repeated ${itemType} ${key} = ${context.fieldIndex++};`);
108
+ fields.push(` repeated ${itemType} ${key} = ${fieldIndex++};`);
112
109
  continue;
113
110
  }
114
111
 
115
- if (TypeGuard.IsObject(value)) {
116
- const subMessageName = `${parentName}_${key}`;
117
- context.proto += this.parseObject(value, subMessageName, context);
118
- fields.push(` ${subMessageName} ${key} = ${context.fieldIndex++};`);
112
+ // Handle nested objects
113
+ if (t.schema.isObject(value)) {
114
+ const subMessageName =
115
+ "title" in value && typeof value.title === "string"
116
+ ? value.title
117
+ : `${parentName}_${key}`;
118
+ const { message: subMessage, subMessages: nestedSubMessages } =
119
+ this.parseObjectWithDependencies(value, subMessageName);
120
+ subMessages.push(...nestedSubMessages);
121
+ subMessages.push(subMessage);
122
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
119
123
  continue;
120
124
  }
121
125
 
122
- fields.push(
123
- ` ${this.convertType(value)} ${key} = ${context.fieldIndex++};`,
124
- );
126
+ // Handle union types (nullable fields)
127
+ if (t.schema.isUnion(value)) {
128
+ const nonNullType = value.anyOf.find(
129
+ (type: TSchema) => !t.schema.isNull(type),
130
+ );
131
+ if (nonNullType) {
132
+ if (t.schema.isObject(nonNullType)) {
133
+ const subMessageName =
134
+ "title" in nonNullType && typeof nonNullType.title === "string"
135
+ ? nonNullType.title
136
+ : `${parentName}_${key}`;
137
+ const { message: subMessage, subMessages: nestedSubMessages } =
138
+ this.parseObjectWithDependencies(nonNullType, subMessageName);
139
+ subMessages.push(...nestedSubMessages);
140
+ subMessages.push(subMessage);
141
+ fields.push(` ${subMessageName} ${key} = ${fieldIndex++};`);
142
+ continue;
143
+ }
144
+ const fieldType = this.convertType(nonNullType);
145
+ fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);
146
+ continue;
147
+ }
148
+ }
149
+
150
+ // Handle records (maps)
151
+ if (t.schema.isRecord(value)) {
152
+ // TypeBox records use additionalProperties or patternProperties for the value type
153
+ let valueSchema: TSchema | undefined;
154
+ if (
155
+ "additionalProperties" in value &&
156
+ value.additionalProperties &&
157
+ typeof value.additionalProperties === "object"
158
+ ) {
159
+ valueSchema = value.additionalProperties;
160
+ } else if (
161
+ value.patternProperties &&
162
+ typeof value.patternProperties === "object"
163
+ ) {
164
+ // Get the first pattern property (usually "^(.*)$" or similar)
165
+ const patterns = Object.values(value.patternProperties);
166
+ if (patterns.length > 0 && typeof patterns[0] === "object") {
167
+ valueSchema = patterns[0] as TSchema;
168
+ }
169
+ }
170
+
171
+ if (valueSchema) {
172
+ const valueType = this.convertType(valueSchema);
173
+ fields.push(` map<string, ${valueType}> ${key} = ${fieldIndex++};`);
174
+ continue;
175
+ }
176
+ }
177
+
178
+ // Handle regular fields
179
+ const fieldType = this.convertType(value);
180
+ fields.push(` ${fieldType} ${key} = ${fieldIndex++};`);
125
181
  }
126
182
 
127
- return `message ${parentName} {\n${fields.join("\n")}\n}\n`;
183
+ const message = `message ${parentName} {\n${fields.join("\n")}\n}\n`;
184
+ return { message, subMessages };
128
185
  }
129
186
 
130
187
  /**
131
188
  * Convert a primitive TypeBox schema type to a Protobuf spec type.
132
- *
133
- * @param schema
134
- * @protected
135
189
  */
136
190
  protected convertType(schema: TSchema): string {
137
- if (TypeGuard.IsInteger(schema)) return "int32";
138
- if (TypeGuard.IsNumber(schema)) return "double";
139
- if (TypeGuard.IsString(schema)) return "string";
140
- if (TypeGuard.IsBoolean(schema)) return "bool";
191
+ if (t.schema.isBoolean(schema)) return "bool";
192
+ if (t.schema.isNumber(schema) && schema.format === "int64") return "int64";
193
+ if (t.schema.isNumber(schema)) return "double";
194
+ if (t.schema.isInteger(schema)) return "int32";
195
+ if (t.schema.isBigInt(schema)) return "int64";
196
+ if (t.schema.isString(schema)) return "string";
197
+
198
+ // Handle union types (nullable)
199
+ if (t.schema.isUnion(schema)) {
200
+ // Find the non-null type in the union
201
+ const nonNullType = schema.anyOf.find(
202
+ (type: TSchema) => !t.schema.isNull(type),
203
+ );
204
+ if (nonNullType) {
205
+ return this.convertType(nonNullType);
206
+ }
207
+ }
208
+
209
+ // Handle optional types
210
+ if (t.schema.isOptional(schema)) {
211
+ return this.convertType(schema);
212
+ }
213
+
214
+ // Handle unsafe types (like enums)
215
+ if (t.schema.isUnsafe(schema)) {
216
+ // if it's an enum or other unsafe types, default to string
217
+ return "string";
218
+ }
141
219
 
142
220
  throw new Error(`Unsupported type: ${JSON.stringify(schema)}`);
143
221
  }