From a40e6cd8586944c4e3801f46972784a79407abbe Mon Sep 17 00:00:00 2001 From: achingbrain Date: Thu, 11 Aug 2022 08:19:14 +0100 Subject: [PATCH] feat: define default types Proto3 language guide [states](https://developers.google.com/protocol-buffers/docs/proto3#default): > When a message is parsed, if the encoded message does not contain a particular singular element, the corresponding field in the parsed object is set to the default value for that field. When decoding objects, create an object with all non-optional fields set to the default values, then overwrite them during decoding. Refs #43 --- .../protons-benchmark/src/protons/bench.ts | 21 +-- packages/protons-runtime/src/index.ts | 6 +- packages/protons/src/index.ts | 88 +++++++++- packages/protons/test/fixtures/basic.proto | 2 +- packages/protons/test/fixtures/basic.ts | 4 +- packages/protons/test/fixtures/circuit.ts | 14 +- packages/protons/test/fixtures/daemon.ts | 158 +++++++----------- packages/protons/test/fixtures/dht.ts | 31 +--- packages/protons/test/fixtures/noise.ts | 6 +- packages/protons/test/fixtures/peer.ts | 34 ++-- packages/protons/test/fixtures/test.ts | 15 +- packages/protons/test/index.spec.ts | 12 ++ 12 files changed, 212 insertions(+), 179 deletions(-) diff --git a/packages/protons-benchmark/src/protons/bench.ts b/packages/protons-benchmark/src/protons/bench.ts index efcdf84..e5f8110 100644 --- a/packages/protons-benchmark/src/protons/bench.ts +++ b/packages/protons-benchmark/src/protons/bench.ts @@ -28,7 +28,7 @@ export namespace Foo { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Foo = {} const end = length == null ? reader.len : reader.pos + length @@ -84,7 +84,7 @@ export namespace Bar { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Bar = {} const end = length == null ? reader.len : reader.pos + length @@ -159,7 +159,9 @@ export namespace Yo { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Yo = { + lol: [] + } const end = length == null ? reader.len : reader.pos + length @@ -168,7 +170,6 @@ export namespace Yo { switch (tag >>> 3) { case 1: - obj.lol = obj.lol ?? [] obj.lol.push(FOO.codec().decode(reader)) break default: @@ -177,12 +178,6 @@ export namespace Yo { } } - obj.lol = obj.lol ?? [] - - if (obj.lol == null) { - throw new Error('Protocol error: value for required field "lol" was not found in protobuf') - } - return obj }) } @@ -230,7 +225,9 @@ export namespace Lol { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Lol = { + b: null + } const end = length == null ? reader.len : reader.pos + length @@ -311,7 +308,7 @@ export namespace Test { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Test = {} const end = length == null ? reader.len : reader.pos + length diff --git a/packages/protons-runtime/src/index.ts b/packages/protons-runtime/src/index.ts index 095bf20..54ac86b 100644 --- a/packages/protons-runtime/src/index.ts +++ b/packages/protons-runtime/src/index.ts @@ -180,12 +180,12 @@ export interface Reader { double: () => number /** - * Reads a sequence of bytes preceeded by its length as a varint + * Reads a sequence of bytes preceded by its length as a varint */ - bytes: () => number + bytes: () => Uint8Array /** - * Reads a string preceeded by its byte length as a varint + * Reads a string preceded by its byte length as a varint */ string: () => string diff --git a/packages/protons/src/index.ts b/packages/protons/src/index.ts index 2b03b25..4a5c8a3 100644 --- a/packages/protons/src/index.ts +++ b/packages/protons/src/index.ts @@ -73,6 +73,25 @@ const decoderGenerators: Record string> = { uint64: () => 'reader.uint64()' } +const defaultValueGenerators: Record string> = { + bool: () => 'false', + bytes: () => 'new Uint8Array(0)', + double: () => '0', + // enumeration: () => `writer.double(${val})`, + fixed32: () => '0', + fixed64: () => '0n', + float: () => '0', + int32: () => '0', + int64: () => '0n', + sfixed32: () => '0', + sfixed64: () => '0n', + sint32: () => '0', + sint64: () => '0n', + string: () => "''", + uint32: () => '0', + uint64: () => '0n' +} + function findTypeName (typeName: string, classDef: MessageDef, moduleDef: ModuleDef): string { if (types[typeName] != null) { return types[typeName] @@ -117,6 +136,65 @@ function findDef (typeName: string, classDef: MessageDef, moduleDef: ModuleDef): throw new Error(`Could not resolve type name "${typeName}"`) } +function createDefaultObject (fields: Record, messageDef: MessageDef, moduleDef: ModuleDef): string { + const output = Object.entries(fields) + .map(([name, fieldDef]) => { + if (fieldDef.repeated) { + return `${name}: []` + } + + if (fieldDef.optional) { + return '' + } + + const type: string = fieldDef.type + let defaultValue + + if (defaultValueGenerators[type] != null) { + defaultValue = defaultValueGenerators[type]() + } else { + const def = findDef(fieldDef.type, messageDef, moduleDef) + + if (isEnumDef(def)) { + // select lowest-value enum - should be 0 but it's not guaranteed + const val = Object.entries(def.values) + .sort((a, b) => { + if (a[1] < b[1]) { + return 1 + } + + if (a[1] > b[1]) { + return -1 + } + + return 0 + }) + .pop() + + if (val == null) { + throw new Error(`Could not find default enum value for ${def.fullName}`) + } + + defaultValue = `${def.name}.${val[0]}` + } else { + defaultValue = 'null' + } + } + + return `${name}: ${defaultValue}` + }) + .filter(Boolean) + .join(',\n ') + + if (output !== '') { + return ` + ${output} + ` + } + + return '' +} + const encoders: Record = { bool: 'bool', bytes: 'bytes', @@ -259,7 +337,7 @@ export interface ${messageDef.name} { const ensureArrayProps = Object.entries(fields) .map(([name, fieldDef]) => { // make sure repeated fields have an array if not set - if (fieldDef.rule === 'repeated') { + if (fieldDef.optional && fieldDef.rule === 'repeated') { return ` obj.${name} = obj.${name} ?? []` } @@ -269,7 +347,7 @@ export interface ${messageDef.name} { const ensureRequiredFields = Object.entries(fields) .map(([name, fieldDef]) => { // make sure required fields are set - if (!fieldDef.optional) { + if (!fieldDef.optional && !fieldDef.repeated) { return ` if (obj.${name} == null) { throw new Error('Protocol error: value for required field "${name}" was not found in protobuf') @@ -331,7 +409,7 @@ ${Object.entries(fields) writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: ${messageDef.name} = {${createDefaultObject(fields, messageDef, moduleDef)}} const end = length == null ? reader.len : reader.pos + length @@ -360,8 +438,10 @@ ${Object.entries(fields) } return `case ${fieldDef.id}:${fieldDef.rule === 'repeated' +? `${fieldDef.optional ? ` - obj.${name} = obj.${name} ?? [] + obj.${name} = obj.${name} ?? []` +: ''} obj.${name}.push(${decoderGenerators[type] == null ? `${codec}.decode(reader${type === 'message' ? ', reader.uint32()' : ''})` : decoderGenerators[type]()})` : ` obj.${name} = ${decoderGenerators[type] == null ? `${codec}.decode(reader${type === 'message' ? ', reader.uint32()' : ''})` : decoderGenerators[type]()}`} diff --git a/packages/protons/test/fixtures/basic.proto b/packages/protons/test/fixtures/basic.proto index 55882d4..83fbc41 100644 --- a/packages/protons/test/fixtures/basic.proto +++ b/packages/protons/test/fixtures/basic.proto @@ -2,5 +2,5 @@ syntax = "proto3"; message Basic { optional string foo = 1; - required int32 num = 2; + int32 num = 2; } diff --git a/packages/protons/test/fixtures/basic.ts b/packages/protons/test/fixtures/basic.ts index 0950495..d13e6ce 100644 --- a/packages/protons/test/fixtures/basic.ts +++ b/packages/protons/test/fixtures/basic.ts @@ -36,7 +36,9 @@ export namespace Basic { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Basic = { + num: 0 + } const end = length == null ? reader.len : reader.pos + length diff --git a/packages/protons/test/fixtures/circuit.ts b/packages/protons/test/fixtures/circuit.ts index 44b0127..f3a651e 100644 --- a/packages/protons/test/fixtures/circuit.ts +++ b/packages/protons/test/fixtures/circuit.ts @@ -112,7 +112,10 @@ export namespace CircuitRelay { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Peer = { + id: new Uint8Array(0), + addrs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -124,7 +127,6 @@ export namespace CircuitRelay { obj.id = reader.bytes() break case 2: - obj.addrs = obj.addrs ?? [] obj.addrs.push(reader.bytes()) break default: @@ -133,16 +135,10 @@ export namespace CircuitRelay { } } - obj.addrs = obj.addrs ?? [] - if (obj.id == null) { throw new Error('Protocol error: value for required field "id" was not found in protobuf') } - if (obj.addrs == null) { - throw new Error('Protocol error: value for required field "addrs" was not found in protobuf') - } - return obj }) } @@ -192,7 +188,7 @@ export namespace CircuitRelay { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: CircuitRelay = {} const end = length == null ? reader.len : reader.pos + length diff --git a/packages/protons/test/fixtures/daemon.ts b/packages/protons/test/fixtures/daemon.ts index e2f0627..c66a93d 100644 --- a/packages/protons/test/fixtures/daemon.ts +++ b/packages/protons/test/fixtures/daemon.ts @@ -110,7 +110,9 @@ export namespace Request { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Request = { + type: Type.IDENTIFY + } const end = length == null ? reader.len : reader.pos + length @@ -258,7 +260,10 @@ export namespace Response { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Response = { + type: Type.OK, + peers: [] + } const end = length == null ? reader.len : reader.pos + length @@ -282,7 +287,6 @@ export namespace Response { obj.dht = DHTResponse.codec().decode(reader, reader.uint32()) break case 6: - obj.peers = obj.peers ?? [] obj.peers.push(PeerInfo.codec().decode(reader, reader.uint32())) break case 7: @@ -297,16 +301,10 @@ export namespace Response { } } - obj.peers = obj.peers ?? [] - if (obj.type == null) { throw new Error('Protocol error: value for required field "type" was not found in protobuf') } - if (obj.peers == null) { - throw new Error('Protocol error: value for required field "peers" was not found in protobuf') - } - return obj }) } @@ -358,7 +356,10 @@ export namespace IdentifyResponse { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: IdentifyResponse = { + id: new Uint8Array(0), + addrs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -370,7 +371,6 @@ export namespace IdentifyResponse { obj.id = reader.bytes() break case 2: - obj.addrs = obj.addrs ?? [] obj.addrs.push(reader.bytes()) break default: @@ -379,16 +379,10 @@ export namespace IdentifyResponse { } } - obj.addrs = obj.addrs ?? [] - if (obj.id == null) { throw new Error('Protocol error: value for required field "id" was not found in protobuf') } - if (obj.addrs == null) { - throw new Error('Protocol error: value for required field "addrs" was not found in protobuf') - } - return obj }) } @@ -446,7 +440,10 @@ export namespace ConnectRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: ConnectRequest = { + peer: new Uint8Array(0), + addrs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -458,7 +455,6 @@ export namespace ConnectRequest { obj.peer = reader.bytes() break case 2: - obj.addrs = obj.addrs ?? [] obj.addrs.push(reader.bytes()) break case 3: @@ -470,16 +466,10 @@ export namespace ConnectRequest { } } - obj.addrs = obj.addrs ?? [] - if (obj.peer == null) { throw new Error('Protocol error: value for required field "peer" was not found in protobuf') } - if (obj.addrs == null) { - throw new Error('Protocol error: value for required field "addrs" was not found in protobuf') - } - return obj }) } @@ -537,7 +527,10 @@ export namespace StreamOpenRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: StreamOpenRequest = { + peer: new Uint8Array(0), + proto: [] + } const end = length == null ? reader.len : reader.pos + length @@ -549,7 +542,6 @@ export namespace StreamOpenRequest { obj.peer = reader.bytes() break case 2: - obj.proto = obj.proto ?? [] obj.proto.push(reader.string()) break case 3: @@ -561,16 +553,10 @@ export namespace StreamOpenRequest { } } - obj.proto = obj.proto ?? [] - if (obj.peer == null) { throw new Error('Protocol error: value for required field "peer" was not found in protobuf') } - if (obj.proto == null) { - throw new Error('Protocol error: value for required field "proto" was not found in protobuf') - } - return obj }) } @@ -622,7 +608,10 @@ export namespace StreamHandlerRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: StreamHandlerRequest = { + addr: new Uint8Array(0), + proto: [] + } const end = length == null ? reader.len : reader.pos + length @@ -634,7 +623,6 @@ export namespace StreamHandlerRequest { obj.addr = reader.bytes() break case 2: - obj.proto = obj.proto ?? [] obj.proto.push(reader.string()) break default: @@ -643,16 +631,10 @@ export namespace StreamHandlerRequest { } } - obj.proto = obj.proto ?? [] - if (obj.addr == null) { throw new Error('Protocol error: value for required field "addr" was not found in protobuf') } - if (obj.proto == null) { - throw new Error('Protocol error: value for required field "proto" was not found in protobuf') - } - return obj }) } @@ -694,7 +676,9 @@ export namespace ErrorResponse { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: ErrorResponse = { + msg: '' + } const end = length == null ? reader.len : reader.pos + length @@ -772,7 +756,11 @@ export namespace StreamInfo { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: StreamInfo = { + peer: new Uint8Array(0), + addr: new Uint8Array(0), + proto: '' + } const end = length == null ? reader.len : reader.pos + length @@ -914,7 +902,9 @@ export namespace DHTRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: DHTRequest = { + type: Type.FIND_PEER + } const end = length == null ? reader.len : reader.pos + length @@ -1024,7 +1014,9 @@ export namespace DHTResponse { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: DHTResponse = { + type: Type.BEGIN + } const end = length == null ? reader.len : reader.pos + length @@ -1102,7 +1094,10 @@ export namespace PeerInfo { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PeerInfo = { + id: new Uint8Array(0), + addrs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -1114,7 +1109,6 @@ export namespace PeerInfo { obj.id = reader.bytes() break case 2: - obj.addrs = obj.addrs ?? [] obj.addrs.push(reader.bytes()) break default: @@ -1123,16 +1117,10 @@ export namespace PeerInfo { } } - obj.addrs = obj.addrs ?? [] - if (obj.id == null) { throw new Error('Protocol error: value for required field "id" was not found in protobuf') } - if (obj.addrs == null) { - throw new Error('Protocol error: value for required field "addrs" was not found in protobuf') - } - return obj }) } @@ -1210,7 +1198,9 @@ export namespace ConnManagerRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: ConnManagerRequest = { + type: Type.TAG_PEER + } const end = length == null ? reader.len : reader.pos + length @@ -1281,7 +1271,9 @@ export namespace DisconnectRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: DisconnectRequest = { + peer: new Uint8Array(0) + } const end = length == null ? reader.len : reader.pos + length @@ -1375,7 +1367,9 @@ export namespace PSRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PSRequest = { + type: Type.GET_TOPICS + } const end = length == null ? reader.len : reader.pos + length @@ -1475,7 +1469,9 @@ export namespace PSMessage { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PSMessage = { + topicIDs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -1493,7 +1489,6 @@ export namespace PSMessage { obj.seqno = reader.bytes() break case 4: - obj.topicIDs = obj.topicIDs ?? [] obj.topicIDs.push(reader.string()) break case 5: @@ -1508,12 +1503,6 @@ export namespace PSMessage { } } - obj.topicIDs = obj.topicIDs ?? [] - - if (obj.topicIDs == null) { - throw new Error('Protocol error: value for required field "topicIDs" was not found in protobuf') - } - return obj }) } @@ -1567,7 +1556,10 @@ export namespace PSResponse { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PSResponse = { + topics: [], + peerIDs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -1576,11 +1568,9 @@ export namespace PSResponse { switch (tag >>> 3) { case 1: - obj.topics = obj.topics ?? [] obj.topics.push(reader.string()) break case 2: - obj.peerIDs = obj.peerIDs ?? [] obj.peerIDs.push(reader.bytes()) break default: @@ -1589,17 +1579,6 @@ export namespace PSResponse { } } - obj.topics = obj.topics ?? [] - obj.peerIDs = obj.peerIDs ?? [] - - if (obj.topics == null) { - throw new Error('Protocol error: value for required field "topics" was not found in protobuf') - } - - if (obj.peerIDs == null) { - throw new Error('Protocol error: value for required field "peerIDs" was not found in protobuf') - } - return obj }) } @@ -1673,7 +1652,10 @@ export namespace PeerstoreRequest { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PeerstoreRequest = { + type: Type.GET_PROTOCOLS, + protos: [] + } const end = length == null ? reader.len : reader.pos + length @@ -1688,7 +1670,6 @@ export namespace PeerstoreRequest { obj.id = reader.bytes() break case 3: - obj.protos = obj.protos ?? [] obj.protos.push(reader.string()) break default: @@ -1697,16 +1678,10 @@ export namespace PeerstoreRequest { } } - obj.protos = obj.protos ?? [] - if (obj.type == null) { throw new Error('Protocol error: value for required field "type" was not found in protobuf') } - if (obj.protos == null) { - throw new Error('Protocol error: value for required field "protos" was not found in protobuf') - } - return obj }) } @@ -1756,7 +1731,9 @@ export namespace PeerstoreResponse { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: PeerstoreResponse = { + protos: [] + } const end = length == null ? reader.len : reader.pos + length @@ -1768,7 +1745,6 @@ export namespace PeerstoreResponse { obj.peer = PeerInfo.codec().decode(reader, reader.uint32()) break case 2: - obj.protos = obj.protos ?? [] obj.protos.push(reader.string()) break default: @@ -1777,12 +1753,6 @@ export namespace PeerstoreResponse { } } - obj.protos = obj.protos ?? [] - - if (obj.protos == null) { - throw new Error('Protocol error: value for required field "protos" was not found in protobuf') - } - return obj }) } diff --git a/packages/protons/test/fixtures/dht.ts b/packages/protons/test/fixtures/dht.ts index fc278a9..0ea8566 100644 --- a/packages/protons/test/fixtures/dht.ts +++ b/packages/protons/test/fixtures/dht.ts @@ -52,7 +52,7 @@ export namespace Record { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Record = {} const end = length == null ? reader.len : reader.pos + length @@ -190,7 +190,9 @@ export namespace Message { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Peer = { + addrs: [] + } const end = length == null ? reader.len : reader.pos + length @@ -202,7 +204,6 @@ export namespace Message { obj.id = reader.bytes() break case 2: - obj.addrs = obj.addrs ?? [] obj.addrs.push(reader.bytes()) break case 3: @@ -214,12 +215,6 @@ export namespace Message { } } - obj.addrs = obj.addrs ?? [] - - if (obj.addrs == null) { - throw new Error('Protocol error: value for required field "addrs" was not found in protobuf') - } - return obj }) } @@ -287,7 +282,10 @@ export namespace Message { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Message = { + closerPeers: [], + providerPeers: [] + } const end = length == null ? reader.len : reader.pos + length @@ -308,11 +306,9 @@ export namespace Message { obj.record = reader.bytes() break case 8: - obj.closerPeers = obj.closerPeers ?? [] obj.closerPeers.push(Message.Peer.codec().decode(reader, reader.uint32())) break case 9: - obj.providerPeers = obj.providerPeers ?? [] obj.providerPeers.push(Message.Peer.codec().decode(reader, reader.uint32())) break default: @@ -321,17 +317,6 @@ export namespace Message { } } - obj.closerPeers = obj.closerPeers ?? [] - obj.providerPeers = obj.providerPeers ?? [] - - if (obj.closerPeers == null) { - throw new Error('Protocol error: value for required field "closerPeers" was not found in protobuf') - } - - if (obj.providerPeers == null) { - throw new Error('Protocol error: value for required field "providerPeers" was not found in protobuf') - } - return obj }) } diff --git a/packages/protons/test/fixtures/noise.ts b/packages/protons/test/fixtures/noise.ts index 0bdada6..154febf 100644 --- a/packages/protons/test/fixtures/noise.ts +++ b/packages/protons/test/fixtures/noise.ts @@ -47,7 +47,11 @@ export namespace pb { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: NoiseHandshakePayload = { + identityKey: new Uint8Array(0), + identitySig: new Uint8Array(0), + data: new Uint8Array(0) + } const end = length == null ? reader.len : reader.pos + length diff --git a/packages/protons/test/fixtures/peer.ts b/packages/protons/test/fixtures/peer.ts index 68bd477..893e1ce 100644 --- a/packages/protons/test/fixtures/peer.ts +++ b/packages/protons/test/fixtures/peer.ts @@ -64,7 +64,11 @@ export namespace Peer { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Peer = { + addresses: [], + protocols: [], + metadata: [] + } const end = length == null ? reader.len : reader.pos + length @@ -73,15 +77,12 @@ export namespace Peer { switch (tag >>> 3) { case 1: - obj.addresses = obj.addresses ?? [] obj.addresses.push(Address.codec().decode(reader, reader.uint32())) break case 2: - obj.protocols = obj.protocols ?? [] obj.protocols.push(reader.string()) break case 3: - obj.metadata = obj.metadata ?? [] obj.metadata.push(Metadata.codec().decode(reader, reader.uint32())) break case 4: @@ -96,22 +97,6 @@ export namespace Peer { } } - obj.addresses = obj.addresses ?? [] - obj.protocols = obj.protocols ?? [] - obj.metadata = obj.metadata ?? [] - - if (obj.addresses == null) { - throw new Error('Protocol error: value for required field "addresses" was not found in protobuf') - } - - if (obj.protocols == null) { - throw new Error('Protocol error: value for required field "protocols" was not found in protobuf') - } - - if (obj.metadata == null) { - throw new Error('Protocol error: value for required field "metadata" was not found in protobuf') - } - return obj }) } @@ -159,7 +144,9 @@ export namespace Address { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Address = { + multiaddr: new Uint8Array(0) + } const end = length == null ? reader.len : reader.pos + length @@ -232,7 +219,10 @@ export namespace Metadata { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: Metadata = { + key: '', + value: new Uint8Array(0) + } const end = length == null ? reader.len : reader.pos + length diff --git a/packages/protons/test/fixtures/test.ts b/packages/protons/test/fixtures/test.ts index eea1650..f830ad3 100644 --- a/packages/protons/test/fixtures/test.ts +++ b/packages/protons/test/fixtures/test.ts @@ -45,7 +45,9 @@ export namespace SubMessage { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: SubMessage = { + foo: '' + } const end = length == null ? reader.len : reader.pos + length @@ -211,7 +213,9 @@ export namespace AllTheTypes { writer.ldelim() } }, (reader, length) => { - const obj: any = {} + const obj: AllTheTypes = { + field14: [] + } const end = length == null ? reader.len : reader.pos + length @@ -259,7 +263,6 @@ export namespace AllTheTypes { obj.field13 = SubMessage.codec().decode(reader, reader.uint32()) break case 14: - obj.field14 = obj.field14 ?? [] obj.field14.push(reader.string()) break case 15: @@ -280,12 +283,6 @@ export namespace AllTheTypes { } } - obj.field14 = obj.field14 ?? [] - - if (obj.field14 == null) { - throw new Error('Protocol error: value for required field "field14" was not found in protobuf') - } - return obj }) } diff --git a/packages/protons/test/index.spec.ts b/packages/protons/test/index.spec.ts index 060c8d6..a97825b 100644 --- a/packages/protons/test/index.spec.ts +++ b/packages/protons/test/index.spec.ts @@ -180,4 +180,16 @@ describe('encode', () => { expect(CircuitRelay.decode(encoded)).to.deep.equal(message) expect(CircuitRelay.decode(pbufJsBuf)).to.deep.equal(message) }) + + it('supports optional fields', () => { + const obj: Basic = { + num: 5 + } + + const encoded = Basic.encode(obj) + const decoded = Basic.decode(encoded) + + // foo is optional + expect(decoded).to.not.have.property('foo') + }) })