import { ChannelCredentials, Metadata } from '@grpc/grpc-js'; import { Logger } from '@diia-inhouse/types'; export interface DynamicGrpcClientOptions { credentials?: ChannelCredentials; reflectionTimeoutMs?: number; callTimeoutMs?: number; } export interface DynamicCallParams { address: string; /** gRPC method path in format: /package.Service/Method */ method: string; body: object; metadata?: Metadata; } /** * A gRPC client that discovers service schemas at runtime via server reflection. * * This allows making gRPC calls without compile-time proto files. The client: * 1. Fetches service schema via gRPC Server Reflection API * 2. Parses file descriptors into protobuf.js types * 3. Encodes requests and decodes responses dynamically * * Caches are maintained for: * - Parsed proto schemas (per address:service) * - gRPC clients (per address) * - Reflection clients (per address) */ export declare class DynamicGrpcClient { private readonly logger; private readonly schemaCache; private readonly clientCache; private readonly reflectionClientCache; private readonly credentials; private readonly reflectionTimeoutMs; private readonly callTimeoutMs; constructor(logger: Logger, options?: DynamicGrpcClientOptions); call(params: DynamicCallParams): Promise; close(): void; private parseMethodPath; private getMethodTypes; private encodeRequest; /** * Converts string enum values to their numeric equivalents. * * protobufjs verify() expects numeric enum values, but JSON input from UI * contains string enum names. This method recursively processes the body * and converts string enum values to numbers using the type's enum definitions. */ private convertEnumValues; private convertSingleEnumValue; private decodeResponse; private getSchema; private resolveAllTypes; /** * Creates an empty placeholder type for types that can't be resolved via reflection. * * When a gRPC server doesn't expose all transitive dependencies via reflection, * we create placeholder types to allow protobuf.js to decode responses without errors. * Fields with placeholder types will be decoded as empty objects. */ private createPlaceholderType; private findUnresolvedTypes; private coerceScalar; private isBuiltinType; private mergeDescriptorsIntoRoot; private fetchFileDescriptors; /** * Builds a unified protobuf.Root from multiple file descriptors. * * IMPORTANT: Each file descriptor is parsed separately and then merged into * a single Root. This is necessary because file descriptors from reflection * come from different proto files that may share package namespaces. * Simple concatenation would fail; we need to recursively merge namespaces. */ private buildProtoRoot; /** * Recursively merges a proto namespace/type into the target namespace. * * WHY THIS IS NEEDED: * When multiple proto files contribute to the same package (e.g., ua.gov.diia.types), * we can't just add them directly - that would throw "duplicate name" errors. * Instead, we need to merge namespace contents recursively while preserving * all types, enums, and nested messages from all contributing files. */ private mergeIntoNamespace; private executeCall; private getOrCreateClient; /** * Creates a reflection client for the gRPC Server Reflection API. * * WORKAROUND: We load the reflection.proto from @grpc/reflection package * The path resolution is fragile but necessary - if @grpc/reflection changes * its structure, this will break. */ private getOrCreateReflectionClient; /** * Resolves the path to reflection.proto from @grpc/reflection package. * * FRAGILE: This depends on @grpc/reflection internal structure. * If the package changes, this path resolution will fail. */ private resolveReflectionProtoPath; }