From 000d0fb0b5d32e3420ad7595b2f3d16b223feda5 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Sat, 20 Nov 2021 15:03:23 +0100 Subject: [PATCH 1/8] Support for Google.Protobuf.Value, ListValue and Struct --- integration/struct/struct-test.ts | 63 +++++++++++++++++ integration/struct/struct.proto | 7 ++ integration/value/value-test.ts | 54 ++++++++++++++ integration/value/value.proto | 10 +++ src/encode.ts | 2 + src/main.ts | 113 ++++++++++++++++++++++++++++-- src/types.ts | 23 ++++++ 7 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 integration/struct/struct-test.ts create mode 100644 integration/struct/struct.proto create mode 100644 integration/value/value-test.ts create mode 100644 integration/value/value.proto diff --git a/integration/struct/struct-test.ts b/integration/struct/struct-test.ts new file mode 100644 index 000000000..27b701754 --- /dev/null +++ b/integration/struct/struct-test.ts @@ -0,0 +1,63 @@ +import { Reader } from 'protobufjs'; +import { StructMessage } from './struct'; + +import { StructMessage as PbStructMessage } from './pbjs'; + +let data = { + value: { + name: 'john', + pet: null, + posts: [{ id: 1, title: 'hello world', public: true }], + }, +}; + +describe('struct', () => { + it('can encode objects', () => { + const s1 = StructMessage.fromJSON(data); + const s2 = PbStructMessage.decode(Reader.create(StructMessage.encode(s1).finish())); + + expect(s2).toMatchInlineSnapshot(` + Object { + "value": Object { + "fields": Object { + "name": Object { + "stringValue": "john", + }, + "pet": Object { + "nullValue": "NULL_VALUE", + }, + "posts": Object { + "listValue": Object { + "values": Array [ + Object { + "structValue": Object { + "fields": Object { + "id": Object { + "numberValue": 1, + }, + "public": Object { + "boolValue": true, + }, + "title": Object { + "stringValue": "hello world", + }, + }, + }, + }, + ], + }, + }, + }, + }, + } + `); + }); + + it('can decode objects', () => { + let message = StructMessage.fromJSON(data); + let encodedValue = StructMessage.encode(message).finish(); + const decodedValue = StructMessage.decode(Reader.create(encodedValue)); + + expect(StructMessage.toJSON(decodedValue)).toEqual(data); + }); +}); diff --git a/integration/struct/struct.proto b/integration/struct/struct.proto new file mode 100644 index 000000000..6aa650d40 --- /dev/null +++ b/integration/struct/struct.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +import "google/protobuf/struct.proto"; + +message StructMessage { + google.protobuf.Struct value = 1; +} diff --git a/integration/value/value-test.ts b/integration/value/value-test.ts new file mode 100644 index 000000000..921b3258e --- /dev/null +++ b/integration/value/value-test.ts @@ -0,0 +1,54 @@ +import { Reader } from 'protobufjs'; +import {ValueMessage} from "./value"; + +import { ValueMessage as PbValueMessage } from './pbjs'; + + +describe('values', () => { + it('json value', () => { + const s1 = ValueMessage.fromJSON({ + value: "Hello", + anyList: [1, "foo", true], + repeatedAny: [2, "bar", false], + }); + + const s2 = PbValueMessage.decode(Reader.create(ValueMessage.encode(s1).finish())); + + expect(s2).toMatchInlineSnapshot(` + Object { + "anyList": Object { + "values": Array [ + Object { + "numberValue": 1, + }, + Object { + "stringValue": "foo", + }, + Object { + "boolValue": true, + }, + ], + }, + "value": Object { + "stringValue": "Hello", + }, + } + `); + }); + + it('decodes lists of any types correctly', () => { + const s1 = ValueMessage.fromJSON({ + anyList: [1, "foo", true], + repeatedAny: [2, "bar", false], + }); + expect(s1).toEqual({anyList: [1, "foo", true], repeatedAny: [2, "bar", false]}); + }) + + it('toJson', () => { + const s1 = ValueMessage.fromPartial({ + anyList: [1], + repeatedAny: [2], + }); + expect(s1).toEqual({anyList: [1], repeatedAny: [2]}); + }) +}); diff --git a/integration/value/value.proto b/integration/value/value.proto new file mode 100644 index 000000000..0b4710624 --- /dev/null +++ b/integration/value/value.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +import "google/protobuf/struct.proto"; +import "google/protobuf/wrappers.proto"; + +message ValueMessage { + google.protobuf.Value value = 1; + google.protobuf.ListValue anyList = 2; + repeated google.protobuf.Value repeatedAny = 3; +} diff --git a/src/encode.ts b/src/encode.ts index d2c412058..022033d01 100644 --- a/src/encode.ts +++ b/src/encode.ts @@ -36,6 +36,8 @@ export function generateEncoder(ctx: Context, typeName: string): Code { return code`${TypeValue}.encode({value: value ?? false}).finish()`; case 'BytesValue': return code`${TypeValue}.encode({value: value ?? new Uint8Array()}).finish()`; + case 'ListValue': + return code`${TypeValue}.encode({value: value ?? []]}).finish()`; } throw new Error(`unknown wrapper type: ${name}`); diff --git a/src/main.ts b/src/main.ts index ea3744a18..537fb4e1d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,9 +14,11 @@ import { defaultValue, detectMapType, getEnumMethod, + isAnyValueType, isBytes, isBytesValueType, isEnum, + isListValueType, isLong, isLongValueType, isMapType, @@ -32,6 +34,8 @@ import { toReaderCall, toTypeName, valueTypeName, + messageToTypeName, + isStructType, } from './types'; import SourceInfo, { Fields } from './sourceInfo'; import { assertInstanceOf, FormattedMethodDescriptor, maybeAddComment, maybePrefixPackage } from './utils'; @@ -258,7 +262,8 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri export type Utils = ReturnType & ReturnType & ReturnType & - ReturnType; + ReturnType & + ReturnType; /** These are runtime utility methods used by the generated code. */ export function makeUtils(options: Options): Utils { @@ -269,6 +274,7 @@ export function makeUtils(options: Options): Utils { ...makeDeepPartial(options, longs), ...makeTimestampMethods(options, longs), ...longs, + ...makeWrappingUtils(), }; } @@ -342,6 +348,61 @@ function makeLongUtils(options: Options, bytes: ReturnType return { numberToLong, longToNumber, longToString, longInit, Long }; } +function makeWrappingUtils() { + const wrapAnyValue = conditionalOutput('wrapAnyValue', code`function wrapAnyValue(value: any): Value { + if (value === null) { + return {nullValue: 0} as Value; + } else if (typeof value === 'boolean') { + return {boolValue: value} as Value; + } else if (typeof value === 'number') { + return {numberValue: value} as Value; + } else if (typeof value === 'string') { + return {stringValue: value} as Value; + } else if (Array.isArray(value)) { + return {listValue: value} as Value; + } else if (typeof value === 'object') { + return {structValue: value} as Value; + } else if (typeof value === 'undefined') { + return {} as Value; + } else { + throw new Error('Unsupported any value type: ' + typeof value); + } + }`); + + const unwrapAnyValue = conditionalOutput('unwrapAnyValue', code`function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { + if (value.stringValue !== undefined) { + return value.stringValue; + } else if (value.numberValue !== undefined) { + return value.numberValue; + } else if (value.boolValue !== undefined) { + return value.boolValue; + } else if (value.structValue !== undefined) { + return value.structValue; + } else if (value.listValue !== undefined) { + return value.listValue; + } else if (value.nullValue !== undefined) { + return null; + } + }`) + + const wrapStruct = conditionalOutput('wrapStruct', code`function wrapStruct(object: {[key: string]: any}): Struct { + const struct = Struct.fromPartial({}); + Object.keys(object).forEach(key => { + struct.fields[key] = object[key]; + }); + return struct; + }`) + + const unwrapStruct = conditionalOutput('unwrapStruct', code`function unwrapStruct(struct: Struct): {[key: string]: any} { + const object: { [key: string]: any } = {}; + Object.keys(struct.fields).forEach(key => { + object[key] = struct.fields[key]; + }); + return object; + }`) + return { wrapAnyValue, unwrapAnyValue, wrapStruct, unwrapStruct }; +} + function makeByteUtils() { const globalThis = conditionalOutput( 'globalThis', @@ -693,8 +754,15 @@ function generateDecode(ctx: Context, fullName: string, messageDesc: DescriptorP } } } else if (isValueType(ctx, field)) { + const unwrap = (decodedValue: any): Code => { + if (isAnyValueType(field)) return code`${ctx.utils.unwrapAnyValue}(${decodedValue})`; + if (isStructType(field)) return code`${ctx.utils.unwrapStruct}(${decodedValue})`; + if (isListValueType(field)) return code`${decodedValue}.values`; + return code`${decodedValue}.value`; + } const type = basicTypeName(ctx, field, { keepValueType: true }); - readSnippet = code`${type}.decode(reader, reader.uint32()).value`; + const decoder = code`${type}.decode(reader, reader.uint32())` + readSnippet = code`${unwrap(decoder)}`; } else if (isTimestamp(field) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING)) { const type = basicTypeName(ctx, field, { keepValueType: true }); readSnippet = code`${utils.fromTimestamp}(${type}.decode(reader, reader.uint32()))`; @@ -789,11 +857,18 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP writeSnippet = (place) => code`${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`; } else if (isValueType(ctx, field)) { + const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : ''; + + const wrappedValue = (place: string): Code => { + if (isAnyValueType(field)) return code`${ctx.utils.wrapAnyValue}(${place})`; + if (isListValueType(field)) return code`{values: ${place}}`; + if (isStructType(field)) return code`${ctx.utils.wrapStruct}(${place})`; + return code`{${maybeTypeField} value: ${place}!}`; + } + const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); - const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : ''; - writeSnippet = (place) => - code`${type}.encode({ ${maybeTypeField} value: ${place}! }, writer.uint32(${tag}).fork()).ldelim()`; + writeSnippet = (place) => code`${type}.encode(${wrappedValue(place)}, writer.uint32(${tag}).fork()).ldelim()`; } else if (isMessage(field)) { const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field); @@ -932,12 +1007,16 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto (options.useDate === DateOption.DATE || options.useDate === DateOption.TIMESTAMP) ) { return code`${utils.fromJsonTimestamp}(${from})`; + } else if (isAnyValueType(field) || isStructType(field)) { + return code`${from}`; } else if (isValueType(ctx, field)) { const valueType = valueTypeName(ctx, field.typeName)!; if (isLongValueType(field) && options.forceLong === LongOption.LONG) { return code`${capitalize(valueType.toCodeString())}.fromValue(${from})`; } else if (isBytesValueType(field)) { return code`new ${capitalize(valueType.toCodeString())}(${from})`; + } else if (isListValueType(field)) { + return code`[...${from}]`; } else { return code`${capitalize(valueType.toCodeString())}(${from})`; } @@ -968,6 +1047,8 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto } else if (isValueType(ctx, valueType)) { const type = basicTypeName(ctx, valueType); return code`${from} as ${type}`; + } else if (isAnyValueType(valueType)) { + return code`${from}`; } else { const type = basicTypeName(ctx, valueType); return code`${type}.fromJSON(${from})`; @@ -993,6 +1074,10 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto }); `); chunks.push(code`}`); + } else if (isAnyValueType(field)) { + chunks.push(code` + message.${fieldName} = Array.isArray(object?.${fieldName}) ? [...object.${fieldName}] : []; + `); } else { // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here. chunks.push(code` @@ -1006,6 +1091,16 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto message.${oneofName} = { $case: '${fieldName}', ${fieldName}: ${readSnippet(`object.${fieldName}`)} } `); chunks.push(code`}`); + } else if (isAnyValueType(field)) { + chunks.push(code`message.${fieldName} = object.${fieldName};`); + } else if (isStructType(field)) { + chunks.push(code`message.${fieldName} = typeof(object.${fieldName}) === 'object' ? object.${fieldName} : undefined;`); + } else if (isListValueType(field)) { + chunks.push(code` + message.${fieldName} = Array.isArray(object?.${fieldName}) + ? ${readSnippet(`object.${fieldName}`)} + : ${'undefined'}; + `); } else { const fallback = isWithinOneOf(field) ? 'undefined' : defaultValue(ctx, field); chunks.push(code` @@ -1063,10 +1158,14 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP return code`${utils.fromTimestamp}(${from}).toISOString()`; } else if (isScalar(valueType) || isValueType(ctx, valueType)) { return code`${from}`; + } else if (isAnyValueType(valueType)) { + return code`${from}`; } else { const type = basicTypeName(ctx, valueType); return code`${type}.toJSON(${from})`; } + } else if (isAnyValueType(field)) { + return code`${from}`; } else if (isMessage(field) && !isValueType(ctx, field) && !isMapType(ctx, messageDesc, field)) { const type = basicTypeName(ctx, field, { keepValueType: true }); return code`${from} ? ${type}.toJSON(${from}) : ${defaultValue(ctx, field)}`; @@ -1152,6 +1251,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const cstr = capitalize(basicTypeName(ctx, valueType).toCodeString()); return code`${cstr}(${from})`; } + } else if (isAnyValueType(valueType)) { + return code`${from}` } else if ( isTimestamp(valueType) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING) @@ -1163,6 +1264,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const type = basicTypeName(ctx, valueType); return code`${type}.fromPartial(${from})`; } + } else if (isAnyValueType(field)) { + return code`${from}` } else { const type = basicTypeName(ctx, field); return code`${type}.fromPartial(${from})`; diff --git a/src/types.ts b/src/types.ts index 6e162b074..7d1c64496 100644 --- a/src/types.ts +++ b/src/types.ts @@ -370,10 +370,26 @@ export function isValueType(ctx: Context, field: FieldDescriptorProto): boolean return valueTypeName(ctx, field.typeName) !== undefined; } +export function isAnyValueType(field: FieldDescriptorProto): boolean { + return field.typeName === '.google.protobuf.Value' +} + export function isBytesValueType(field: FieldDescriptorProto): boolean { return field.typeName === '.google.protobuf.BytesValue'; } +export function isListValueType(field: FieldDescriptorProto): boolean { + return isListValueTypeName(field.typeName) +} + +export function isListValueTypeName(typeName: string): boolean { + return typeName === 'google.protobuf.ListValue' || typeName === '.google.protobuf.ListValue'; +} + +export function isStructType(field: FieldDescriptorProto): boolean { + return field.typeName === '.google.protobuf.Struct'; +} + export function isLongValueType(field: FieldDescriptorProto): boolean { return field.typeName === '.google.protobuf.Int64Value' || field.typeName === '.google.protobuf.UInt64Value'; } @@ -399,6 +415,12 @@ export function valueTypeName(ctx: Context, typeName: string): Code | undefined return code`boolean`; case '.google.protobuf.BytesValue': return code`Uint8Array`; + case '.google.protobuf.ListValue': + return code`Array` + case '.google.protobuf.Value': + return code`any` + case '.google.protobuf.Struct': + return code`{[key: string]: any}` default: return undefined; } @@ -415,6 +437,7 @@ export function wrapperTypeName(typeName: string): string | undefined { case '.google.protobuf.UInt64Value': case '.google.protobuf.BoolValue': case '.google.protobuf.BytesValue': + case '.google.protobuf.ListValue': case '.google.protobuf.Timestamp': return typeName.split('.')[3]; default: From 05902396c6db2e10c1a68968c8620d18b8ce1b57 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Sun, 21 Nov 2021 17:06:32 +0100 Subject: [PATCH 2/8] Generate pbjs files for struct and value tests --- integration/pbjs.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/integration/pbjs.sh b/integration/pbjs.sh index 8ed0a2036..5f93eb0dd 100755 --- a/integration/pbjs.sh +++ b/integration/pbjs.sh @@ -52,3 +52,11 @@ yarn pbts --no-comments -o integration/oneof-properties/pbjs.d.ts integration/on # oneof-unions/ yarn pbjs --force-message --force-number -t static-module -o integration/oneof-unions/pbjs.js integration/oneof-unions/oneof.proto yarn pbts --no-comments -o integration/oneof-unions/pbjs.d.ts integration/oneof-unions/pbjs.js + +# struct/ +yarn pbjs --force-message --force-number -t static-module -o integration/struct/pbjs.js integration/struct/struct.proto +yarn pbts --no-comments -o integration/struct/pbjs.d.ts integration/struct/pbjs.js + +# value/ +yarn pbjs --force-message --force-number -t static-module -o integration/value/pbjs.js integration/value/value.proto +yarn pbts --no-comments -o integration/value/pbjs.d.ts integration/value/pbjs.js From 980e365925a72e4e7f47191597792856e0623a5a Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Sun, 21 Nov 2021 17:10:33 +0100 Subject: [PATCH 3/8] Include generated ts and bin files for added struct and value tests --- integration/struct/google/protobuf/struct.ts | 436 +++++++++++++ integration/struct/struct.bin | Bin 0 -> 4734 bytes integration/struct/struct.ts | 91 +++ integration/value/google/protobuf/struct.ts | 436 +++++++++++++ integration/value/google/protobuf/wrappers.ts | 578 ++++++++++++++++++ integration/value/value.bin | Bin 0 -> 9573 bytes integration/value/value.ts | 136 +++++ 7 files changed, 1677 insertions(+) create mode 100644 integration/struct/google/protobuf/struct.ts create mode 100644 integration/struct/struct.bin create mode 100644 integration/struct/struct.ts create mode 100644 integration/value/google/protobuf/struct.ts create mode 100644 integration/value/google/protobuf/wrappers.ts create mode 100644 integration/value/value.bin create mode 100644 integration/value/value.ts diff --git a/integration/struct/google/protobuf/struct.ts b/integration/struct/google/protobuf/struct.ts new file mode 100644 index 000000000..d54b6ca9b --- /dev/null +++ b/integration/struct/google/protobuf/struct.ts @@ -0,0 +1,436 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; +import * as Long from 'long'; + +export const protobufPackage = 'google.protobuf'; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +export function nullValueFromJSON(object: any): NullValue { + switch (object) { + case 0: + case 'NULL_VALUE': + return NullValue.NULL_VALUE; + case -1: + case 'UNRECOGNIZED': + default: + return NullValue.UNRECOGNIZED; + } +} + +export function nullValueToJSON(object: NullValue): string { + switch (object) { + case NullValue.NULL_VALUE: + return 'NULL_VALUE'; + default: + return 'UNKNOWN'; + } +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields: { [key: string]: any | undefined }; +} + +export interface Struct_FieldsEntry { + key: string; + value: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of that + * variants, absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue: NullValue | undefined; + /** Represents a double value. */ + numberValue: number | undefined; + /** Represents a string value. */ + stringValue: string | undefined; + /** Represents a boolean value. */ + boolValue: boolean | undefined; + /** Represents a structured value. */ + structValue: { [key: string]: any } | undefined; + /** Represents a repeated `Value`. */ + listValue: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values: any[]; +} + +const baseStruct: object = {}; + +export const Struct = { + encode(message: Struct, writer: Writer = Writer.create()): Writer { + Object.entries(message.fields).forEach(([key, value]) => { + if (value !== undefined) { + Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + } + }); + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Struct { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStruct } as Struct; + message.fields = {}; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.fields[entry1.key] = entry1.value; + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Struct { + const message = { ...baseStruct } as Struct; + message.fields = {}; + if (object.fields !== undefined && object.fields !== null) { + Object.entries(object.fields).forEach(([key, value]) => { + message.fields[key] = value as any | undefined; + }); + } + return message; + }, + + toJSON(message: Struct): unknown { + const obj: any = {}; + obj.fields = {}; + if (message.fields) { + Object.entries(message.fields).forEach(([k, v]) => { + obj.fields[k] = v; + }); + } + return obj; + }, + + fromPartial(object: DeepPartial): Struct { + const message = { ...baseStruct } as Struct; + message.fields = {}; + if (object.fields !== undefined && object.fields !== null) { + Object.entries(object.fields).forEach(([key, value]) => { + if (value !== undefined) { + message.fields[key] = value; + } + }); + } + return message; + }, +}; + +const baseStruct_FieldsEntry: object = { key: '' }; + +export const Struct_FieldsEntry = { + encode(message: Struct_FieldsEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== '') { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(wrapAnyValue(message.value), writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Struct_FieldsEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.string(); + break; + case 2: + message.value = unwrapAnyValue(Value.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Struct_FieldsEntry { + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + message.key = object.key !== undefined && object.key !== null ? String(object.key) : ''; + message.value = object.value; + return message; + }, + + toJSON(message: Struct_FieldsEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): Struct_FieldsEntry { + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + message.key = object.key ?? ''; + message.value = object.value ?? undefined; + return message; + }, +}; + +const baseValue: object = {}; + +export const Value = { + encode(message: Value, writer: Writer = Writer.create()): Writer { + if (message.nullValue !== undefined) { + writer.uint32(8).int32(message.nullValue); + } + if (message.numberValue !== undefined) { + writer.uint32(17).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(26).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(32).bool(message.boolValue); + } + if (message.structValue !== undefined) { + Struct.encode(wrapStruct(message.structValue), writer.uint32(42).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode({ values: message.listValue }, writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseValue } as Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.nullValue = reader.int32() as any; + break; + case 2: + message.numberValue = reader.double(); + break; + case 3: + message.stringValue = reader.string(); + break; + case 4: + message.boolValue = reader.bool(); + break; + case 5: + message.structValue = unwrapStruct(Struct.decode(reader, reader.uint32())); + break; + case 6: + message.listValue = ListValue.decode(reader, reader.uint32()).values; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Value { + const message = { ...baseValue } as Value; + message.nullValue = + object.nullValue !== undefined && object.nullValue !== null ? nullValueFromJSON(object.nullValue) : undefined; + message.numberValue = + object.numberValue !== undefined && object.numberValue !== null ? Number(object.numberValue) : undefined; + message.stringValue = + object.stringValue !== undefined && object.stringValue !== null ? String(object.stringValue) : undefined; + message.boolValue = + object.boolValue !== undefined && object.boolValue !== null ? Boolean(object.boolValue) : undefined; + message.structValue = typeof object.structValue === 'object' ? object.structValue : undefined; + message.listValue = Array.isArray(object?.listValue) ? [...object.listValue] : undefined; + return message; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + message.nullValue !== undefined && + (obj.nullValue = message.nullValue !== undefined ? nullValueToJSON(message.nullValue) : undefined); + message.numberValue !== undefined && (obj.numberValue = message.numberValue); + message.stringValue !== undefined && (obj.stringValue = message.stringValue); + message.boolValue !== undefined && (obj.boolValue = message.boolValue); + message.structValue !== undefined && (obj.structValue = message.structValue); + message.listValue !== undefined && (obj.listValue = message.listValue); + return obj; + }, + + fromPartial(object: DeepPartial): Value { + const message = { ...baseValue } as Value; + message.nullValue = object.nullValue ?? undefined; + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.structValue = object.structValue ?? undefined; + message.listValue = object.listValue ?? undefined; + return message; + }, +}; + +const baseListValue: object = {}; + +export const ListValue = { + encode(message: ListValue, writer: Writer = Writer.create()): Writer { + for (const v of message.values) { + Value.encode(wrapAnyValue(v!), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseListValue } as ListValue; + message.values = []; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.values.push(unwrapAnyValue(Value.decode(reader, reader.uint32()))); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ListValue { + const message = { ...baseListValue } as ListValue; + message.values = Array.isArray(object?.values) ? [...object.values] : []; + return message; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values) { + obj.values = message.values.map((e) => e); + } else { + obj.values = []; + } + return obj; + }, + + fromPartial(object: DeepPartial): ListValue { + const message = { ...baseListValue } as ListValue; + message.values = (object.values ?? []).map((e) => e); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function wrapAnyValue(value: any): Value { + if (value === null) { + return { nullValue: 0 } as Value; + } else if (typeof value === 'boolean') { + return { boolValue: value } as Value; + } else if (typeof value === 'number') { + return { numberValue: value } as Value; + } else if (typeof value === 'string') { + return { stringValue: value } as Value; + } else if (Array.isArray(value)) { + return { listValue: value } as Value; + } else if (typeof value === 'object') { + return { structValue: value } as Value; + } else if (typeof value === 'undefined') { + return {} as Value; + } else { + throw new Error('Unsupported any value type: ' + typeof value); + } +} + +function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { + if (value.stringValue !== undefined) { + return value.stringValue; + } else if (value.numberValue !== undefined) { + return value.numberValue; + } else if (value.boolValue !== undefined) { + return value.boolValue; + } else if (value.structValue !== undefined) { + return value.structValue; + } else if (value.listValue !== undefined) { + return value.listValue; + } else if (value.nullValue !== undefined) { + return null; + } +} + +function wrapStruct(object: { [key: string]: any }): Struct { + const struct = Struct.fromPartial({}); + Object.keys(object).forEach((key) => { + struct.fields[key] = object[key]; + }); + return struct; +} + +function unwrapStruct(struct: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + Object.keys(struct.fields).forEach((key) => { + object[key] = struct.fields[key]; + }); + return object; +} diff --git a/integration/struct/struct.bin b/integration/struct/struct.bin new file mode 100644 index 0000000000000000000000000000000000000000..af5a5b0a396832343032246911f0234c0605e685 GIT binary patch literal 4734 zcmbtYTW=f372XRe&?8x~nTu8WHldUF0-6fc6iDr+!IE52Yf~h%OG;G(H(HV-X=`$q z*j-9dUJCSAv_PNwP@sKmfxZ`g?XT#s=u?sJ%wBYh-G}l)nw>f4n{)Z*oY`e7Q5-JD zaeW>JaZo9h^c$;dN>%ypzg2eUFbEE(VqGqc76Z5Bm4a+rFJa(rG zv8G{edEGYcffIwF_Dpvry5Rvj*ihYgb6XgeN z#b3-uB23k3e-$Fj$!so!IC=hIDx{O)B$Ui1LiZUP1wm#;0U{;Zisll5pRqJCDM}F{ z%Nw_@SenE!g-0F}ZFo8LqBzxElDb#D>_9}y)7B&}9~w)n@Y?ey)#vP5E>W(trDKX} zmeDr_J(YTLn_bI;SYBnUJM476*)cjp^M=y+4SRPS%r5)VSWcYj6BGODFV|Df90pU@ zKdcAg;TaR-)47P&U;Dv{pR#g3`j4vqQPbP~#$U91ZOPoagaS9~yJ9+hNzwySeDV9c zY^hwhr~FD~|5{<{N?E_BlsEpd!llAEnDWNr;6Q{Cf65c6hY_E+vCBO_7UB2^1_^!; zgflm0ycx_-L+|h?<{O`W`gy9rEq`3++?YoNuBYdIG#t(*4HQESWJUC zHp+s5xFlY}h^Mfej96!k+hT$Qh2Cfpdx6hgf5I1$fH^!07U5XPnUUwa;iL&iJu6!5}_zWBeiSrqkdAJm%xTpLnDc zkrHr3{0t`1@5v>@A|4!M7L9|6;Il;(!}HjMrG)B^f@1;1G-8bVLF|o14U1mHiG+l3 zen)tE(HsO~JaxU92ocF2HUgWSi$!Jxe4H#0s@x8CX2}0#2ctX?>LeI1X2OqUh$$uO zh<|{Ukb@~A^xSE57HXLwEHZvRx4BbYfdoR6fPHr+C@={b6K?sy&emkyy*MI|P)vy? z0oXfpPx(j?QxL>}3x9%H0EJ}4EQrAX8Iu_7hIJq(KfnS5F0v5?2k{BfgbW3Pcr+Jd zVi2@EB34M;@e>wB5eQNTEe2bb!=2vdVArrsj&a}a?N}|d#T$EAGb7ef2)x@HFy67YtpVf)y_($isvhrc@@>;@ZUM$SRw2Xlv zw+05ng>^X47&?{=oYftewms|*tX}sqvb>APz;y$ zQxF9k6C+_4Qbgl()J2eyPi}2;qqPH&RwfMwI95s*C^^ICRvL5Azr<9ntaD|JMy0Y| zRnSJ6R{A&@CX)}8`*QMrS*Kw#`H`|ICzs1Q4U@@_m71JX%Q_8{$xqTrz|%09e5h2V zBrEGQOeP;GAIiy($~q1IrZcUelnWcmPt?kvbiSW7+W;#9P)S)LlxijE^P@h5xqfWkX z4+Yv&wmJHV?477n(PBOiLTX2&QyAi-jG|fO#>uK$=bI=!;#+q%pQ3GeKI73i^ybpA z93GwIwcszD+#Qa3mIGk}7oI{j82wt{^2-NQys1l}KEpfN1atAUnxha-1no6~ejFSM zs?LZ2wOOjeD5Z!&>H{@lIh&azOCzLSI3WKeXp+ssL5511)HH^lUQj@QjnXRnDJv8d zO)2Z2X}nV7L-YU?ZrDQ^fZ=b<0AnkxNUBSu`(E}0*w5a-ojvJzx_6)bQv*%Za^VYQ zTdn+E+Afq*<8+|5uf3Z+5zq&>*i(sGDC-tX zFV6~P)u*-f@pp^?>93Y= zWNX;v)y?EYK@v%SRk=M{l6~aI-~C(7J^`fPX5RWaruuVs-I0xZTSO5$Z24*Sm4rTW zy!q2X`kCa#l-`IHWl5#0173&~rA$``9nNdYC#Yuf=1?GktRWRj@3Un@NT#}|;p<4= YVv1T)eUeNUe{?TfgOkM%tC^GE0}&J%YybcN literal 0 HcmV?d00001 diff --git a/integration/struct/struct.ts b/integration/struct/struct.ts new file mode 100644 index 000000000..5edbeb70a --- /dev/null +++ b/integration/struct/struct.ts @@ -0,0 +1,91 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; +import * as Long from 'long'; +import { Struct } from './google/protobuf/struct'; + +export const protobufPackage = ''; + +export interface StructMessage { + value: { [key: string]: any } | undefined; +} + +const baseStructMessage: object = {}; + +export const StructMessage = { + encode(message: StructMessage, writer: Writer = Writer.create()): Writer { + if (message.value !== undefined) { + Struct.encode(wrapStruct(message.value), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): StructMessage { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStructMessage } as StructMessage; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = unwrapStruct(Struct.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): StructMessage { + const message = { ...baseStructMessage } as StructMessage; + message.value = typeof object.value === 'object' ? object.value : undefined; + return message; + }, + + toJSON(message: StructMessage): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): StructMessage { + const message = { ...baseStructMessage } as StructMessage; + message.value = object.value ?? undefined; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function wrapStruct(object: { [key: string]: any }): Struct { + const struct = Struct.fromPartial({}); + Object.keys(object).forEach((key) => { + struct.fields[key] = object[key]; + }); + return struct; +} + +function unwrapStruct(struct: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + Object.keys(struct.fields).forEach((key) => { + object[key] = struct.fields[key]; + }); + return object; +} diff --git a/integration/value/google/protobuf/struct.ts b/integration/value/google/protobuf/struct.ts new file mode 100644 index 000000000..d54b6ca9b --- /dev/null +++ b/integration/value/google/protobuf/struct.ts @@ -0,0 +1,436 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; +import * as Long from 'long'; + +export const protobufPackage = 'google.protobuf'; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +export function nullValueFromJSON(object: any): NullValue { + switch (object) { + case 0: + case 'NULL_VALUE': + return NullValue.NULL_VALUE; + case -1: + case 'UNRECOGNIZED': + default: + return NullValue.UNRECOGNIZED; + } +} + +export function nullValueToJSON(object: NullValue): string { + switch (object) { + case NullValue.NULL_VALUE: + return 'NULL_VALUE'; + default: + return 'UNKNOWN'; + } +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields: { [key: string]: any | undefined }; +} + +export interface Struct_FieldsEntry { + key: string; + value: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of that + * variants, absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue: NullValue | undefined; + /** Represents a double value. */ + numberValue: number | undefined; + /** Represents a string value. */ + stringValue: string | undefined; + /** Represents a boolean value. */ + boolValue: boolean | undefined; + /** Represents a structured value. */ + structValue: { [key: string]: any } | undefined; + /** Represents a repeated `Value`. */ + listValue: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values: any[]; +} + +const baseStruct: object = {}; + +export const Struct = { + encode(message: Struct, writer: Writer = Writer.create()): Writer { + Object.entries(message.fields).forEach(([key, value]) => { + if (value !== undefined) { + Struct_FieldsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim(); + } + }); + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Struct { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStruct } as Struct; + message.fields = {}; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + const entry1 = Struct_FieldsEntry.decode(reader, reader.uint32()); + if (entry1.value !== undefined) { + message.fields[entry1.key] = entry1.value; + } + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Struct { + const message = { ...baseStruct } as Struct; + message.fields = {}; + if (object.fields !== undefined && object.fields !== null) { + Object.entries(object.fields).forEach(([key, value]) => { + message.fields[key] = value as any | undefined; + }); + } + return message; + }, + + toJSON(message: Struct): unknown { + const obj: any = {}; + obj.fields = {}; + if (message.fields) { + Object.entries(message.fields).forEach(([k, v]) => { + obj.fields[k] = v; + }); + } + return obj; + }, + + fromPartial(object: DeepPartial): Struct { + const message = { ...baseStruct } as Struct; + message.fields = {}; + if (object.fields !== undefined && object.fields !== null) { + Object.entries(object.fields).forEach(([key, value]) => { + if (value !== undefined) { + message.fields[key] = value; + } + }); + } + return message; + }, +}; + +const baseStruct_FieldsEntry: object = { key: '' }; + +export const Struct_FieldsEntry = { + encode(message: Struct_FieldsEntry, writer: Writer = Writer.create()): Writer { + if (message.key !== '') { + writer.uint32(10).string(message.key); + } + if (message.value !== undefined) { + Value.encode(wrapAnyValue(message.value), writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Struct_FieldsEntry { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.key = reader.string(); + break; + case 2: + message.value = unwrapAnyValue(Value.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Struct_FieldsEntry { + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + message.key = object.key !== undefined && object.key !== null ? String(object.key) : ''; + message.value = object.value; + return message; + }, + + toJSON(message: Struct_FieldsEntry): unknown { + const obj: any = {}; + message.key !== undefined && (obj.key = message.key); + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): Struct_FieldsEntry { + const message = { ...baseStruct_FieldsEntry } as Struct_FieldsEntry; + message.key = object.key ?? ''; + message.value = object.value ?? undefined; + return message; + }, +}; + +const baseValue: object = {}; + +export const Value = { + encode(message: Value, writer: Writer = Writer.create()): Writer { + if (message.nullValue !== undefined) { + writer.uint32(8).int32(message.nullValue); + } + if (message.numberValue !== undefined) { + writer.uint32(17).double(message.numberValue); + } + if (message.stringValue !== undefined) { + writer.uint32(26).string(message.stringValue); + } + if (message.boolValue !== undefined) { + writer.uint32(32).bool(message.boolValue); + } + if (message.structValue !== undefined) { + Struct.encode(wrapStruct(message.structValue), writer.uint32(42).fork()).ldelim(); + } + if (message.listValue !== undefined) { + ListValue.encode({ values: message.listValue }, writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseValue } as Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.nullValue = reader.int32() as any; + break; + case 2: + message.numberValue = reader.double(); + break; + case 3: + message.stringValue = reader.string(); + break; + case 4: + message.boolValue = reader.bool(); + break; + case 5: + message.structValue = unwrapStruct(Struct.decode(reader, reader.uint32())); + break; + case 6: + message.listValue = ListValue.decode(reader, reader.uint32()).values; + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Value { + const message = { ...baseValue } as Value; + message.nullValue = + object.nullValue !== undefined && object.nullValue !== null ? nullValueFromJSON(object.nullValue) : undefined; + message.numberValue = + object.numberValue !== undefined && object.numberValue !== null ? Number(object.numberValue) : undefined; + message.stringValue = + object.stringValue !== undefined && object.stringValue !== null ? String(object.stringValue) : undefined; + message.boolValue = + object.boolValue !== undefined && object.boolValue !== null ? Boolean(object.boolValue) : undefined; + message.structValue = typeof object.structValue === 'object' ? object.structValue : undefined; + message.listValue = Array.isArray(object?.listValue) ? [...object.listValue] : undefined; + return message; + }, + + toJSON(message: Value): unknown { + const obj: any = {}; + message.nullValue !== undefined && + (obj.nullValue = message.nullValue !== undefined ? nullValueToJSON(message.nullValue) : undefined); + message.numberValue !== undefined && (obj.numberValue = message.numberValue); + message.stringValue !== undefined && (obj.stringValue = message.stringValue); + message.boolValue !== undefined && (obj.boolValue = message.boolValue); + message.structValue !== undefined && (obj.structValue = message.structValue); + message.listValue !== undefined && (obj.listValue = message.listValue); + return obj; + }, + + fromPartial(object: DeepPartial): Value { + const message = { ...baseValue } as Value; + message.nullValue = object.nullValue ?? undefined; + message.numberValue = object.numberValue ?? undefined; + message.stringValue = object.stringValue ?? undefined; + message.boolValue = object.boolValue ?? undefined; + message.structValue = object.structValue ?? undefined; + message.listValue = object.listValue ?? undefined; + return message; + }, +}; + +const baseListValue: object = {}; + +export const ListValue = { + encode(message: ListValue, writer: Writer = Writer.create()): Writer { + for (const v of message.values) { + Value.encode(wrapAnyValue(v!), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ListValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseListValue } as ListValue; + message.values = []; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.values.push(unwrapAnyValue(Value.decode(reader, reader.uint32()))); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ListValue { + const message = { ...baseListValue } as ListValue; + message.values = Array.isArray(object?.values) ? [...object.values] : []; + return message; + }, + + toJSON(message: ListValue): unknown { + const obj: any = {}; + if (message.values) { + obj.values = message.values.map((e) => e); + } else { + obj.values = []; + } + return obj; + }, + + fromPartial(object: DeepPartial): ListValue { + const message = { ...baseListValue } as ListValue; + message.values = (object.values ?? []).map((e) => e); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function wrapAnyValue(value: any): Value { + if (value === null) { + return { nullValue: 0 } as Value; + } else if (typeof value === 'boolean') { + return { boolValue: value } as Value; + } else if (typeof value === 'number') { + return { numberValue: value } as Value; + } else if (typeof value === 'string') { + return { stringValue: value } as Value; + } else if (Array.isArray(value)) { + return { listValue: value } as Value; + } else if (typeof value === 'object') { + return { structValue: value } as Value; + } else if (typeof value === 'undefined') { + return {} as Value; + } else { + throw new Error('Unsupported any value type: ' + typeof value); + } +} + +function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { + if (value.stringValue !== undefined) { + return value.stringValue; + } else if (value.numberValue !== undefined) { + return value.numberValue; + } else if (value.boolValue !== undefined) { + return value.boolValue; + } else if (value.structValue !== undefined) { + return value.structValue; + } else if (value.listValue !== undefined) { + return value.listValue; + } else if (value.nullValue !== undefined) { + return null; + } +} + +function wrapStruct(object: { [key: string]: any }): Struct { + const struct = Struct.fromPartial({}); + Object.keys(object).forEach((key) => { + struct.fields[key] = object[key]; + }); + return struct; +} + +function unwrapStruct(struct: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + Object.keys(struct.fields).forEach((key) => { + object[key] = struct.fields[key]; + }); + return object; +} diff --git a/integration/value/google/protobuf/wrappers.ts b/integration/value/google/protobuf/wrappers.ts new file mode 100644 index 000000000..216285744 --- /dev/null +++ b/integration/value/google/protobuf/wrappers.ts @@ -0,0 +1,578 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; +import * as Long from 'long'; + +export const protobufPackage = 'google.protobuf'; + +/** + * Wrapper message for `double`. + * + * The JSON representation for `DoubleValue` is JSON number. + */ +export interface DoubleValue { + /** The double value. */ + value: number; +} + +/** + * Wrapper message for `float`. + * + * The JSON representation for `FloatValue` is JSON number. + */ +export interface FloatValue { + /** The float value. */ + value: number; +} + +/** + * Wrapper message for `int64`. + * + * The JSON representation for `Int64Value` is JSON string. + */ +export interface Int64Value { + /** The int64 value. */ + value: number; +} + +/** + * Wrapper message for `uint64`. + * + * The JSON representation for `UInt64Value` is JSON string. + */ +export interface UInt64Value { + /** The uint64 value. */ + value: number; +} + +/** + * Wrapper message for `int32`. + * + * The JSON representation for `Int32Value` is JSON number. + */ +export interface Int32Value { + /** The int32 value. */ + value: number; +} + +/** + * Wrapper message for `uint32`. + * + * The JSON representation for `UInt32Value` is JSON number. + */ +export interface UInt32Value { + /** The uint32 value. */ + value: number; +} + +/** + * Wrapper message for `bool`. + * + * The JSON representation for `BoolValue` is JSON `true` and `false`. + */ +export interface BoolValue { + /** The bool value. */ + value: boolean; +} + +/** + * Wrapper message for `string`. + * + * The JSON representation for `StringValue` is JSON string. + */ +export interface StringValue { + /** The string value. */ + value: string; +} + +/** + * Wrapper message for `bytes`. + * + * The JSON representation for `BytesValue` is JSON string. + */ +export interface BytesValue { + /** The bytes value. */ + value: Uint8Array; +} + +const baseDoubleValue: object = { value: 0 }; + +export const DoubleValue = { + encode(message: DoubleValue, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(9).double(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): DoubleValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseDoubleValue } as DoubleValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.double(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): DoubleValue { + const message = { ...baseDoubleValue } as DoubleValue; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: DoubleValue): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): DoubleValue { + const message = { ...baseDoubleValue } as DoubleValue; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseFloatValue: object = { value: 0 }; + +export const FloatValue = { + encode(message: FloatValue, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(13).float(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): FloatValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseFloatValue } as FloatValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.float(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): FloatValue { + const message = { ...baseFloatValue } as FloatValue; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: FloatValue): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): FloatValue { + const message = { ...baseFloatValue } as FloatValue; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseInt64Value: object = { value: 0 }; + +export const Int64Value = { + encode(message: Int64Value, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(8).int64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Int64Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseInt64Value } as Int64Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = longToNumber(reader.int64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Int64Value { + const message = { ...baseInt64Value } as Int64Value; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: Int64Value): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): Int64Value { + const message = { ...baseInt64Value } as Int64Value; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseUInt64Value: object = { value: 0 }; + +export const UInt64Value = { + encode(message: UInt64Value, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(8).uint64(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): UInt64Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseUInt64Value } as UInt64Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = longToNumber(reader.uint64() as Long); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): UInt64Value { + const message = { ...baseUInt64Value } as UInt64Value; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: UInt64Value): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): UInt64Value { + const message = { ...baseUInt64Value } as UInt64Value; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseInt32Value: object = { value: 0 }; + +export const Int32Value = { + encode(message: Int32Value, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(8).int32(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Int32Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseInt32Value } as Int32Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Int32Value { + const message = { ...baseInt32Value } as Int32Value; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: Int32Value): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): Int32Value { + const message = { ...baseInt32Value } as Int32Value; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseUInt32Value: object = { value: 0 }; + +export const UInt32Value = { + encode(message: UInt32Value, writer: Writer = Writer.create()): Writer { + if (message.value !== 0) { + writer.uint32(8).uint32(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): UInt32Value { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseUInt32Value } as UInt32Value; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.uint32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): UInt32Value { + const message = { ...baseUInt32Value } as UInt32Value; + message.value = object.value !== undefined && object.value !== null ? Number(object.value) : 0; + return message; + }, + + toJSON(message: UInt32Value): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): UInt32Value { + const message = { ...baseUInt32Value } as UInt32Value; + message.value = object.value ?? 0; + return message; + }, +}; + +const baseBoolValue: object = { value: false }; + +export const BoolValue = { + encode(message: BoolValue, writer: Writer = Writer.create()): Writer { + if (message.value === true) { + writer.uint32(8).bool(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): BoolValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseBoolValue } as BoolValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.bool(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): BoolValue { + const message = { ...baseBoolValue } as BoolValue; + message.value = object.value !== undefined && object.value !== null ? Boolean(object.value) : false; + return message; + }, + + toJSON(message: BoolValue): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): BoolValue { + const message = { ...baseBoolValue } as BoolValue; + message.value = object.value ?? false; + return message; + }, +}; + +const baseStringValue: object = { value: '' }; + +export const StringValue = { + encode(message: StringValue, writer: Writer = Writer.create()): Writer { + if (message.value !== '') { + writer.uint32(10).string(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): StringValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseStringValue } as StringValue; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.string(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): StringValue { + const message = { ...baseStringValue } as StringValue; + message.value = object.value !== undefined && object.value !== null ? String(object.value) : ''; + return message; + }, + + toJSON(message: StringValue): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + return obj; + }, + + fromPartial(object: DeepPartial): StringValue { + const message = { ...baseStringValue } as StringValue; + message.value = object.value ?? ''; + return message; + }, +}; + +const baseBytesValue: object = {}; + +export const BytesValue = { + encode(message: BytesValue, writer: Writer = Writer.create()): Writer { + if (message.value.length !== 0) { + writer.uint32(10).bytes(message.value); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): BytesValue { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseBytesValue } as BytesValue; + message.value = new Uint8Array(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): BytesValue { + const message = { ...baseBytesValue } as BytesValue; + message.value = + object.value !== undefined && object.value !== null ? bytesFromBase64(object.value) : new Uint8Array(); + return message; + }, + + toJSON(message: BytesValue): unknown { + const obj: any = {}; + message.value !== undefined && + (obj.value = base64FromBytes(message.value !== undefined ? message.value : new Uint8Array())); + return obj; + }, + + fromPartial(object: DeepPartial): BytesValue { + const message = { ...baseBytesValue } as BytesValue; + message.value = object.value ?? new Uint8Array(); + return message; + }, +}; + +declare var self: any | undefined; +declare var window: any | undefined; +declare var global: any | undefined; +var globalThis: any = (() => { + if (typeof globalThis !== 'undefined') return globalThis; + if (typeof self !== 'undefined') return self; + if (typeof window !== 'undefined') return window; + if (typeof global !== 'undefined') return global; + throw 'Unable to locate global object'; +})(); + +const atob: (b64: string) => string = + globalThis.atob || ((b64) => globalThis.Buffer.from(b64, 'base64').toString('binary')); +function bytesFromBase64(b64: string): Uint8Array { + const bin = atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; +} + +const btoa: (bin: string) => string = + globalThis.btoa || ((bin) => globalThis.Buffer.from(bin, 'binary').toString('base64')); +function base64FromBytes(arr: Uint8Array): string { + const bin: string[] = []; + for (const byte of arr) { + bin.push(String.fromCharCode(byte)); + } + return btoa(bin.join('')); +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error('Value is larger than Number.MAX_SAFE_INTEGER'); + } + return long.toNumber(); +} + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} diff --git a/integration/value/value.bin b/integration/value/value.bin new file mode 100644 index 0000000000000000000000000000000000000000..4f4868c81ba4bc4633509427088832fae5610f2f GIT binary patch literal 9573 zcmeHNO>Y~=8D1_)i5kg{y>=6U{+h^1EE}}s$ZirlEsQ0#lGdh3nIBS>z_nK7N@7gy z%DYP|%%wnk?GI>yo_Z+IqNf(0$B|&9*QP8icE%|3`a%kDOz|t?> zJ{YBDCu%e_3M?xyPwQBlTzK~l=IbO;O%grQKTFO0NoQHH2xQFbb-OPl17do9A^!%0 z3P=R~idLH-db#-~oAP@5+isw?)A=coOv=@S2zu&xyNW1Hz==q%N}?N#ZTo%KV=VKs z|A3NR()tXROjJmtU-#<)(E(EB=gKYWe#|WhqWd zVb;hK*HT~1E1%i*yNnE z?Y8F_-;1Jy@X`Ey+kNeJ{R12nD+79wQUb(XASV#>jPaV=hJk|4b}#Dq9(TMp?}aYl z@X+rCEmy2;cRVNP3tVA=A9kWW?g!%keh)bJ{dQ-k({hMIfjfcAaq@mAirhBG9e>?v zV-W2*5&lEcb-Vr{{Ft|Vuic?dA#DO5H+lp}^zY6QU?KN+Vni*!?ehIz7(w#L0aD`X zZ2PZWa8w#&-1DPO%PrtzC*g~H;Bvec$nTP&;dv6C2k z&b~{Ek$R~8Mgf7?_ak_K&`AXE z20Az=-@yk4zOY8CIl27#KQx(MIMA*9dfLIe8-bs6ozn+Ik-h*#jf*ub*Zso z)+~cy)=dTD8&&*qBATY&SN=flGFyLWeC?YmA$G&8#)-QnPH<_*%2JR;^o{BwMuW zrLt+ST8ncyA7^rF-KsQreZ?%7M+IfPx>2!egnn2|UbKMHES4=g3P*2(Y0WA%NT9)} z1myu>xxjdR%_`X#K_3>>VAeJZifP??)&vzk@Scjo2Qd=e|qz>mOHHOJ> zAbbUe9>~cn)^gciwkjoy@KlM-hF!OA!(cYBEfY^t^M;8-;V3wmJQBEIA{r05E|d&= za(juJi|b&r7*oRo>b7zhY^pa)D@x~Z{~Sx{Mw)BWG|Cz284Y2S>A4T(P%hroZivMj zMw*6l@dIs1EKV9}8p_2FwSrho8EG2I#gEh?*wauh-qL0SB{R}Al#8?4`(p6}BTd6U zrC1By-_-T{Xr%pj5I1$YcZ$P;nDKo3iZ=a~Wm9wM3E zF@Ct$Y3<>iIiUBY-N!qH0MqUEIYsC;mpKD22*oKpVHC-G&aR8_6d#UwB4Q`rsjzo& z;0F|rw)+6$;Tc7+NWh6#b&fCL>2bey_7A!U8y%1FuoZL;1h9lXBFRgaKdC3;aJ**; zBOKsBQf&3NzjBe$@&-NL6s7P!Lpl)z6LR&M;~{Ljbk;WZNB*u$uQN12VV2%uJf+Zq zl7WJ-SdAfxrx8?7>Tv!etcf;O2LTl{3L3+|bVh>%Eac9!hb)uPbj?UV(0RVVn}`8; zxPgZ(0N^KDfUzl-rLAMM_i{V~+Xp|m63^0@UBAKpro&BAM&`%bYAXL1oo}sE=6A$P zPB=+9M9V=0cDamT2=Yjwk#XSvls(gkav5)zMwIiq4kaW|ORE=zWOM0*(tQdPuN;;n z2eD6?DtIA8LUOT6%5%R)lp-G@W4K5sJP|p`qzqJu!Eke&whezrGJvg)*N;EIpW2im zx#S+)4FVrY2HvUD45zdc+f4w$CNf(J`u%b49GijJQaRkb$9g%PzbyDsj-3DpJ=q5| z*zq_w#ct6KGPIewmd_*08oHpQO(}1@O_?`?P8BTQ3SqE*OrlBG2Ho?@B;(^CG5Vu~lvdXuTN@e!svm|6)_9Zao+sSc(V znBEacFQdiOncS8&Z3X&X@aDA%DkFSehs zhn~TGu@kts6=^$xn*n1Ra3y0PcnVt`VT_yZ!kyajB}y!o1>>iQ#j;@hG_hC~jGw|{ zzfUne3$Lkb>#6*2QhX~ZskX?|af@(=x^9Gmz{S;a1NltpiJv7F;}P#P z?|CYDJKfVqj~B1h7V+Ma?w%#Tt7p%%Jj-OoE7jC5=Ff53Hf%5Y2Fj*BMHZqN{HOWO z1@?2uo6(KTmnh8gulZI|ekySf1LpuG3#t&mhs#3`hSIJGN6Bppf#-BdMmY;foFH)e zQjiR}<(IjO>=9f>r(5`3znZ^GE+dVZ9CyMiNC3!xuowRJTu){&`t!*P@i(0E{Gz;3 zpoGz%=dWy!i8ylqn}6P9*JTAfD0`Ix!joDsH`(N(xXI#8GA_rB66$lLR6}HDhOwot z@2FP62#r2OV|&pj502rJj%jdM^CY57OoYQgbARC&M%kD~F*=s$Tug*SrkHd`MnLCc z67Z>uuyV{{6EP7075jMKkD-}TG{s+}j&D$$+mH?zuBjHq$I?J3-YLwV+@gr>_Pv@E zf4@OZidpTO6#Lf&(WIDt+oX8gr1)Q(C2yM)`<=HUqey6| z3vYV;Etzmq|C*}agRawZ(X~88DUCiO8lA4J+^8c8uDwpUhe9$sbkS#|B73-O8yty- zrD!+Eyo{Km7m8|q$LV#WB=w|}5Q4G6@EVmjx6hu@KP0v zN)`Ej3+-YmQe)QC?M1apWo%3>doyxL?xS+t=?1RT?jMDUOclK%B;a?Fo>gJcv!bDF zCEXqLtY|0~=d_i$XGKG~I6vrF(NHel)8+&vdR8=)iyseqRy3503xl2&4R>YB>Qn7e zD*t8dwlJA+QQ<>dvUsH;;X)IZv93#%GE1$U{a100jhirkn>e?0%-42Q#SunR1?tk=f9`I8FWc z7rvAFRe?2tKjQn;+t^U*&k+30VfEi%7(ovLpx;#L#|)b{mHIIQ`c0+&tdV)4ZJ(#3@p}bJ)&k}xTMEyqu00jhal=?BlZbzvfGYH^7{XYB$HQ289RVv>){_avg z>TK;NrRzZXTWGVRsY=0WYX^T9bcqW!4aWdw7tM$3xW!{6NC#b%i{cj3VIy|HcZku^ z$HcY840}7{qOU@GAw~y%6}krFM#k5I6YU^x;@N@>71#q&My~NVAo$7-;{^XzVh2e# zA^<2Lz$;}3%&_~FvIAxi;FY=tIU{q3TKJ?lbeoF$v+)mV{}bTn0N_wvgB-!{C)Yq6 zF@hchK;Ku_05fdvt80K6(DyG~OgcK>rrF4kZBG4tP5zAL&&(fFY)UjSR%I+4{49pL zJ!kza#(2QS(f6i4nEDybDZdY?#iYh(Y%63rs*Fi=`(1**fsDF&VueO2rxIU{srLb*WkN@sLVRQT{e%vGeLPDMG@sDn)ZAgrA`p4>%j%Hp? cHS;uG2b0_+kH3LQ?)^eM1C!kRz1Z~s1>^ZEQvd(} literal 0 HcmV?d00001 diff --git a/integration/value/value.ts b/integration/value/value.ts new file mode 100644 index 000000000..7e15efe5b --- /dev/null +++ b/integration/value/value.ts @@ -0,0 +1,136 @@ +/* eslint-disable */ +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; +import * as Long from 'long'; +import { Value, ListValue } from './google/protobuf/struct'; + +export const protobufPackage = ''; + +export interface ValueMessage { + value: any | undefined; + anyList: Array | undefined; + repeatedAny: any[]; +} + +const baseValueMessage: object = {}; + +export const ValueMessage = { + encode(message: ValueMessage, writer: Writer = Writer.create()): Writer { + if (message.value !== undefined) { + Value.encode(wrapAnyValue(message.value), writer.uint32(10).fork()).ldelim(); + } + if (message.anyList !== undefined) { + ListValue.encode({ values: message.anyList }, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.repeatedAny) { + Value.encode(wrapAnyValue(v!), writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): ValueMessage { + const reader = input instanceof Reader ? input : new Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseValueMessage } as ValueMessage; + message.repeatedAny = []; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.value = unwrapAnyValue(Value.decode(reader, reader.uint32())); + break; + case 2: + message.anyList = ListValue.decode(reader, reader.uint32()).values; + break; + case 3: + message.repeatedAny.push(unwrapAnyValue(Value.decode(reader, reader.uint32()))); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): ValueMessage { + const message = { ...baseValueMessage } as ValueMessage; + message.value = object.value; + message.anyList = Array.isArray(object?.anyList) ? [...object.anyList] : undefined; + message.repeatedAny = Array.isArray(object?.repeatedAny) ? [...object.repeatedAny] : []; + return message; + }, + + toJSON(message: ValueMessage): unknown { + const obj: any = {}; + message.value !== undefined && (obj.value = message.value); + message.anyList !== undefined && (obj.anyList = message.anyList); + if (message.repeatedAny) { + obj.repeatedAny = message.repeatedAny.map((e) => e); + } else { + obj.repeatedAny = []; + } + return obj; + }, + + fromPartial(object: DeepPartial): ValueMessage { + const message = { ...baseValueMessage } as ValueMessage; + message.value = object.value ?? undefined; + message.anyList = object.anyList ?? undefined; + message.repeatedAny = (object.repeatedAny ?? []).map((e) => e); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +// If you get a compile-error about 'Constructor and ... have no overlap', +// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'. +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} + +function wrapAnyValue(value: any): Value { + if (value === null) { + return { nullValue: 0 } as Value; + } else if (typeof value === 'boolean') { + return { boolValue: value } as Value; + } else if (typeof value === 'number') { + return { numberValue: value } as Value; + } else if (typeof value === 'string') { + return { stringValue: value } as Value; + } else if (Array.isArray(value)) { + return { listValue: value } as Value; + } else if (typeof value === 'object') { + return { structValue: value } as Value; + } else if (typeof value === 'undefined') { + return {} as Value; + } else { + throw new Error('Unsupported any value type: ' + typeof value); + } +} + +function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { + if (value.stringValue !== undefined) { + return value.stringValue; + } else if (value.numberValue !== undefined) { + return value.numberValue; + } else if (value.boolValue !== undefined) { + return value.boolValue; + } else if (value.structValue !== undefined) { + return value.structValue; + } else if (value.listValue !== undefined) { + return value.listValue; + } else if (value.nullValue !== undefined) { + return null; + } +} From ba3281d7524997d6032ea643b674419d726a5969 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Sun, 21 Nov 2021 17:39:16 +0100 Subject: [PATCH 4/8] Update inline snapshot for value tests --- integration/value/value-test.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/integration/value/value-test.ts b/integration/value/value-test.ts index 921b3258e..deb4a73f3 100644 --- a/integration/value/value-test.ts +++ b/integration/value/value-test.ts @@ -1,15 +1,14 @@ import { Reader } from 'protobufjs'; -import {ValueMessage} from "./value"; +import { ValueMessage } from './value'; import { ValueMessage as PbValueMessage } from './pbjs'; - describe('values', () => { it('json value', () => { const s1 = ValueMessage.fromJSON({ - value: "Hello", - anyList: [1, "foo", true], - repeatedAny: [2, "bar", false], + value: 'Hello', + anyList: [1, 'foo', true], + repeatedAny: [2, 'bar', false], }); const s2 = PbValueMessage.decode(Reader.create(ValueMessage.encode(s1).finish())); @@ -29,6 +28,17 @@ describe('values', () => { }, ], }, + "repeatedAny": Array [ + Object { + "numberValue": 2, + }, + Object { + "stringValue": "bar", + }, + Object { + "boolValue": false, + }, + ], "value": Object { "stringValue": "Hello", }, @@ -38,17 +48,17 @@ describe('values', () => { it('decodes lists of any types correctly', () => { const s1 = ValueMessage.fromJSON({ - anyList: [1, "foo", true], - repeatedAny: [2, "bar", false], + anyList: [1, 'foo', true], + repeatedAny: [2, 'bar', false], }); - expect(s1).toEqual({anyList: [1, "foo", true], repeatedAny: [2, "bar", false]}); - }) + expect(s1).toEqual({ anyList: [1, 'foo', true], repeatedAny: [2, 'bar', false] }); + }); it('toJson', () => { const s1 = ValueMessage.fromPartial({ anyList: [1], repeatedAny: [2], }); - expect(s1).toEqual({anyList: [1], repeatedAny: [2]}); - }) + expect(s1).toEqual({ anyList: [1], repeatedAny: [2] }); + }); }); From 389c6db842bf5f641f7f66736abea87c0499320b Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Sun, 21 Nov 2021 19:02:51 +0100 Subject: [PATCH 5/8] Run prettier --- src/main.ts | 44 +++++++++++++++++++++++++++++--------------- src/types.ts | 10 +++++----- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/main.ts b/src/main.ts index 537fb4e1d..bc8251458 100644 --- a/src/main.ts +++ b/src/main.ts @@ -349,7 +349,9 @@ function makeLongUtils(options: Options, bytes: ReturnType } function makeWrappingUtils() { - const wrapAnyValue = conditionalOutput('wrapAnyValue', code`function wrapAnyValue(value: any): Value { + const wrapAnyValue = conditionalOutput( + 'wrapAnyValue', + code`function wrapAnyValue(value: any): Value { if (value === null) { return {nullValue: 0} as Value; } else if (typeof value === 'boolean') { @@ -367,9 +369,12 @@ function makeWrappingUtils() { } else { throw new Error('Unsupported any value type: ' + typeof value); } - }`); + }` + ); - const unwrapAnyValue = conditionalOutput('unwrapAnyValue', code`function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { + const unwrapAnyValue = conditionalOutput( + 'unwrapAnyValue', + code`function unwrapAnyValue(value: Value): string | number | boolean | Object | null | Array | undefined { if (value.stringValue !== undefined) { return value.stringValue; } else if (value.numberValue !== undefined) { @@ -383,23 +388,30 @@ function makeWrappingUtils() { } else if (value.nullValue !== undefined) { return null; } - }`) + }` + ); - const wrapStruct = conditionalOutput('wrapStruct', code`function wrapStruct(object: {[key: string]: any}): Struct { + const wrapStruct = conditionalOutput( + 'wrapStruct', + code`function wrapStruct(object: {[key: string]: any}): Struct { const struct = Struct.fromPartial({}); Object.keys(object).forEach(key => { struct.fields[key] = object[key]; }); return struct; - }`) + }` + ); - const unwrapStruct = conditionalOutput('unwrapStruct', code`function unwrapStruct(struct: Struct): {[key: string]: any} { + const unwrapStruct = conditionalOutput( + 'unwrapStruct', + code`function unwrapStruct(struct: Struct): {[key: string]: any} { const object: { [key: string]: any } = {}; Object.keys(struct.fields).forEach(key => { object[key] = struct.fields[key]; }); return object; - }`) + }` + ); return { wrapAnyValue, unwrapAnyValue, wrapStruct, unwrapStruct }; } @@ -759,9 +771,9 @@ function generateDecode(ctx: Context, fullName: string, messageDesc: DescriptorP if (isStructType(field)) return code`${ctx.utils.unwrapStruct}(${decodedValue})`; if (isListValueType(field)) return code`${decodedValue}.values`; return code`${decodedValue}.value`; - } + }; const type = basicTypeName(ctx, field, { keepValueType: true }); - const decoder = code`${type}.decode(reader, reader.uint32())` + const decoder = code`${type}.decode(reader, reader.uint32())`; readSnippet = code`${unwrap(decoder)}`; } else if (isTimestamp(field) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING)) { const type = basicTypeName(ctx, field, { keepValueType: true }); @@ -864,7 +876,7 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP if (isListValueType(field)) return code`{values: ${place}}`; if (isStructType(field)) return code`${ctx.utils.wrapStruct}(${place})`; return code`{${maybeTypeField} value: ${place}!}`; - } + }; const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); @@ -1007,7 +1019,7 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto (options.useDate === DateOption.DATE || options.useDate === DateOption.TIMESTAMP) ) { return code`${utils.fromJsonTimestamp}(${from})`; - } else if (isAnyValueType(field) || isStructType(field)) { + } else if (isAnyValueType(field) || isStructType(field)) { return code`${from}`; } else if (isValueType(ctx, field)) { const valueType = valueTypeName(ctx, field.typeName)!; @@ -1094,7 +1106,9 @@ function generateFromJson(ctx: Context, fullName: string, messageDesc: Descripto } else if (isAnyValueType(field)) { chunks.push(code`message.${fieldName} = object.${fieldName};`); } else if (isStructType(field)) { - chunks.push(code`message.${fieldName} = typeof(object.${fieldName}) === 'object' ? object.${fieldName} : undefined;`); + chunks.push( + code`message.${fieldName} = typeof(object.${fieldName}) === 'object' ? object.${fieldName} : undefined;` + ); } else if (isListValueType(field)) { chunks.push(code` message.${fieldName} = Array.isArray(object?.${fieldName}) @@ -1252,7 +1266,7 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return code`${cstr}(${from})`; } } else if (isAnyValueType(valueType)) { - return code`${from}` + return code`${from}`; } else if ( isTimestamp(valueType) && (options.useDate === DateOption.DATE || options.useDate === DateOption.STRING) @@ -1265,7 +1279,7 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return code`${type}.fromPartial(${from})`; } } else if (isAnyValueType(field)) { - return code`${from}` + return code`${from}`; } else { const type = basicTypeName(ctx, field); return code`${type}.fromPartial(${from})`; diff --git a/src/types.ts b/src/types.ts index 7d1c64496..450c0f804 100644 --- a/src/types.ts +++ b/src/types.ts @@ -371,7 +371,7 @@ export function isValueType(ctx: Context, field: FieldDescriptorProto): boolean } export function isAnyValueType(field: FieldDescriptorProto): boolean { - return field.typeName === '.google.protobuf.Value' + return field.typeName === '.google.protobuf.Value'; } export function isBytesValueType(field: FieldDescriptorProto): boolean { @@ -379,7 +379,7 @@ export function isBytesValueType(field: FieldDescriptorProto): boolean { } export function isListValueType(field: FieldDescriptorProto): boolean { - return isListValueTypeName(field.typeName) + return isListValueTypeName(field.typeName); } export function isListValueTypeName(typeName: string): boolean { @@ -416,11 +416,11 @@ export function valueTypeName(ctx: Context, typeName: string): Code | undefined case '.google.protobuf.BytesValue': return code`Uint8Array`; case '.google.protobuf.ListValue': - return code`Array` + return code`Array`; case '.google.protobuf.Value': - return code`any` + return code`any`; case '.google.protobuf.Struct': - return code`{[key: string]: any}` + return code`{[key: string]: any}`; default: return undefined; } From c221208e4d311ce8025bbdbb4eed23d13b51d59d Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Mon, 22 Nov 2021 00:48:22 +0100 Subject: [PATCH 6/8] Update README description of well-known types, wrapper types, json types and default values --- README.markdown | 207 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 185 insertions(+), 22 deletions(-) diff --git a/README.markdown b/README.markdown index 1a9f0b58d..39546da53 100644 --- a/README.markdown +++ b/README.markdown @@ -22,10 +22,13 @@ - [Assumptions](#assumptions) - [Todo](#todo) - [OneOf Handling](#oneof-handling) -- [Primitive Types](#primitive-types) +- [Default Values and Unset Fields](#default-values-and-unset-fields) +- [Well-Known Types](#well-known-types) + - [Wrapper Types](#wrapper-types) + - [JSON Types (Struct Types)](#json-types-struct-types) + - [Timestamps](#timestamp) - [Wrapper Types](#wrapper-types) - [Number Types](#number-types) -- [Timestamps](#timestamps) - [Current Status of Optional Values](#current-status-of-optional-values) # Overview @@ -399,29 +402,197 @@ As this will automatically enforce only one of `field_a` or `field_b` "being set In ts-proto's currently-unscheduled 2.x release, `oneof=unions` will become the default behavior. -# Primitive Types +# Default values and unset fields -Protobuf has the somewhat annoying behavior that primitives types cannot differentiate between set-to-defalut-value and unset. +In core Protobuf, values that are _unset_ or equal to the default value are not sent over the wire. +The default value of a message is `undefined`. Primitive types take their natural default value, i.e. `string` is `''`, `number` is `0`, etc. +This behavior enables forward compatibility, as primitive fields will always have a value, even when omitted by outdated agents, but it also means _default_ and _unset_ values cannot be distinguished. -I.e. if you have a `string name = 1`, and set `object.name = ''`, Protobuf will skip sending the tagged `name` field over the wire, because its understood that readers on the other end will, when they see `name` is not included in the payload, return empty string. +If you need primitive fields where you can detect set/unset, see [Wrapper Types](#wrapper-types). -`ts-proto` models this behavior, of "unset" values being the primitive's default. (Technically by setting up an object prototype that knows the default values of the message's primitive fields.) +**Encode / Decode** -If you want fields where you can model set/unset, see Wrapper Types. +`ts-proto` follows the Protobuf rules, and always returns default values for unsets fields when decoding, while omitting them from the output when serialized in binary format. -# Wrapper Types +```protobuf +syntax = "proto3"; +message Foo { + string bar = 1; +} +``` + +```typescript +protobufBytes; // assume this is an empty Foo object, in protobuf binary format +Foo.decode(protobufBytes); // => { bar: '' } +``` + +```typescript +Foo.encode({ bar: '' }); // => { }, writes an empty Foo object, in protobuf binary format +``` + +**fromJSON / toJSON** + +Reading JSON will also initialize the default values. Since senders may either omit unset fields, or set them to the default value, use `fromJSON` to normalize the input. + +```typescript +Foo.fromJSON({}); // => { bar: '' } +``` + +When writing JSON, `ts-proto` currently does **not** normalize message when converting to JSON, other than omitting unset fields, but it may do so in the future. + +```typescript +// Current ts-proto behavior +Foo.toJSON({ }); // => { } +Foo.toJSON({ bar: '' }); // => { bar: '' } +Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' } +``` + +```typescript +// Possible future behavior, where ts-proto would normalize message +Foo.toJSON({ }); // => { } +Foo.toJSON({ bar: '' }); // => { } +Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' } +``` -In core Protobuf, unset primitive fields become their respective default values (so you loose ability to distinguish "unset" from "default"). +- Please open an issue if you need this behavior. -However, unset message fields stay `null`. +# Well-Known Types -This allows a cute hack where you can model a logical `string | unset` by creating a field that is technically a message (i.e. so it can stay `null` for the unset case), but the message only has a single string field (i.e for storing the value in the set case). +Protobuf comes with several predefined message definitions, called "[Well-Known Types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf)". +Their interpretation is defined by the Protobuf specification, and libraries are expected to convert these messages to corresponding native types in the target language. -Protobuf has already "blessed" this pattern with several built-in types, i.e. `google.protobuf.StringValue`, `google.protobuf.Int32Value`, etc. +`ts-proto` currently automatically converts these messages to their corresponding native types. -`ts-proto` understands these wrapper types and "re-idiomizes" them by generating a `google.protobuf.StringValue name = 1` field as a `name: string | undefined`, and hides the `StringValue` implementation detail from your code (i.e. during `encode`/`decode` of the `name` field on the wire to external consumers, it's still read/written as a `StringValue` message field). +- Wrapper Types: + + * [google.protobuf.DoubleValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#DoubleValue) ⇆ `number | undefined` + * [google.protobuf.FloatValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#FloatValue) ⇆ `number | undefined` + * [google.protobuf.Int64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Int64Value) ⇆ `number | undefined` + * [google.protobuf.UInt64Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#UInt64Value) ⇆ `number | undefined` + * [google.protobuf.Int32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Int32Value) ⇆ `number | undefined` + * [google.protobuf.UInt32Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#UInt32Value) ⇆ `number | undefined` + * [google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#BoolValue) ⇆ `boolean | undefined` + * [google.protobuf.StringValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#StringValue) ⇆ `string | undefined` + * [google.protobuf.BytesValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.BytesValue) ⇆ `Uint8Array | undefined` + +- JSON Types (Struct Types): + + * [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Value) ⇆ `any | undefined` (i.e. `number | string | boolean | null | array | object`) + * [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#ListValue) ⇆ `any[]` + * [google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Struct) ⇆ `{ [key: string]: any } | undefined` + +## Wrapper Types + +Wrapper Types are messages containing a single primitive field, and can be imported in `.proto` files with `import "google/protobuf/wrappers.proto"`. + + +Since these are _messages_, their default value is `undefined`, allowing you to distinguish unset primitives from their default values, when using Wrapper Types. +`ts-proto` generates these fields as ` | undefined`. + +For example: + +```protobuf +// Protobuf +syntax = "proto3"; + +import "google/protobuf/wrappers.proto"; + +message ExampleMessage { + google.protobuf.StringValue name = 1; +} +``` + +```typescript +// TypeScript +interface ExampleMessage { + name: string | undefined; +} +``` + +When encoding a message the primitive value is converted back to its corresponding wrapper type: + +```typescript +ExampleMessage.encode({ name: 'foo' }) // => { name: { value: 'foo' } }, in binary +``` + +When calling toJSON, the value is not converted, because wrapper types are idiomatic in JSON. + +```typescript +ExampleMessage.toJSON({ name: 'foo' }) // => { name: 'foo' } +``` + +## JSON Types (Struct Types) + +Protobuf's language and types are not sufficient to represent all possible JSON values, since JSON may contain values whose type is unknown in advance. +For this reason, Protobuf offers several additional types to represent arbitrary JSON values. + +These are called Struct Types, and can be imported in `.proto` files with `import "google/protobuf/struct.proto"`. + +- [google.protobuf.Value](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Value) ⇆ `any` + - This is the most general type, and can represent any JSON value (i.e. `number | string | boolean | null | array | object`). +- [google.protobuf.ListValue](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#ListValue) ⇆ `any[]` + - To represent a JSON array +- [google.protobuf.Struct](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#Struct) ⇆ `{ [key: string]: any }` + - To represent a JSON object + +`ts-proto` automatically converts back and forth between these Struct Types and their corresponding JSON types. + +Example: + +```protobuf +// Protobuf +syntax = "proto3"; + +import "google/protobuf/struct.proto"; + +message ExampleMessage { + google.protobuf.Value anything = 1; +} +``` + +```typescript +// TypeScript +interface ExampleMessage { + anything: any | undefined; +} +``` + +Encoding a JSON value embedded in a message, converts it to a Struct Type: +```typescript +ExampleMessage.encode({ anything: { "name": "hello" } }) +/* Outputs the following structure, encoded in protobuf binary format: +{ + anything: Value { + structValue = Struct { + fields = [ + MapEntry { + key = "name", + value = Value { + stringValue = "hello" + } + ] + } + } + } +}*/ + +ExampleMessage.encode({ anything: true }) +/* Outputs the following structure encoded in protobuf binary format: +{ + anything: Value { + boolValue = true + } +}*/ +``` + +## Timestamp + +The representation of `google.protobuf.Timestamp` is configurable by the `useDate` flag. + +| Protobuf well-known type | Default/`useDate=true` | `useDate=false` | `useDate=string` | +| --------------------------- | ---------------------- | ------------------------------------ | ---------------- | +| `google.protobuf.Timestamp` | `Date` | `{ seconds: number, nanos: number }` | `string` | -This makes dealing with `string | unset` in your code much nicer, albeit it's unfortunate that, in Protobuf core, this is not as simple as marking a `string name = 1` field as `optional`, i.e. you have to "dirty" your proto files a bit by knowing to use the `StringValue` convention. # Number Types @@ -452,14 +623,6 @@ The protobuf number types map to JavaScript types based on the `forceLong` confi Where (\*) indicates they might throw an error at runtime. -# Timestamps - -The representation of `google.protobuf.Timestamp` is configurable by the `useDate` flag. - -| Protobuf well-known type | Default/`useDate=true` | `useDate=false` | `useDate=string` | -| --------------------------- | ---------------------- | ------------------------------------ | ---------------- | -| `google.protobuf.Timestamp` | `Date` | `{ seconds: number, nanos: number }` | `string` | - # Current Status of Optional Values - Required primitives: use as-is, i.e. `string name = 1`. From 899dc1887a9b7176b43d98196deb3884d56eb08e Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Mon, 22 Nov 2021 00:53:49 +0100 Subject: [PATCH 7/8] Better fromJSON normalization example --- README.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 39546da53..b6475b6ae 100644 --- a/README.markdown +++ b/README.markdown @@ -435,7 +435,9 @@ Foo.encode({ bar: '' }); // => { }, writes an empty Foo object, in protobuf bina Reading JSON will also initialize the default values. Since senders may either omit unset fields, or set them to the default value, use `fromJSON` to normalize the input. ```typescript -Foo.fromJSON({}); // => { bar: '' } +Foo.fromJSON({ }); // => { bar: '' } +Foo.fromJSON({ bar: '' }); // => { bar: '' } +Foo.fromJSON({ bar: 'baz' }); // => { bar: 'baz' } ``` When writing JSON, `ts-proto` currently does **not** normalize message when converting to JSON, other than omitting unset fields, but it may do so in the future. From f6b2a6dc2cd80a7b7657999d1ffee813e6f9e068 Mon Sep 17 00:00:00 2001 From: Bouke Versteegh Date: Mon, 22 Nov 2021 00:57:29 +0100 Subject: [PATCH 8/8] Add example that highlights non-normalizing behavior of toJSON --- README.markdown | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.markdown b/README.markdown index b6475b6ae..9bf3c0b2e 100644 --- a/README.markdown +++ b/README.markdown @@ -445,14 +445,16 @@ When writing JSON, `ts-proto` currently does **not** normalize message when conv ```typescript // Current ts-proto behavior Foo.toJSON({ }); // => { } -Foo.toJSON({ bar: '' }); // => { bar: '' } +Foo.toJSON({ bar: undefined }); // => { } +Foo.toJSON({ bar: '' }); // => { bar: '' } - note: this is the default value, but it's not omitted Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' } ``` ```typescript // Possible future behavior, where ts-proto would normalize message Foo.toJSON({ }); // => { } -Foo.toJSON({ bar: '' }); // => { } +Foo.toJSON({ bar: undefined }); // => { } +Foo.toJSON({ bar: '' }); // => { } - note: omitting the default value, as expected Foo.toJSON({ bar: 'baz' }); // => { bar: 'baz' } ```