@alepha/protobuf 0.14.1 → 0.14.3

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
@@ -399,6 +399,7 @@ declare class EventManager {
399
399
  protected events: Record<string, Array<Hook>>;
400
400
  constructor(logFn?: () => LoggerInterface | undefined);
401
401
  protected get log(): LoggerInterface | undefined;
402
+ clear(): void;
402
403
  /**
403
404
  * Registers a hook for the specified event.
404
405
  */
@@ -931,6 +932,34 @@ interface State {
931
932
  "alepha.logger"?: LoggerInterface;
932
933
  /**
933
934
  * If defined, the Alepha container will only register this service and its dependencies.
935
+ *
936
+ * @example
937
+ * ```ts
938
+ * class MigrateCmd {
939
+ * db = $inject(DatabaseProvider);
940
+ * alepha = $inject(Alepha);
941
+ * env = $env(
942
+ * t.object({
943
+ * MIGRATE: t.optional(t.boolean()),
944
+ * }),
945
+ * );
946
+ *
947
+ * constructor() {
948
+ * if (this.env.MIGRATE) {
949
+ * this.alepha.set("alepha.target", MigrateCmd);
950
+ * }
951
+ * }
952
+ *
953
+ * ready = $hook({
954
+ * on: "ready",
955
+ * handler: async () => {
956
+ * if (this.env.MIGRATE) {
957
+ * await this.db.migrate();
958
+ * }
959
+ * },
960
+ * });
961
+ * }
962
+ * ```
934
963
  */
935
964
  "alepha.target"?: Service;
936
965
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fields: string[]","subMessages: string[]","fieldType","valueSchema: TSchema | undefined","result: any"],"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\nexport const AlephaProtobuf = $module({\n name: \"alepha.protobuf\",\n services: [ProtobufProvider, ProtobufSchemaCodec],\n register: (alepha) => {\n alepha.with(ProtobufProvider);\n alepha.codec.register(\"protobuf\", alepha.inject(ProtobufSchemaCodec));\n },\n});\n"],"mappings":";;;;;AAIA,IAAa,mBAAb,MAA8B;CAC5B,AAAmB,SAAS,QAAQ,OAAO;CAC3C,AAAmB,0BAAuC,IAAI,KAAK;CACnE,AAAmB,WAA8B;CACjD,AAAmB,kCAAyC,IAAI,KAAK;;;;CAKrE,AAAO,OAAO,QAAwB,SAA0B;AAC9D,SAAO,KAAK,MAAM,OAAO,CAAC,OAAO,QAAQ,CAAC,QAAQ;;;;;CAMpD,AAAO,OAAgB,QAAwB,MAAqB;AAClE,SAAO,KAAK,MAAM,OAAO,CAAC,OAAO,KAAK;;;;;CAMxC,AAAO,MAAM,QAAwB,WAAW,eAAqB;EACnE,MAAM,SAAS,KAAK,QAAQ,IAAI,OAAO;AACvC,MAAI,OACF,QAAO;EAIT,MAAM,OADS,KAAK,SAAS,MAAM,OAAO,CACtB,KAAK,WAAW,SAAS;AAC7C,OAAK,QAAQ,IAAI,QAAQ,KAAK;AAC9B,SAAO;;;;;CAMT,AAAO,qBACL,QACA,UAAuC,EAAE,EACjC;EACR,MAAM,EAAE,WAAW,QAAQ,kBAAkB,aAAa;AAE1D,OAAK,gBAAgB,OAAO;EAE5B,MAAM,UAAU;GACd,OAAO,WAAW,SAAS;GAC3B,YAAY;GACb;AAED,MAAI,EAAE,OAAO,SAAS,OAAO,EAAE;GAC7B,MAAM,EAAE,SAAS,gBAAgB,KAAK,4BACpC,QACA,gBACD;AAGD,QAAK,MAAM,CAAC,UAAU,WAAW,KAAK,gBACpC,SAAQ,SAAS,KAAK,uBAAuB,UAAU,OAAO;AAIhE,WAAQ,SAAS,YAAY,KAAK,GAAG;AAErC,WAAQ,SAAS;;AAGnB,SAAO,QAAQ;;;;;CAMjB,AAAU,4BACR,KACA,YAC4C;AAC5C,MAAI,CAAC,EAAE,OAAO,SAAS,IAAI,CACzB,QAAO;GAAE,SAAS;GAAI,aAAa,EAAE;GAAE;EAGzC,MAAMA,SAAmB,EAAE;EAC3B,MAAMC,cAAwB,EAAE;EAChC,IAAI,aAAa;AAEjB,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,WAAW,EAAE;AAEzD,OAAI,EAAE,OAAO,QAAQ,MAAM,EAAE;AAE3B,QAAI,KAAK,OAAO,MAAM,MAAM,EAAE;KAC5B,MAAM,aAAa,KAAK,cAAc,MAAM,MAAM;KAClD,MAAM,WAAW,KAAK,aAAa,KAAK,WAAW;AACnD,YAAO,KAAK,cAAc,SAAS,GAAG,IAAI,KAAK,aAAa,GAAG;AAC/D;;AAGF,QAAI,EAAE,OAAO,SAAS,MAAM,MAAM,EAAE;KAClC,MAAM,iBACJ,WAAW,MAAM,SAAS,OAAO,MAAM,MAAM,UAAU,WACnD,MAAM,MAAM,QACZ,GAAG,WAAW,GAAG;KACvB,MAAM,EAAE,SAAS,YAAY,aAAa,sBACxC,KAAK,4BAA4B,MAAM,OAAO,eAAe;AAC/D,iBAAY,KAAK,GAAG,kBAAkB;AACtC,iBAAY,KAAK,WAAW;AAC5B,YAAO,KAAK,cAAc,eAAe,GAAG,IAAI,KAAK,aAAa,GAAG;AACrE;;IAGF,MAAM,WAAW,KAAK,YAAY,MAAM,MAAM;AAC9C,WAAO,KAAK,cAAc,SAAS,GAAG,IAAI,KAAK,aAAa,GAAG;AAC/D;;AAIF,OAAI,EAAE,OAAO,SAAS,MAAM,EAAE;IAC5B,MAAM,iBACJ,WAAW,SAAS,OAAO,MAAM,UAAU,WACvC,MAAM,QACN,GAAG,WAAW,GAAG;IACvB,MAAM,EAAE,SAAS,YAAY,aAAa,sBACxC,KAAK,4BAA4B,OAAO,eAAe;AACzD,gBAAY,KAAK,GAAG,kBAAkB;AACtC,gBAAY,KAAK,WAAW;AAC5B,WAAO,KAAK,KAAK,eAAe,GAAG,IAAI,KAAK,aAAa,GAAG;AAC5D;;AAIF,OAAI,EAAE,OAAO,QAAQ,MAAM,EAAE;IAC3B,MAAM,cAAc,MAAM,MAAM,MAC7B,SAAkB,CAAC,EAAE,OAAO,OAAO,KAAK,CAC1C;AACD,QAAI,aAAa;AAEf,SAAI,KAAK,OAAO,YAAY,EAAE;MAC5B,MAAM,aAAa,KAAK,cAAc,YAAY;MAClD,MAAM,WAAW,KAAK,aAAa,KAAK,WAAW;AACnD,aAAO,KAAK,KAAK,SAAS,GAAG,IAAI,KAAK,aAAa,GAAG;AACtD;;AAGF,SAAI,EAAE,OAAO,SAAS,YAAY,EAAE;MAClC,MAAM,iBACJ,WAAW,eAAe,OAAO,YAAY,UAAU,WACnD,YAAY,QACZ,GAAG,WAAW,GAAG;MACvB,MAAM,EAAE,SAAS,YAAY,aAAa,sBACxC,KAAK,4BAA4B,aAAa,eAAe;AAC/D,kBAAY,KAAK,GAAG,kBAAkB;AACtC,kBAAY,KAAK,WAAW;AAC5B,aAAO,KAAK,KAAK,eAAe,GAAG,IAAI,KAAK,aAAa,GAAG;AAC5D;;KAEF,MAAMC,cAAY,KAAK,YAAY,YAAY;AAC/C,YAAO,KAAK,KAAKA,YAAU,GAAG,IAAI,KAAK,aAAa,GAAG;AACvD;;;AAKJ,OAAI,EAAE,OAAO,SAAS,MAAM,EAAE;IAE5B,IAAIC;AACJ,QACE,0BAA0B,SAC1B,MAAM,wBACN,OAAO,MAAM,yBAAyB,SAEtC,eAAc,MAAM;aAEpB,MAAM,qBACN,OAAO,MAAM,sBAAsB,UACnC;KAEA,MAAM,WAAW,OAAO,OAAO,MAAM,kBAAkB;AACvD,SAAI,SAAS,SAAS,KAAK,OAAO,SAAS,OAAO,SAChD,eAAc,SAAS;;AAI3B,QAAI,aAAa;KACf,MAAM,YAAY,KAAK,YAAY,YAAY;AAC/C,YAAO,KAAK,iBAAiB,UAAU,IAAI,IAAI,KAAK,aAAa,GAAG;AACpE;;;AAKJ,OAAI,KAAK,OAAO,MAAM,EAAE;IACtB,MAAM,aAAa,KAAK,cAAc,MAAM;IAC5C,MAAM,WAAW,KAAK,aAAa,KAAK,WAAW;AACnD,WAAO,KAAK,KAAK,SAAS,GAAG,IAAI,KAAK,aAAa,GAAG;AACtD;;GAIF,MAAM,YAAY,KAAK,YAAY,MAAM;AACzC,UAAO,KAAK,KAAK,UAAU,GAAG,IAAI,KAAK,aAAa,GAAG;;AAIzD,SAAO;GAAE,SADO,WAAW,WAAW,MAAM,OAAO,KAAK,KAAK,CAAC;GAC5C;GAAa;;;;;CAMjC,AAAU,YAAY,QAAyB;AAC7C,MAAI,EAAE,OAAO,UAAU,OAAO,CAAE,QAAO;AACvC,MAAI,EAAE,OAAO,SAAS,OAAO,IAAI,OAAO,WAAW,QAAS,QAAO;AACnE,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO;AACtC,MAAI,EAAE,OAAO,UAAU,OAAO,CAAE,QAAO;AACvC,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO;AACtC,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO;AAGtC,MAAI,EAAE,OAAO,QAAQ,OAAO,EAAE;GAE5B,MAAM,cAAc,OAAO,MAAM,MAC9B,SAAkB,CAAC,EAAE,OAAO,OAAO,KAAK,CAC1C;AACD,OAAI,YACF,QAAO,KAAK,YAAY,YAAY;;AAKxC,MAAI,EAAE,OAAO,WAAW,OAAO,CAC7B,QAAO,KAAK,YAAY,OAAO;AAIjC,MAAI,EAAE,OAAO,SAAS,OAAO,CAE3B,QAAO;AAGT,QAAM,IAAI,MAAM,qBAAqB,KAAK,UAAU,OAAO,GAAG;;;;;;CAOhE,AAAU,OAAO,QAA0B;AACzC,SAAO,UAAU,UAAU,MAAM,QAAQ,OAAO,KAAK;;;;;CAMvD,AAAU,cAAc,QAA2B;AACjD,MAAI,UAAU,UAAU,MAAM,QAAQ,OAAO,KAAK,CAChD,QAAO,OAAO,KAAK,IAAI,OAAO;AAEhC,SAAO,EAAE;;;;;;CAOX,AAAU,aAAa,WAAmB,QAA0B;EAElE,MAAM,WAAW,UAAU,OAAO,EAAE,CAAC,aAAa,GAAG,UAAU,MAAM,EAAE;EAGvE,MAAM,WAAW,OAAO,KAAK,IAAI;EACjC,MAAM,eAAe,MAAM,KAAK,KAAK,gBAAgB,SAAS,CAAC,CAAC,MAC7D,CAAC,GAAG,gBAAgB,WAAW,KAAK,IAAI,KAAK,SAC/C;AAED,MAAI,aAEF,QAAO,aAAa;AAItB,OAAK,gBAAgB,IAAI,UAAU,OAAO;AAC1C,SAAO;;;;;CAMT,AAAU,uBAAuB,UAAkB,QAA0B;AAI3E,SAAO,QAAQ,SAAS,MAHL,OAChB,KAAK,OAAO,UAAU,KAAK,MAAM,KAAK,MAAM,GAAG,CAC/C,KAAK,KAAK,CAC4B;;;;;;;;;;;;;;;ACnR7C,IAAa,sBAAb,cAAyC,YAAY;CACnD,AAAU,mBAAmB,QAAQ,iBAAiB;CACtD,AAAU,UAAU,IAAI,aAAa;CAErC,AAAO,eACL,QACA,OACQ;EACR,MAAM,SAAS,KAAK,eAAe,QAAQ,MAAM;AAEjD,MAAI,OAAO,WAAW,YACpB,QAAO,OAAO,KAAK,OAAO,CAAC,SAAS,SAAS;MAE7C,QAAO,KAAK,OAAO,aAAa,GAAG,OAAO,CAAC;;CAI/C,AAAO,eACL,QACA,OACY;EACZ,MAAM,QAAQ,KAAK,iBAAiB,qBAAqB,OAAO;AAChE,SAAO,KAAK,iBAAiB,OAAO,OAAO,MAAM;;CAGnD,AAAO,OAAU,QAAiB,OAAmB;EAEnD,MAAM,QAAQ,KAAK,iBAAiB,qBAAqB,OAAO;AAEhE,MAAI,iBAAiB,WACnB,QAAO,KAAK,oBACV,QACA,KAAK,iBAAiB,OAAO,OAAO,MAAM,CAC3C;AAGH,MAAI,OAAO,UAAU,SACnB,QAAO,KAAK,oBACV,QACA,KAAK,iBAAiB,OACpB,OACA,OAAO,WAAW,cACd,WAAW,KAAK,OAAO,KAAK,OAAO,SAAS,CAAC,GAC7C,WAAW,KACT,KAAK,MAAM,CACR,MAAM,GAAG,CACT,KAAK,MAAM,EAAE,WAAW,EAAE,CAAC,CAC/B,CACN,CACF;AAGH,QAAM,IAAI,YACR,iDAAiD,OAAO,QACzD;;;;;;;CAQH,AAAU,oBAAoB,QAAiB,OAAiB;AAC9D,MAAI,CAAC,SAAS,OAAO,UAAU,SAC7B,QAAO;AAGT,MAAI,EAAE,OAAO,SAAS,OAAO,EAAE;GAC7B,MAAMC,SAAc,EAAE,GAAG,OAAO;AAEhC,QAAK,MAAM,CAAC,KAAK,eAAe,OAAO,QAAQ,OAAO,WAAW,CAC/D,KAAI,EAAE,OAAO,WAAW,OAAO,SAAS,OAEtC,QAAO,OAAO,KAAK,iBAAiB,WAAW;YAG3C,KAAK,OAAO,WAAW,CACzB,QAAO,OAAO,KAAK,iBAAiB,YAAY,OAAO,KAAK;YACnD,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,KAE5D,QAAO,OAAO,KAAK,oBAAoB,YAAY,OAAO,KAAK;AAKrE,UAAO;;AAGT,MAAI,EAAE,OAAO,QAAQ,OAAO,IAAI,MAAM,QAAQ,MAAM,CAClD,QAAO,MAAM,KAAK,SAAS,KAAK,oBAAoB,OAAO,OAAO,KAAK,CAAC;AAG1E,SAAO;;;;;CAMT,AAAU,OAAO,QAA0B;AACzC,SAAO,UAAU,UAAU,MAAM,QAAQ,OAAO,KAAK;;;;;CAMvD,AAAU,iBAAiB,QAAiB,OAAiB;AAC3D,MACE,OAAO,UAAU,YACjB,UAAU,UACV,MAAM,QAAQ,OAAO,KAAK,CAG1B,QAAO,OAAO,KAAK;AAErB,SAAO;;;;;CAMT,AAAU,iBAAiB,QAAsB;AAE/C,MAAI,EAAE,OAAO,WAAW,OAAO,IAAI,EAAE,OAAO,QAAQ,OAAO,CACzD;AAIF,MAAI,EAAE,OAAO,QAAQ,OAAO,CAC1B,QAAO,EAAE;AAIX,MAAI,EAAE,OAAO,SAAS,OAAO,CAC3B,QAAO,EAAE;AAIX,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO;AACtC,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO;AACtC,MAAI,EAAE,OAAO,UAAU,OAAO,CAAE,QAAO;AACvC,MAAI,EAAE,OAAO,SAAS,OAAO,CAAE,QAAO,OAAO,EAAE;AAC/C,MAAI,EAAE,OAAO,UAAU,OAAO,CAAE,QAAO;AAGvC,MAAI,EAAE,OAAO,SAAS,OAAO,CAC3B,QAAO,EAAE;;;;;;AC9Jf,MAAa,iBAAiB,QAAQ;CACpC,MAAM;CACN,UAAU,CAAC,kBAAkB,oBAAoB;CACjD,WAAW,WAAW;AACpB,SAAO,KAAK,iBAAiB;AAC7B,SAAO,MAAM,SAAS,YAAY,OAAO,OAAO,oBAAoB,CAAC;;CAExE,CAAC"}
1
+ {"version":3,"file":"index.js","names":["fieldType"],"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\nexport const AlephaProtobuf = $module({\n name: \"alepha.protobuf\",\n services: [ProtobufProvider, ProtobufSchemaCodec],\n register: (alepha) => {\n alepha.with(ProtobufProvider);\n alepha.codec.register(\"protobuf\", alepha.inject(ProtobufSchemaCodec));\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.14.1",
5
+ "version": "0.14.3",
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.14.1",
22
- "tsdown": "^0.18.4",
21
+ "alepha": "0.14.3",
22
+ "tsdown": "^0.19.0-beta.5",
23
23
  "typescript": "^5.9.3",
24
24
  "vitest": "^4.0.16"
25
25
  },
26
26
  "peerDependencies": {
27
- "alepha": "0.14.1"
27
+ "alepha": "0.14.3"
28
28
  },
29
29
  "scripts": {
30
30
  "lint": "alepha lint",
@@ -0,0 +1,465 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { describe, test } from "vitest";
3
+ import { AlephaProtobuf, ProtobufProvider } from "../index.ts";
4
+
5
+ const alepha = Alepha.create().with(AlephaProtobuf);
6
+ const protobuf = alepha.inject(ProtobufProvider);
7
+
8
+ describe("ProtobufProvider", () => {
9
+ describe("Basic types", () => {
10
+ test("should handle primitive types", async ({ expect }) => {
11
+ const userSchema = t.object({
12
+ username: t.text(),
13
+ createdAt: t.datetime(),
14
+ age: t.integer(),
15
+ isActive: t.boolean(),
16
+ score: t.number(),
17
+ bigNumber: t.bigint(),
18
+ level: t.integer(),
19
+ points: t.integer(),
20
+ });
21
+
22
+ const schema = protobuf.createProtobufSchema(userSchema);
23
+ expect(schema).toBe(
24
+ `package root;
25
+ syntax = "proto3";
26
+
27
+ message Target {
28
+ string username = 1;
29
+ string createdAt = 2;
30
+ int32 age = 3;
31
+ bool isActive = 4;
32
+ double score = 5;
33
+ int64 bigNumber = 6;
34
+ int32 level = 7;
35
+ int32 points = 8;
36
+ }
37
+ `,
38
+ );
39
+ });
40
+
41
+ test("should encode and decode primitive types", async ({ expect }) => {
42
+ const userSchema = t.object({
43
+ username: t.text(),
44
+ createdAt: t.datetime(),
45
+ age: t.integer(),
46
+ isActive: t.boolean(),
47
+ });
48
+
49
+ const createdAt = new Date().toISOString();
50
+ const data = {
51
+ username: "John Doe",
52
+ createdAt,
53
+ age: 30,
54
+ isActive: true,
55
+ };
56
+ const buf = alepha.codec.encode(userSchema, data, {
57
+ as: "binary",
58
+ encoder: "protobuf",
59
+ });
60
+ expect(buf).toBeInstanceOf(Uint8Array);
61
+
62
+ const user = alepha.codec.decode(userSchema, buf, {
63
+ encoder: "protobuf",
64
+ });
65
+ // When decoding, dates come back as dayjs objects, so compare the ISO strings
66
+ expect(user.username).toBe(data.username);
67
+ expect(user.age).toBe(data.age);
68
+ expect(user.isActive).toBe(data.isActive);
69
+ // Compare datetime as ISO strings since dayjs might be used
70
+ expect(user.createdAt).toBe(createdAt);
71
+ });
72
+ });
73
+
74
+ describe("Arrays", () => {
75
+ test("should handle arrays of primitives", async ({ expect }) => {
76
+ const schema = t.object({
77
+ tags: t.array(t.text()),
78
+ scores: t.array(t.number()),
79
+ flags: t.array(t.boolean()),
80
+ });
81
+
82
+ const protoSchema = protobuf.createProtobufSchema(schema);
83
+ expect(protoSchema).toBe(
84
+ `package root;
85
+ syntax = "proto3";
86
+
87
+ message Target {
88
+ repeated string tags = 1;
89
+ repeated double scores = 2;
90
+ repeated bool flags = 3;
91
+ }
92
+ `,
93
+ );
94
+ });
95
+
96
+ test("should handle arrays of objects", async ({ expect }) => {
97
+ const schema = t.object({
98
+ users: t.array(
99
+ t.object({
100
+ name: t.text(),
101
+ age: t.integer(),
102
+ }),
103
+ ),
104
+ });
105
+
106
+ const protoSchema = protobuf.createProtobufSchema(schema);
107
+ expect(protoSchema).toBe(
108
+ `package root;
109
+ syntax = "proto3";
110
+
111
+ message Target_users {
112
+ string name = 1;
113
+ int32 age = 2;
114
+ }
115
+ message Target {
116
+ repeated Target_users users = 1;
117
+ }
118
+ `,
119
+ );
120
+ });
121
+
122
+ test("should encode and decode arrays", async ({ expect }) => {
123
+ const schema = t.object({
124
+ tags: t.array(t.text()),
125
+ users: t.array(
126
+ t.object({
127
+ name: t.text(),
128
+ age: t.integer(),
129
+ }),
130
+ ),
131
+ });
132
+
133
+ const data = {
134
+ tags: ["admin", "user"],
135
+ users: [
136
+ { name: "John", age: 30 },
137
+ { name: "Jane", age: 25 },
138
+ ],
139
+ };
140
+
141
+ const buf = alepha.codec.encode(schema, data, {
142
+ as: "binary",
143
+ encoder: "protobuf",
144
+ });
145
+ expect(buf).toBeInstanceOf(Uint8Array);
146
+
147
+ const decoded = alepha.codec.decode(schema, buf, {
148
+ encoder: "protobuf",
149
+ });
150
+ expect(decoded).toEqual(data);
151
+ });
152
+ });
153
+
154
+ describe("Nested objects", () => {
155
+ test("should handle nested objects", async ({ expect }) => {
156
+ const schema = t.object({
157
+ user: t.object({
158
+ profile: t.object({
159
+ name: t.text(),
160
+ bio: t.text(),
161
+ }),
162
+ settings: t.object({
163
+ theme: t.text(),
164
+ notifications: t.boolean(),
165
+ }),
166
+ }),
167
+ });
168
+
169
+ const protoSchema = protobuf.createProtobufSchema(schema);
170
+ expect(protoSchema).toBe(
171
+ `package root;
172
+ syntax = "proto3";
173
+
174
+ message Target_user_profile {
175
+ string name = 1;
176
+ string bio = 2;
177
+ }
178
+ message Target_user_settings {
179
+ string theme = 1;
180
+ bool notifications = 2;
181
+ }
182
+ message Target_user {
183
+ Target_user_profile profile = 1;
184
+ Target_user_settings settings = 2;
185
+ }
186
+ message Target {
187
+ Target_user user = 1;
188
+ }
189
+ `,
190
+ );
191
+ });
192
+
193
+ test("should encode and decode nested objects", async ({ expect }) => {
194
+ const schema = t.object({
195
+ user: t.object({
196
+ profile: t.object({
197
+ name: t.text(),
198
+ bio: t.text(),
199
+ }),
200
+ age: t.integer(),
201
+ }),
202
+ });
203
+
204
+ const data = {
205
+ user: {
206
+ profile: {
207
+ name: "John Doe",
208
+ bio: "Software developer",
209
+ },
210
+ age: 30,
211
+ },
212
+ };
213
+
214
+ const buf = alepha.codec.encode(schema, data, {
215
+ as: "binary",
216
+ encoder: "protobuf",
217
+ });
218
+ expect(buf).toBeInstanceOf(Uint8Array);
219
+
220
+ const decoded = alepha.codec.decode(schema, buf, {
221
+ encoder: "protobuf",
222
+ });
223
+ expect(decoded).toEqual(data);
224
+ });
225
+ });
226
+
227
+ describe("Optional and nullable types", () => {
228
+ test("should handle nullable types", async ({ expect }) => {
229
+ const schema = t.object({
230
+ name: t.text(),
231
+ email: t.nullable(t.text()),
232
+ age: t.nullable(t.integer()),
233
+ });
234
+
235
+ const protoSchema = protobuf.createProtobufSchema(schema);
236
+ expect(protoSchema).toBe(
237
+ `package root;
238
+ syntax = "proto3";
239
+
240
+ message Target {
241
+ string name = 1;
242
+ string email = 2;
243
+ int32 age = 3;
244
+ }
245
+ `,
246
+ );
247
+ });
248
+
249
+ test("should encode and decode nullable types", async ({ expect }) => {
250
+ const schema = t.object({
251
+ name: t.text(),
252
+ email: t.nullable(t.text()),
253
+ });
254
+
255
+ const data1 = {
256
+ name: "John",
257
+ email: "john@example.com",
258
+ };
259
+
260
+ const data2 = {
261
+ name: "Jane",
262
+ email: "", // In proto3, null string becomes empty string
263
+ };
264
+
265
+ const buf1 = alepha.codec.encode(schema, data1, {
266
+ as: "binary",
267
+ encoder: "protobuf",
268
+ });
269
+ const buf2 = alepha.codec.encode(schema, data2, {
270
+ as: "binary",
271
+ encoder: "protobuf",
272
+ });
273
+
274
+ const decoded1 = alepha.codec.decode(schema, buf1, {
275
+ encoder: "protobuf",
276
+ });
277
+ const decoded2 = alepha.codec.decode(schema, buf2, {
278
+ encoder: "protobuf",
279
+ });
280
+
281
+ expect(decoded1).toEqual(data1);
282
+ expect(decoded2.name).toEqual("Jane");
283
+ expect(decoded2.email).toEqual(""); // Proto3 default value for string is ""
284
+ });
285
+ });
286
+
287
+ describe("Enums and special types", () => {
288
+ test("should handle enums", async ({ expect }) => {
289
+ const schema = t.object({
290
+ status: t.enum(["ACTIVE", "INACTIVE", "PENDING"]),
291
+ role: t.enum(["USER", "ADMIN", "MODERATOR"]),
292
+ });
293
+
294
+ const protoSchema = protobuf.createProtobufSchema(schema);
295
+ expect(protoSchema).toBe(
296
+ `package root;
297
+ syntax = "proto3";
298
+
299
+ enum Status {
300
+ ACTIVE = 0;
301
+ INACTIVE = 1;
302
+ PENDING = 2;
303
+ }
304
+ enum Role {
305
+ USER = 0;
306
+ ADMIN = 1;
307
+ MODERATOR = 2;
308
+ }
309
+ message Target {
310
+ Status status = 1;
311
+ Role role = 2;
312
+ }
313
+ `,
314
+ );
315
+ });
316
+
317
+ test("should handle special string types", async ({ expect }) => {
318
+ const schema = t.object({
319
+ id: t.uuid(),
320
+ createdAt: t.datetime(),
321
+ birthDate: t.date(),
322
+ shortText: t.text({ size: "short" }),
323
+ longText: t.text({ size: "long" }),
324
+ richText: t.text({ size: "rich" }),
325
+ });
326
+
327
+ const protoSchema = protobuf.createProtobufSchema(schema);
328
+ expect(protoSchema).toBe(
329
+ `package root;
330
+ syntax = "proto3";
331
+
332
+ message Target {
333
+ string id = 1;
334
+ string createdAt = 2;
335
+ string birthDate = 3;
336
+ string shortText = 4;
337
+ string longText = 5;
338
+ string richText = 6;
339
+ }
340
+ `,
341
+ );
342
+ });
343
+
344
+ test("should encode and decode enums", async ({ expect }) => {
345
+ const schema = t.object({
346
+ status: t.enum(["ACTIVE", "INACTIVE"]),
347
+ name: t.text(),
348
+ });
349
+
350
+ const data = {
351
+ status: "ACTIVE",
352
+ name: "Test",
353
+ };
354
+
355
+ const buf = alepha.codec.encode(schema, data, {
356
+ as: "binary",
357
+ encoder: "protobuf",
358
+ });
359
+ const decoded = alepha.codec.decode(schema, buf, {
360
+ encoder: "protobuf",
361
+ });
362
+
363
+ expect(decoded).toEqual(data);
364
+ });
365
+ });
366
+
367
+ describe("Records (maps)", () => {
368
+ test("should handle records", async ({ expect }) => {
369
+ const schema = t.object({
370
+ metadata: t.record(t.text(), t.text()),
371
+ scores: t.record(t.text(), t.number()),
372
+ });
373
+
374
+ const protoSchema = protobuf.createProtobufSchema(schema);
375
+ expect(protoSchema).toBe(
376
+ `package root;
377
+ syntax = "proto3";
378
+
379
+ message Target {
380
+ map<string, string> metadata = 1;
381
+ map<string, double> scores = 2;
382
+ }
383
+ `,
384
+ );
385
+ });
386
+
387
+ test("should encode and decode records", async ({ expect }) => {
388
+ const schema = t.object({
389
+ metadata: t.record(t.text(), t.text()),
390
+ });
391
+
392
+ const data = {
393
+ metadata: {
394
+ version: "1.0.0",
395
+ author: "John Doe",
396
+ },
397
+ };
398
+
399
+ const buf = alepha.codec.encode(schema, data, {
400
+ as: "binary",
401
+ encoder: "protobuf",
402
+ });
403
+ const decoded = alepha.codec.decode(schema, buf, {
404
+ encoder: "protobuf",
405
+ });
406
+
407
+ expect(decoded).toEqual(data);
408
+ });
409
+ });
410
+
411
+ describe("Complex combinations", () => {
412
+ test("should handle complex nested structures", async ({ expect }) => {
413
+ const schema = t.object({
414
+ user: t.object({
415
+ id: t.uuid(),
416
+ profile: t.object({
417
+ name: t.text(),
418
+ age: t.nullable(t.integer()),
419
+ tags: t.array(t.text()),
420
+ }),
421
+ posts: t.array(
422
+ t.object({
423
+ title: t.text(),
424
+ content: t.text({ size: "rich" }),
425
+ metadata: t.record(t.text(), t.text()),
426
+ }),
427
+ ),
428
+ }),
429
+ status: t.enum(["ACTIVE", "INACTIVE"]),
430
+ });
431
+
432
+ const data = {
433
+ user: {
434
+ id: "123e4567-e89b-12d3-a456-426614174000",
435
+ profile: {
436
+ name: "John Doe",
437
+ age: 30,
438
+ tags: ["developer", "typescript"],
439
+ },
440
+ posts: [
441
+ {
442
+ title: "Hello World",
443
+ content: "This is my first post",
444
+ metadata: {
445
+ category: "tech",
446
+ draft: "false",
447
+ },
448
+ },
449
+ ],
450
+ },
451
+ status: "ACTIVE",
452
+ };
453
+
454
+ const buf = alepha.codec.encode(schema, data, {
455
+ as: "binary",
456
+ encoder: "protobuf",
457
+ });
458
+ const decoded = alepha.codec.decode(schema, buf, {
459
+ encoder: "protobuf",
460
+ });
461
+
462
+ expect(decoded).toEqual(data);
463
+ });
464
+ });
465
+ });