diff --git a/packages/protons-benchmark/src/protons/rpc.ts b/packages/protons-benchmark/src/protons/rpc.ts index 75781fd..a757885 100644 --- a/packages/protons-benchmark/src/protons/rpc.ts +++ b/packages/protons-benchmark/src/protons/rpc.ts @@ -115,7 +115,7 @@ export namespace RPC { if (opts.writeDefaults === true || obj.topic !== '') { w.uint32(34) - w.string(obj.topic) + w.string(obj.topic ?? '') } if (obj.signature != null) { diff --git a/packages/protons-runtime/src/codec.ts b/packages/protons-runtime/src/codec.ts index 367849a..c73fe80 100644 --- a/packages/protons-runtime/src/codec.ts +++ b/packages/protons-runtime/src/codec.ts @@ -16,7 +16,7 @@ export interface EncodeOptions { } export interface EncodeFunction { - (value: T, writer: Writer, opts?: EncodeOptions): void + (value: Partial, writer: Writer, opts?: EncodeOptions): void } export interface DecodeFunction { diff --git a/packages/protons-runtime/src/codecs/message.ts b/packages/protons-runtime/src/codecs/message.ts index f7abf47..eca5171 100644 --- a/packages/protons-runtime/src/codecs/message.ts +++ b/packages/protons-runtime/src/codecs/message.ts @@ -6,6 +6,6 @@ export interface Factory { new (obj: A): T } -export function message (encode: (obj: T, writer: Writer, opts?: EncodeOptions) => void, decode: (reader: Reader, length?: number) => T): Codec { +export function message (encode: (obj: Partial, writer: Writer, opts?: EncodeOptions) => void, decode: (reader: Reader, length?: number) => T): Codec { return createCodec('message', CODEC_TYPES.LENGTH_DELIMITED, encode, decode) } diff --git a/packages/protons/src/index.ts b/packages/protons/src/index.ts index d26c19f..6f5744d 100644 --- a/packages/protons/src/index.ts +++ b/packages/protons/src/index.ts @@ -37,22 +37,22 @@ const types: Record = { uint64: 'bigint' } -const encoderGenerators: Record string> = { - bool: (val) => `w.bool(${val})`, - bytes: (val) => `w.bytes(${val})`, - double: (val) => `w.double(${val})`, - fixed32: (val) => `w.fixed32(${val})`, - fixed64: (val) => `w.fixed64(${val})`, - float: (val) => `w.float(${val})`, - int32: (val) => `w.int32(${val})`, - int64: (val) => `w.int64(${val})`, - sfixed32: (val) => `w.sfixed32(${val})`, - sfixed64: (val) => `w.sfixed64(${val})`, - sint32: (val) => `w.sint32(${val})`, - sint64: (val) => `w.sint64(${val})`, - string: (val) => `w.string(${val})`, - uint32: (val) => `w.uint32(${val})`, - uint64: (val) => `w.uint64(${val})` +const encoderGenerators: Record string> = { + bool: (val, includeDefault) => `w.bool(${val}${includeDefault ? ' ?? false' : ''})`, + bytes: (val, includeDefault) => `w.bytes(${val}${includeDefault ? ' ?? new Uint8Array(0)' : ''})`, + double: (val, includeDefault) => `w.double(${val}${includeDefault ? ' ?? 0' : ''})`, + fixed32: (val, includeDefault) => `w.fixed32(${val}${includeDefault ? ' ?? 0' : ''})`, + fixed64: (val, includeDefault) => `w.fixed64(${val}${includeDefault ? ' ?? 0n' : ''})`, + float: (val, includeDefault) => `w.float(${val}${includeDefault ? ' ?? 0' : ''})`, + int32: (val, includeDefault) => `w.int32(${val}${includeDefault ? ' ?? 0' : ''})`, + int64: (val, includeDefault) => `w.int64(${val}${includeDefault ? ' ?? 0n' : ''})`, + sfixed32: (val, includeDefault) => `w.sfixed32(${val}${includeDefault ? ' ?? 0' : ''})`, + sfixed64: (val, includeDefault) => `w.sfixed64(${val}${includeDefault ? ' ?? 0n' : ''})`, + sint32: (val, includeDefault) => `w.sint32(${val}${includeDefault ? ' ?? 0' : ''})`, + sint64: (val, includeDefault) => `w.sint64(${val}${includeDefault ? ' ?? 0n' : ''})`, + string: (val, includeDefault) => `w.string(${val}${includeDefault ? ' ?? \'\'' : ''})`, + uint32: (val, includeDefault) => `w.uint32(${val}${includeDefault ? ' ?? 0' : ''})`, + uint64: (val, includeDefault) => `w.uint64(${val}${includeDefault ? ' ?? 0n' : ''})` } const decoderGenerators: Record string> = { @@ -92,21 +92,21 @@ const defaultValueGenerators: Record string> = { } const defaultValueTestGenerators: Record string> = { - bool: (field) => `${field} !== false`, + bool: (field) => `(${field} != null && ${field} !== false)`, bytes: (field) => `(${field} != null && ${field}.byteLength > 0)`, - double: (field) => `${field} !== 0`, - fixed32: (field) => `${field} !== 0`, - fixed64: (field) => `${field} !== 0n`, - float: (field) => `${field} !== 0`, - int32: (field) => `${field} !== 0`, - int64: (field) => `${field} !== 0n`, - sfixed32: (field) => `${field} !== 0`, - sfixed64: (field) => `${field} !== 0n`, - sint32: (field) => `${field} !== 0`, - sint64: (field) => `${field} !== 0n`, - string: (field) => `${field} !== ''`, - uint32: (field) => `${field} !== 0`, - uint64: (field) => `${field} !== 0n` + double: (field) => `(${field} != null && ${field} !== 0)`, + fixed32: (field) => `(${field} != null && ${field} !== 0)`, + fixed64: (field) => `(${field} != null && ${field} !== 0n)`, + float: (field) => `(${field} != null && ${field} !== 0)`, + int32: (field) => `(${field} != null && ${field} !== 0)`, + int64: (field) => `(${field} != null && ${field} !== 0n)`, + sfixed32: (field) => `(${field} != null && ${field} !== 0)`, + sfixed64: (field) => `(${field} != null && ${field} !== 0n)`, + sint32: (field) => `(${field} != null && ${field} !== 0)`, + sint64: (field) => `(${field} != null && ${field} !== 0n)`, + string: (field) => `(${field} != null && ${field} !== '')`, + uint32: (field) => `(${field} != null && ${field} !== 0)`, + uint64: (field) => `(${field} != null && ${field} !== 0n)` } function findTypeName (typeName: string, classDef: MessageDef, moduleDef: ModuleDef): string { @@ -401,15 +401,26 @@ export interface ${messageDef.name} { } } - function createWriteField (valueVar: string): string { + function createWriteField (valueVar: string): (includeDefault: boolean) => string { const id = (fieldDef.id << 3) | codecTypes[type] + let defaultValue = '' - let writeField = `w.uint32(${id}) - ${encoderGenerators[type] == null ? `${codec}.encode(${valueVar}, w)` : encoderGenerators[type](valueVar)}` + if (fieldDef.enum) { + const def = findDef(fieldDef.type, messageDef, moduleDef) + + if (!isEnumDef(def)) { + throw new Error(`${fieldDef.type} was not enum def`) + } + + defaultValue = Object.keys(def.values)[0] + } + + let writeField = (includeDefault: boolean): string => `w.uint32(${id}) + ${encoderGenerators[type] == null ? `${codec}.encode(${valueVar}${includeDefault ? ` ?? ${typeName}.${defaultValue}` : ''}, w)` : encoderGenerators[type](valueVar, includeDefault)}` if (type === 'message') { // message fields are only written if they have values - writeField = `w.uint32(${id}) + writeField = () => `w.uint32(${id}) ${typeName}.codec().encode(${valueVar}, w, { writeDefaults: ${Boolean(fieldDef.repeated).toString()} })` @@ -422,10 +433,10 @@ export interface ${messageDef.name} { if (fieldDef.repeated) { if (fieldDef.map) { - writeField = ` + writeField = () => ` for (const [key, value] of obj.${name}.entries()) { ${ - createWriteField('{ key, value }') + createWriteField('{ key, value }')(false) .split('\n') .map(s => { const trimmed = s.trim() @@ -437,10 +448,10 @@ export interface ${messageDef.name} { } `.trim() } else { - writeField = ` + writeField = () => ` for (const value of obj.${name}) { ${ - createWriteField('value') + createWriteField('value')(false) .split('\n') .map(s => { const trimmed = s.trim() @@ -456,7 +467,7 @@ export interface ${messageDef.name} { return ` if (${valueTest}) { - ${writeField} + ${writeField(valueTest.includes('opts.writeDefaults === true'))} }` }).join('\n') @@ -537,7 +548,7 @@ ${encodeFields === '' ? '' : `${encodeFields}\n`} return _codec } - export const encode = (obj: ${messageDef.name}): Uint8Array => { + export const encode = (obj: Partial<${messageDef.name}>): Uint8Array => { return encodeMessage(obj, ${messageDef.name}.codec()) } diff --git a/packages/protons/test/fixtures/basic.ts b/packages/protons/test/fixtures/basic.ts index e629eee..759c1e5 100644 --- a/packages/protons/test/fixtures/basic.ts +++ b/packages/protons/test/fixtures/basic.ts @@ -28,9 +28,9 @@ export namespace Basic { w.string(obj.foo) } - if (opts.writeDefaults === true || obj.num !== 0) { + if (opts.writeDefaults === true || (obj.num != null && obj.num !== 0)) { w.uint32(16) - w.int32(obj.num) + w.int32(obj.num ?? 0) } if (opts.lengthDelimited !== false) { @@ -66,7 +66,7 @@ export namespace Basic { return _codec } - export const encode = (obj: Basic): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Basic.codec()) } @@ -112,7 +112,7 @@ export namespace Empty { return _codec } - export const encode = (obj: Empty): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Empty.codec()) } diff --git a/packages/protons/test/fixtures/circuit.proto b/packages/protons/test/fixtures/circuit.proto index 1eaec2e..18c4535 100644 --- a/packages/protons/test/fixtures/circuit.proto +++ b/packages/protons/test/fixtures/circuit.proto @@ -29,7 +29,7 @@ message CircuitRelay { } message Peer { - required bytes id = 1; // peer id + bytes id = 1; // peer id repeated bytes addrs = 2; // peer's known addresses } diff --git a/packages/protons/test/fixtures/circuit.ts b/packages/protons/test/fixtures/circuit.ts index a0134c4..658f7ef 100644 --- a/packages/protons/test/fixtures/circuit.ts +++ b/packages/protons/test/fixtures/circuit.ts @@ -97,7 +97,7 @@ export namespace CircuitRelay { if (opts.writeDefaults === true || (obj.id != null && obj.id.byteLength > 0)) { w.uint32(10) - w.bytes(obj.id) + w.bytes(obj.id ?? new Uint8Array(0)) } if (obj.addrs != null) { @@ -141,7 +141,7 @@ export namespace CircuitRelay { return _codec } - export const encode = (obj: Peer): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Peer.codec()) } @@ -220,7 +220,7 @@ export namespace CircuitRelay { return _codec } - export const encode = (obj: CircuitRelay): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, CircuitRelay.codec()) } diff --git a/packages/protons/test/fixtures/daemon.ts b/packages/protons/test/fixtures/daemon.ts index 96b0eb2..8db6fee 100644 --- a/packages/protons/test/fixtures/daemon.ts +++ b/packages/protons/test/fixtures/daemon.ts @@ -64,7 +64,7 @@ export namespace Request { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - Request.Type.codec().encode(obj.type, w) + Request.Type.codec().encode(obj.type ?? Request.Type.IDENTIFY, w) } if (obj.connect != null) { @@ -177,7 +177,7 @@ export namespace Request { return _codec } - export const encode = (obj: Request): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Request.codec()) } @@ -225,7 +225,7 @@ export namespace Response { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - Response.Type.codec().encode(obj.type, w) + Response.Type.codec().encode(obj.type ?? Response.Type.OK, w) } if (obj.error != null) { @@ -331,7 +331,7 @@ export namespace Response { return _codec } - export const encode = (obj: Response): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Response.codec()) } @@ -357,7 +357,7 @@ export namespace IdentifyResponse { if (opts.writeDefaults === true || (obj.id != null && obj.id.byteLength > 0)) { w.uint32(10) - w.bytes(obj.id) + w.bytes(obj.id ?? new Uint8Array(0)) } if (obj.addrs != null) { @@ -401,7 +401,7 @@ export namespace IdentifyResponse { return _codec } - export const encode = (obj: IdentifyResponse): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, IdentifyResponse.codec()) } @@ -428,7 +428,7 @@ export namespace ConnectRequest { if (opts.writeDefaults === true || (obj.peer != null && obj.peer.byteLength > 0)) { w.uint32(10) - w.bytes(obj.peer) + w.bytes(obj.peer ?? new Uint8Array(0)) } if (obj.addrs != null) { @@ -480,7 +480,7 @@ export namespace ConnectRequest { return _codec } - export const encode = (obj: ConnectRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, ConnectRequest.codec()) } @@ -507,7 +507,7 @@ export namespace StreamOpenRequest { if (opts.writeDefaults === true || (obj.peer != null && obj.peer.byteLength > 0)) { w.uint32(10) - w.bytes(obj.peer) + w.bytes(obj.peer ?? new Uint8Array(0)) } if (obj.proto != null) { @@ -559,7 +559,7 @@ export namespace StreamOpenRequest { return _codec } - export const encode = (obj: StreamOpenRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, StreamOpenRequest.codec()) } @@ -585,7 +585,7 @@ export namespace StreamHandlerRequest { if (opts.writeDefaults === true || (obj.addr != null && obj.addr.byteLength > 0)) { w.uint32(10) - w.bytes(obj.addr) + w.bytes(obj.addr ?? new Uint8Array(0)) } if (obj.proto != null) { @@ -629,7 +629,7 @@ export namespace StreamHandlerRequest { return _codec } - export const encode = (obj: StreamHandlerRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, StreamHandlerRequest.codec()) } @@ -652,9 +652,9 @@ export namespace ErrorResponse { w.fork() } - if (opts.writeDefaults === true || obj.msg !== '') { + if (opts.writeDefaults === true || (obj.msg != null && obj.msg !== '')) { w.uint32(10) - w.string(obj.msg) + w.string(obj.msg ?? '') } if (opts.lengthDelimited !== false) { @@ -687,7 +687,7 @@ export namespace ErrorResponse { return _codec } - export const encode = (obj: ErrorResponse): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, ErrorResponse.codec()) } @@ -714,17 +714,17 @@ export namespace StreamInfo { if (opts.writeDefaults === true || (obj.peer != null && obj.peer.byteLength > 0)) { w.uint32(10) - w.bytes(obj.peer) + w.bytes(obj.peer ?? new Uint8Array(0)) } if (opts.writeDefaults === true || (obj.addr != null && obj.addr.byteLength > 0)) { w.uint32(18) - w.bytes(obj.addr) + w.bytes(obj.addr ?? new Uint8Array(0)) } - if (opts.writeDefaults === true || obj.proto !== '') { + if (opts.writeDefaults === true || (obj.proto != null && obj.proto !== '')) { w.uint32(26) - w.string(obj.proto) + w.string(obj.proto ?? '') } if (opts.lengthDelimited !== false) { @@ -765,7 +765,7 @@ export namespace StreamInfo { return _codec } - export const encode = (obj: StreamInfo): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, StreamInfo.codec()) } @@ -826,7 +826,7 @@ export namespace DHTRequest { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - DHTRequest.Type.codec().encode(obj.type, w) + DHTRequest.Type.codec().encode(obj.type ?? DHTRequest.Type.FIND_PEER, w) } if (obj.peer != null) { @@ -907,7 +907,7 @@ export namespace DHTRequest { return _codec } - export const encode = (obj: DHTRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, DHTRequest.codec()) } @@ -952,7 +952,7 @@ export namespace DHTResponse { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - DHTResponse.Type.codec().encode(obj.type, w) + DHTResponse.Type.codec().encode(obj.type ?? DHTResponse.Type.BEGIN, w) } if (obj.peer != null) { @@ -1003,7 +1003,7 @@ export namespace DHTResponse { return _codec } - export const encode = (obj: DHTResponse): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, DHTResponse.codec()) } @@ -1029,7 +1029,7 @@ export namespace PeerInfo { if (opts.writeDefaults === true || (obj.id != null && obj.id.byteLength > 0)) { w.uint32(10) - w.bytes(obj.id) + w.bytes(obj.id ?? new Uint8Array(0)) } if (obj.addrs != null) { @@ -1073,7 +1073,7 @@ export namespace PeerInfo { return _codec } - export const encode = (obj: PeerInfo): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PeerInfo.codec()) } @@ -1119,7 +1119,7 @@ export namespace ConnManagerRequest { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - ConnManagerRequest.Type.codec().encode(obj.type, w) + ConnManagerRequest.Type.codec().encode(obj.type ?? ConnManagerRequest.Type.TAG_PEER, w) } if (obj.peer != null) { @@ -1176,7 +1176,7 @@ export namespace ConnManagerRequest { return _codec } - export const encode = (obj: ConnManagerRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, ConnManagerRequest.codec()) } @@ -1201,7 +1201,7 @@ export namespace DisconnectRequest { if (opts.writeDefaults === true || (obj.peer != null && obj.peer.byteLength > 0)) { w.uint32(10) - w.bytes(obj.peer) + w.bytes(obj.peer ?? new Uint8Array(0)) } if (opts.lengthDelimited !== false) { @@ -1234,7 +1234,7 @@ export namespace DisconnectRequest { return _codec } - export const encode = (obj: DisconnectRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, DisconnectRequest.codec()) } @@ -1281,7 +1281,7 @@ export namespace PSRequest { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - PSRequest.Type.codec().encode(obj.type, w) + PSRequest.Type.codec().encode(obj.type ?? PSRequest.Type.GET_TOPICS, w) } if (obj.topic != null) { @@ -1330,7 +1330,7 @@ export namespace PSRequest { return _codec } - export const encode = (obj: PSRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PSRequest.codec()) } @@ -1435,7 +1435,7 @@ export namespace PSMessage { return _codec } - export const encode = (obj: PSMessage): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PSMessage.codec()) } @@ -1507,7 +1507,7 @@ export namespace PSResponse { return _codec } - export const encode = (obj: PSResponse): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PSResponse.codec()) } @@ -1552,7 +1552,7 @@ export namespace PeerstoreRequest { if (opts.writeDefaults === true || (obj.type != null && __TypeValues[obj.type] !== 0)) { w.uint32(8) - PeerstoreRequest.Type.codec().encode(obj.type, w) + PeerstoreRequest.Type.codec().encode(obj.type ?? PeerstoreRequest.Type.INVALID, w) } if (obj.id != null) { @@ -1604,7 +1604,7 @@ export namespace PeerstoreRequest { return _codec } - export const encode = (obj: PeerstoreRequest): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PeerstoreRequest.codec()) } @@ -1675,7 +1675,7 @@ export namespace PeerstoreResponse { return _codec } - export const encode = (obj: PeerstoreResponse): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, PeerstoreResponse.codec()) } diff --git a/packages/protons/test/fixtures/dht.ts b/packages/protons/test/fixtures/dht.ts index f692c0c..9fcab58 100644 --- a/packages/protons/test/fixtures/dht.ts +++ b/packages/protons/test/fixtures/dht.ts @@ -91,7 +91,7 @@ export namespace Record { return _codec } - export const encode = (obj: Record): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Record.codec()) } @@ -225,7 +225,7 @@ export namespace Message { return _codec } - export const encode = (obj: Peer): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Peer.codec()) } @@ -327,7 +327,7 @@ export namespace Message { return _codec } - export const encode = (obj: Message): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Message.codec()) } diff --git a/packages/protons/test/fixtures/maps.ts b/packages/protons/test/fixtures/maps.ts index 8bd8a50..5295105 100644 --- a/packages/protons/test/fixtures/maps.ts +++ b/packages/protons/test/fixtures/maps.ts @@ -22,9 +22,9 @@ export namespace SubMessage { w.fork() } - if (opts.writeDefaults === true || obj.foo !== '') { + if (opts.writeDefaults === true || (obj.foo != null && obj.foo !== '')) { w.uint32(10) - w.string(obj.foo) + w.string(obj.foo ?? '') } if (opts.lengthDelimited !== false) { @@ -57,7 +57,7 @@ export namespace SubMessage { return _codec } - export const encode = (obj: SubMessage): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, SubMessage.codec()) } @@ -89,14 +89,14 @@ export namespace MapTypes { w.fork() } - if (opts.writeDefaults === true || obj.key !== '') { + if (opts.writeDefaults === true || (obj.key != null && obj.key !== '')) { w.uint32(10) - w.string(obj.key) + w.string(obj.key ?? '') } - if (opts.writeDefaults === true || obj.value !== '') { + if (opts.writeDefaults === true || (obj.value != null && obj.value !== '')) { w.uint32(18) - w.string(obj.value) + w.string(obj.value ?? '') } if (opts.lengthDelimited !== false) { @@ -133,7 +133,7 @@ export namespace MapTypes { return _codec } - export const encode = (obj: MapTypes$stringMapEntry): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, MapTypes$stringMapEntry.codec()) } @@ -157,14 +157,14 @@ export namespace MapTypes { w.fork() } - if (opts.writeDefaults === true || obj.key !== 0) { + if (opts.writeDefaults === true || (obj.key != null && obj.key !== 0)) { w.uint32(8) - w.int32(obj.key) + w.int32(obj.key ?? 0) } - if (opts.writeDefaults === true || obj.value !== 0) { + if (opts.writeDefaults === true || (obj.value != null && obj.value !== 0)) { w.uint32(16) - w.int32(obj.value) + w.int32(obj.value ?? 0) } if (opts.lengthDelimited !== false) { @@ -201,7 +201,7 @@ export namespace MapTypes { return _codec } - export const encode = (obj: MapTypes$intMapEntry): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, MapTypes$intMapEntry.codec()) } @@ -225,14 +225,14 @@ export namespace MapTypes { w.fork() } - if (opts.writeDefaults === true || obj.key !== false) { + if (opts.writeDefaults === true || (obj.key != null && obj.key !== false)) { w.uint32(8) - w.bool(obj.key) + w.bool(obj.key ?? false) } - if (opts.writeDefaults === true || obj.value !== false) { + if (opts.writeDefaults === true || (obj.value != null && obj.value !== false)) { w.uint32(16) - w.bool(obj.value) + w.bool(obj.value ?? false) } if (opts.lengthDelimited !== false) { @@ -269,7 +269,7 @@ export namespace MapTypes { return _codec } - export const encode = (obj: MapTypes$boolMapEntry): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, MapTypes$boolMapEntry.codec()) } @@ -293,9 +293,9 @@ export namespace MapTypes { w.fork() } - if (opts.writeDefaults === true || obj.key !== '') { + if (opts.writeDefaults === true || (obj.key != null && obj.key !== '')) { w.uint32(10) - w.string(obj.key) + w.string(obj.key ?? '') } if (obj.value != null) { @@ -339,7 +339,7 @@ export namespace MapTypes { return _codec } - export const encode = (obj: MapTypes$messageMapEntry): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, MapTypes$messageMapEntry.codec()) } @@ -443,7 +443,7 @@ export namespace MapTypes { return _codec } - export const encode = (obj: MapTypes): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, MapTypes.codec()) } diff --git a/packages/protons/test/fixtures/noise.ts b/packages/protons/test/fixtures/noise.ts index 86e0417..204fef0 100644 --- a/packages/protons/test/fixtures/noise.ts +++ b/packages/protons/test/fixtures/noise.ts @@ -29,17 +29,17 @@ export namespace pb { if (opts.writeDefaults === true || (obj.identityKey != null && obj.identityKey.byteLength > 0)) { w.uint32(10) - w.bytes(obj.identityKey) + w.bytes(obj.identityKey ?? new Uint8Array(0)) } if (opts.writeDefaults === true || (obj.identitySig != null && obj.identitySig.byteLength > 0)) { w.uint32(18) - w.bytes(obj.identitySig) + w.bytes(obj.identitySig ?? new Uint8Array(0)) } if (opts.writeDefaults === true || (obj.data != null && obj.data.byteLength > 0)) { w.uint32(26) - w.bytes(obj.data) + w.bytes(obj.data ?? new Uint8Array(0)) } if (opts.lengthDelimited !== false) { @@ -80,7 +80,7 @@ export namespace pb { return _codec } - export const encode = (obj: NoiseHandshakePayload): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, NoiseHandshakePayload.codec()) } @@ -123,7 +123,7 @@ export namespace pb { return _codec } - export const encode = (obj: pb): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, pb.codec()) } diff --git a/packages/protons/test/fixtures/optional.ts b/packages/protons/test/fixtures/optional.ts index 91ff353..a882080 100644 --- a/packages/protons/test/fixtures/optional.ts +++ b/packages/protons/test/fixtures/optional.ts @@ -81,7 +81,7 @@ export namespace OptionalSubMessage { return _codec } - export const encode = (obj: OptionalSubMessage): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, OptionalSubMessage.codec()) } @@ -283,7 +283,7 @@ export namespace Optional { return _codec } - export const encode = (obj: Optional): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Optional.codec()) } diff --git a/packages/protons/test/fixtures/peer.ts b/packages/protons/test/fixtures/peer.ts index a55f9eb..0911b18 100644 --- a/packages/protons/test/fixtures/peer.ts +++ b/packages/protons/test/fixtures/peer.ts @@ -105,7 +105,7 @@ export namespace Peer { return _codec } - export const encode = (obj: Peer): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Peer.codec()) } @@ -131,7 +131,7 @@ export namespace Address { if (opts.writeDefaults === true || (obj.multiaddr != null && obj.multiaddr.byteLength > 0)) { w.uint32(10) - w.bytes(obj.multiaddr) + w.bytes(obj.multiaddr ?? new Uint8Array(0)) } if (obj.isCertified != null) { @@ -172,7 +172,7 @@ export namespace Address { return _codec } - export const encode = (obj: Address): Uint8Array => { + export const encode = (obj: Partial
): Uint8Array => { return encodeMessage(obj, Address.codec()) } @@ -196,14 +196,14 @@ export namespace Metadata { w.fork() } - if (opts.writeDefaults === true || obj.key !== '') { + if (opts.writeDefaults === true || (obj.key != null && obj.key !== '')) { w.uint32(10) - w.string(obj.key) + w.string(obj.key ?? '') } if (opts.writeDefaults === true || (obj.value != null && obj.value.byteLength > 0)) { w.uint32(18) - w.bytes(obj.value) + w.bytes(obj.value ?? new Uint8Array(0)) } if (opts.lengthDelimited !== false) { @@ -240,7 +240,7 @@ export namespace Metadata { return _codec } - export const encode = (obj: Metadata): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Metadata.codec()) } diff --git a/packages/protons/test/fixtures/singular.ts b/packages/protons/test/fixtures/singular.ts index ff3a65d..2342c3a 100644 --- a/packages/protons/test/fixtures/singular.ts +++ b/packages/protons/test/fixtures/singular.ts @@ -40,14 +40,14 @@ export namespace SingularSubMessage { w.fork() } - if (opts.writeDefaults === true || obj.foo !== '') { + if (opts.writeDefaults === true || (obj.foo != null && obj.foo !== '')) { w.uint32(10) - w.string(obj.foo) + w.string(obj.foo ?? '') } - if (opts.writeDefaults === true || obj.bar !== 0) { + if (opts.writeDefaults === true || (obj.bar != null && obj.bar !== 0)) { w.uint32(16) - w.int32(obj.bar) + w.int32(obj.bar ?? 0) } if (opts.lengthDelimited !== false) { @@ -84,7 +84,7 @@ export namespace SingularSubMessage { return _codec } - export const encode = (obj: SingularSubMessage): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, SingularSubMessage.codec()) } @@ -123,84 +123,84 @@ export namespace Singular { w.fork() } - if (opts.writeDefaults === true || obj.double !== 0) { + if (opts.writeDefaults === true || (obj.double != null && obj.double !== 0)) { w.uint32(9) - w.double(obj.double) + w.double(obj.double ?? 0) } - if (opts.writeDefaults === true || obj.float !== 0) { + if (opts.writeDefaults === true || (obj.float != null && obj.float !== 0)) { w.uint32(21) - w.float(obj.float) + w.float(obj.float ?? 0) } - if (opts.writeDefaults === true || obj.int32 !== 0) { + if (opts.writeDefaults === true || (obj.int32 != null && obj.int32 !== 0)) { w.uint32(24) - w.int32(obj.int32) + w.int32(obj.int32 ?? 0) } - if (opts.writeDefaults === true || obj.int64 !== 0n) { + if (opts.writeDefaults === true || (obj.int64 != null && obj.int64 !== 0n)) { w.uint32(32) - w.int64(obj.int64) + w.int64(obj.int64 ?? 0n) } - if (opts.writeDefaults === true || obj.uint32 !== 0) { + if (opts.writeDefaults === true || (obj.uint32 != null && obj.uint32 !== 0)) { w.uint32(40) - w.uint32(obj.uint32) + w.uint32(obj.uint32 ?? 0) } - if (opts.writeDefaults === true || obj.uint64 !== 0n) { + if (opts.writeDefaults === true || (obj.uint64 != null && obj.uint64 !== 0n)) { w.uint32(48) - w.uint64(obj.uint64) + w.uint64(obj.uint64 ?? 0n) } - if (opts.writeDefaults === true || obj.sint32 !== 0) { + if (opts.writeDefaults === true || (obj.sint32 != null && obj.sint32 !== 0)) { w.uint32(56) - w.sint32(obj.sint32) + w.sint32(obj.sint32 ?? 0) } - if (opts.writeDefaults === true || obj.sint64 !== 0n) { + if (opts.writeDefaults === true || (obj.sint64 != null && obj.sint64 !== 0n)) { w.uint32(64) - w.sint64(obj.sint64) + w.sint64(obj.sint64 ?? 0n) } - if (opts.writeDefaults === true || obj.fixed32 !== 0) { + if (opts.writeDefaults === true || (obj.fixed32 != null && obj.fixed32 !== 0)) { w.uint32(77) - w.fixed32(obj.fixed32) + w.fixed32(obj.fixed32 ?? 0) } - if (opts.writeDefaults === true || obj.fixed64 !== 0n) { + if (opts.writeDefaults === true || (obj.fixed64 != null && obj.fixed64 !== 0n)) { w.uint32(81) - w.fixed64(obj.fixed64) + w.fixed64(obj.fixed64 ?? 0n) } - if (opts.writeDefaults === true || obj.sfixed32 !== 0) { + if (opts.writeDefaults === true || (obj.sfixed32 != null && obj.sfixed32 !== 0)) { w.uint32(93) - w.sfixed32(obj.sfixed32) + w.sfixed32(obj.sfixed32 ?? 0) } - if (opts.writeDefaults === true || obj.sfixed64 !== 0n) { + if (opts.writeDefaults === true || (obj.sfixed64 != null && obj.sfixed64 !== 0n)) { w.uint32(97) - w.sfixed64(obj.sfixed64) + w.sfixed64(obj.sfixed64 ?? 0n) } - if (opts.writeDefaults === true || obj.bool !== false) { + if (opts.writeDefaults === true || (obj.bool != null && obj.bool !== false)) { w.uint32(104) - w.bool(obj.bool) + w.bool(obj.bool ?? false) } - if (opts.writeDefaults === true || obj.string !== '') { + if (opts.writeDefaults === true || (obj.string != null && obj.string !== '')) { w.uint32(114) - w.string(obj.string) + w.string(obj.string ?? '') } if (opts.writeDefaults === true || (obj.bytes != null && obj.bytes.byteLength > 0)) { w.uint32(122) - w.bytes(obj.bytes) + w.bytes(obj.bytes ?? new Uint8Array(0)) } if (opts.writeDefaults === true || (obj.enum != null && __SingularEnumValues[obj.enum] !== 0)) { w.uint32(128) - SingularEnum.codec().encode(obj.enum, w) + SingularEnum.codec().encode(obj.enum ?? SingularEnum.NO_VALUE, w) } if (obj.subMessage != null) { @@ -303,7 +303,7 @@ export namespace Singular { return _codec } - export const encode = (obj: Singular): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, Singular.codec()) } diff --git a/packages/protons/test/fixtures/test.ts b/packages/protons/test/fixtures/test.ts index 670d7c5..05849d8 100644 --- a/packages/protons/test/fixtures/test.ts +++ b/packages/protons/test/fixtures/test.ts @@ -37,9 +37,9 @@ export namespace SubMessage { w.fork() } - if (opts.writeDefaults === true || obj.foo !== '') { + if (opts.writeDefaults === true || (obj.foo != null && obj.foo !== '')) { w.uint32(10) - w.string(obj.foo) + w.string(obj.foo ?? '') } if (opts.lengthDelimited !== false) { @@ -72,7 +72,7 @@ export namespace SubMessage { return _codec } - export const encode = (obj: SubMessage): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, SubMessage.codec()) } @@ -287,7 +287,7 @@ export namespace AllTheTypes { return _codec } - export const encode = (obj: AllTheTypes): Uint8Array => { + export const encode = (obj: Partial): Uint8Array => { return encodeMessage(obj, AllTheTypes.codec()) } diff --git a/packages/protons/test/index.spec.ts b/packages/protons/test/index.spec.ts index 7ad8c56..ebda6da 100644 --- a/packages/protons/test/index.spec.ts +++ b/packages/protons/test/index.spec.ts @@ -103,6 +103,7 @@ function normalizeProtonbufjs (obj: any, target: any): any { interface TestEncodingOptions { compareBytes?: boolean comparePbjs?: boolean + outputObject?: any } /** @@ -111,32 +112,33 @@ interface TestEncodingOptions { * 1. the generated bytes between protons, pbjs and protobuf.js are the same * 2. protons and protobuf.js agree on deserialization */ -function testEncodings (obj: any, protons: any, proto: string, typeName: string, opts: TestEncodingOptions = {}): void { +function testEncodings (inputObject: any, protons: any, proto: string, typeName: string, opts: TestEncodingOptions = {}): void { + const outputObject = opts.outputObject ?? inputObject const pbjsSchema = pbjs.parseSchema(fs.readFileSync(proto, 'utf-8')).compile() - const pbjsBuf = pbjsSchema[`encode${typeName}`](longifyBigInts(obj)) + const pbjsBuf = pbjsSchema[`encode${typeName}`](longifyBigInts(inputObject)) const protobufJsSchema = protobufjs.loadSync(proto).lookupType(typeName) - const protobufJsBuf = protobufJsSchema.encode(protobufJsSchema.fromObject(longifyBigInts(obj))).finish() + const protobufJsBuf = protobufJsSchema.encode(protobufJsSchema.fromObject(longifyBigInts(inputObject))).finish() - const encoded = protons.encode(obj) + const encoded = protons.encode(inputObject) if (opts.compareBytes !== false) { expect(encoded).to.equalBytes(pbjsBuf) expect(encoded).to.equalBytes(protobufJsBuf) } - expect(protons.decode(encoded)).to.deep.equal(obj) - expect(protons.decode(pbjsBuf)).to.deep.equal(obj) - expect(protons.decode(protobufJsBuf)).to.deep.equal(obj) + expect(protons.decode(encoded)).to.deep.equal(outputObject) + expect(protons.decode(pbjsBuf)).to.deep.equal(outputObject) + expect(protons.decode(protobufJsBuf)).to.deep.equal(outputObject) if (opts.comparePbjs !== false) { - expect(normalizePbjs(pbjsSchema[`decode${typeName}`](encoded), obj)).to.deep.equal(obj) + expect(normalizePbjs(pbjsSchema[`decode${typeName}`](encoded), inputObject)).to.deep.equal(outputObject) } expect(normalizeProtonbufjs(protobufJsSchema.toObject(protobufJsSchema.decode(encoded), { enums: String, defaults: true - }), obj)).to.deep.equal(obj) + }), inputObject)).to.deep.equal(outputObject) } describe('encode', () => { @@ -384,6 +386,89 @@ describe('encode', () => { }) }) + it('does not require singular field values when set to defaults', () => { + const inputObject = {} + const outputObject: Singular = { + double: 0, + float: 0, + int32: 0, + int64: 0n, + uint32: 0, + uint64: 0n, + sint32: 0, + sint64: 0n, + fixed32: 0, + fixed64: 0n, + sfixed32: 0, + sfixed64: 0n, + bool: false, + string: '', + bytes: new Uint8Array(0), + enum: SingularEnum.NO_VALUE + } + + testEncodings(inputObject, Singular, './test/fixtures/singular.proto', 'Singular', { + // protobuf.js writes default values for singular fields - https://github.com/protobufjs/protobuf.js/issues/1822 + compareBytes: false, + // pbjs does not set default values when values are not present on the wire + comparePbjs: false, + // output object should have default values set + outputObject + }) + }) + + it('does not write singular field values when defaults are omitted', () => { + const buf = Singular.encode({}) + + expect(buf.byteLength).to.equal(0, 'wrote default values for singular fields') + + const outputObject: Singular = { + double: 0, + float: 0, + int32: 0, + int64: 0n, + uint32: 0, + uint64: 0n, + sint32: 0, + sint64: 0n, + fixed32: 0, + fixed64: 0n, + sfixed32: 0, + sfixed64: 0n, + bool: false, + string: '', + bytes: new Uint8Array(0), + enum: SingularEnum.NO_VALUE + } + + expect(Singular.decode(buf)).to.deep.equal(outputObject) + }) + + it('does not write singular field values when set to defaults', () => { + const objectWithDefaults: Singular = { + double: 0, + float: 0, + int32: 0, + int64: 0n, + uint32: 0, + uint64: 0n, + sint32: 0, + sint64: 0n, + fixed32: 0, + fixed64: 0n, + sfixed32: 0, + sfixed64: 0n, + bool: false, + string: '', + bytes: new Uint8Array(0), + enum: SingularEnum.NO_VALUE + } + + const buf = Singular.encode(objectWithDefaults) + expect(buf.byteLength).to.equal(0, 'wrote default values for singular fields') + expect(Singular.decode(buf)).to.deep.equal(objectWithDefaults) + }) + it('writes singular field values when not set to defaults', () => { const obj: Singular = { double: 1.0,