diff --git a/README.markdown b/README.markdown index b8b7fb353..b0428896f 100644 --- a/README.markdown +++ b/README.markdown @@ -551,6 +551,8 @@ Generated code will be placed in the Gradle build directory. - With `--ts_proto_opt=comments=false`, comments won't be copied from the proto files to the generated code. +- With `--ts_proto_opt=bigIntLiteral=false`, the generated code will use `BigInt("0")` instead of `0n` for BigInt literals. BigInt literals aren't supported by TypeScript when the "target" compiler option set to something older than "ES2020". + - With `--ts_proto_opt=useNullAsOptional=true`, `undefined` values will be converted to `null`, and if you use `optional` label in your `.proto` file, the field will have `undefined` type as well. for example: ```protobuf @@ -603,7 +605,7 @@ export interface User { } ``` -- With `--ts_proto_opt=noDefaultsForOptionals=true`, `undefined` primitive values will not be defaulted as per the protobuf spec. Additionally unlike the standard behavior, when a field is set to it's standard default value, it *will* be encoded allowing it to be sent over the wire and distinguished from undefined values. For example if a message does not set a boolean value, ordinarily this would be defaulted to `false` which is different to it being undefined. +- With `--ts_proto_opt=noDefaultsForOptionals=true`, `undefined` primitive values will not be defaulted as per the protobuf spec. Additionally unlike the standard behavior, when a field is set to it's standard default value, it *will* be encoded allowing it to be sent over the wire and distinguished from undefined values. For example if a message does not set a boolean value, ordinarily this would be defaulted to `false` which is different to it being undefined. This option allows the library to act in a compatible way with the [Wire implementation](https://square.github.io/wire/) maintained and used by Square/Block. Note: this option should only be used in combination with other client/server code generated using Wire or ts-proto with this option enabled. diff --git a/integration/extension-import/tsconfig.json b/integration/extension-import/tsconfig.json index 3fac580ac..6003b4a6b 100644 --- a/integration/extension-import/tsconfig.json +++ b/integration/extension-import/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": ["es2018"], "module": "commonjs", "strict": true, diff --git a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts index 8ead513d0..f83ab7476 100644 --- a/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts +++ b/integration/fieldoption-jstype-with-forcelong-bigint/fieldoption-jstype-with-forcelong-bigint.ts @@ -14,12 +14,12 @@ export interface FieldOption { } function createBaseFieldOption(): FieldOption { - return { normalField: BigInt("0"), numberField: 0, stringField: "0" }; + return { normalField: 0n, numberField: 0, stringField: "0" }; } export const FieldOption = { encode(message: FieldOption, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.normalField !== BigInt("0")) { + if (message.normalField !== 0n) { if (BigInt.asIntN(64, message.normalField) !== message.normalField) { throw new globalThis.Error("value provided for field message.normalField of type int64 too large"); } @@ -79,7 +79,7 @@ export const FieldOption = { fromJSON(object: any): FieldOption { return { - normalField: isSet(object.normalField) ? BigInt(object.normalField) : BigInt("0"), + normalField: isSet(object.normalField) ? BigInt(object.normalField) : 0n, numberField: isSet(object.numberField) ? globalThis.Number(object.numberField) : 0, stringField: isSet(object.stringField) ? globalThis.String(object.stringField) : "0", }; @@ -87,7 +87,7 @@ export const FieldOption = { toJSON(message: FieldOption): unknown { const obj: any = {}; - if (message.normalField !== BigInt("0")) { + if (message.normalField !== 0n) { obj.normalField = message.normalField.toString(); } if (message.numberField !== 0) { @@ -104,7 +104,7 @@ export const FieldOption = { }, fromPartial, I>>(object: I): FieldOption { const message = createBaseFieldOption(); - message.normalField = object.normalField ?? BigInt("0"); + message.normalField = object.normalField ?? 0n; message.numberField = object.numberField ?? 0; message.stringField = object.stringField ?? "0"; return message; diff --git a/integration/map-bigint-optional/test.ts b/integration/map-bigint-optional/test.ts index 7a3b541d8..ba5bde4bf 100644 --- a/integration/map-bigint-optional/test.ts +++ b/integration/map-bigint-optional/test.ts @@ -127,18 +127,18 @@ export const MapBigInt = { }; function createBaseMapBigInt_MapEntry(): MapBigInt_MapEntry { - return { key: BigInt("0"), value: BigInt("0") }; + return { key: 0n, value: 0n }; } export const MapBigInt_MapEntry = { encode(message: MapBigInt_MapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.key !== BigInt("0")) { + if (message.key !== 0n) { if (BigInt.asUintN(64, message.key) !== message.key) { throw new globalThis.Error("value provided for field message.key of type fixed64 too large"); } writer.uint32(9).fixed64(message.key.toString()); } - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { if (BigInt.asIntN(64, message.value) !== message.value) { throw new globalThis.Error("value provided for field message.value of type int64 too large"); } @@ -205,18 +205,15 @@ export const MapBigInt_MapEntry = { }, fromJSON(object: any): MapBigInt_MapEntry { - return { - key: isSet(object.key) ? BigInt(object.key) : BigInt("0"), - value: isSet(object.value) ? BigInt(object.value) : BigInt("0"), - }; + return { key: isSet(object.key) ? BigInt(object.key) : 0n, value: isSet(object.value) ? BigInt(object.value) : 0n }; }, toJSON(message: MapBigInt_MapEntry): unknown { const obj: any = {}; - if (message.key !== BigInt("0")) { + if (message.key !== 0n) { obj.key = message.key.toString(); } - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { obj.value = message.value.toString(); } return obj; @@ -227,8 +224,8 @@ export const MapBigInt_MapEntry = { }, fromPartial, I>>(object: I): MapBigInt_MapEntry { const message = createBaseMapBigInt_MapEntry(); - message.key = object.key ?? BigInt("0"); - message.value = object.value ?? BigInt("0"); + message.key = object.key ?? 0n; + message.value = object.value ?? 0n; return message; }, }; diff --git a/integration/pbjs.sh b/integration/pbjs.sh index d6051ec80..82ce604a3 100755 --- a/integration/pbjs.sh +++ b/integration/pbjs.sh @@ -50,6 +50,11 @@ if match "simple-long-bigint"; then yarn run pbts --no-comments -o integration/simple-long-bigint/pbjs.d.ts integration/simple-long-bigint/pbjs.js fi +if match "simple-long-bigint-noliteral"; then + yarn run pbjs --force-message --force-long -t static-module -o integration/simple-long-bigint-noliteral/pbjs.js integration/simple-long-bigint-noliteral/simple.proto + yarn run pbts --no-comments -o integration/simple-long-bigint-noliteral/pbjs.d.ts integration/simple-long-bigint-noliteral/pbjs.js +fi + if match "vector-tile"; then yarn run pbjs --force-message --force-number -t static-module -o integration/vector-tile/pbjs.js integration/vector-tile/vector_tile.proto yarn run pbts --no-comments -o integration/vector-tile/pbjs.d.ts integration/vector-tile/pbjs.js diff --git a/integration/simple-esmodule-interop/tsconfig.json b/integration/simple-esmodule-interop/tsconfig.json index 3fac580ac..6003b4a6b 100644 --- a/integration/simple-esmodule-interop/tsconfig.json +++ b/integration/simple-esmodule-interop/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": ["es2018"], "module": "commonjs", "strict": true, diff --git a/integration/simple-long-bigint-noliteral/google/protobuf/timestamp.ts b/integration/simple-long-bigint-noliteral/google/protobuf/timestamp.ts new file mode 100644 index 000000000..1760585bb --- /dev/null +++ b/integration/simple-long-bigint-noliteral/google/protobuf/timestamp.ts @@ -0,0 +1,217 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: google/protobuf/timestamp.proto + +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("long"); + +export const protobufPackage = "google.protobuf"; + +/** + * A Timestamp represents a point in time independent of any time zone or local + * calendar, encoded as a count of seconds and fractions of seconds at + * nanosecond resolution. The count is relative to an epoch at UTC midnight on + * January 1, 1970, in the proleptic Gregorian calendar which extends the + * Gregorian calendar backwards to year one. + * + * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap + * second table is needed for interpretation, using a [24-hour linear + * smear](https://developers.google.com/time/smear). + * + * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By + * restricting to that range, we ensure that we can convert to and from [RFC + * 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. + * + * # Examples + * + * Example 1: Compute Timestamp from POSIX `time()`. + * + * Timestamp timestamp; + * timestamp.set_seconds(time(NULL)); + * timestamp.set_nanos(0); + * + * Example 2: Compute Timestamp from POSIX `gettimeofday()`. + * + * struct timeval tv; + * gettimeofday(&tv, NULL); + * + * Timestamp timestamp; + * timestamp.set_seconds(tv.tv_sec); + * timestamp.set_nanos(tv.tv_usec * 1000); + * + * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. + * + * FILETIME ft; + * GetSystemTimeAsFileTime(&ft); + * UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + * + * // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z + * // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. + * Timestamp timestamp; + * timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); + * timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); + * + * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. + * + * long millis = System.currentTimeMillis(); + * + * Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) + * .setNanos((int) ((millis % 1000) * 1000000)).build(); + * + * Example 5: Compute Timestamp from Java `Instant.now()`. + * + * Instant now = Instant.now(); + * + * Timestamp timestamp = + * Timestamp.newBuilder().setSeconds(now.getEpochSecond()) + * .setNanos(now.getNano()).build(); + * + * Example 6: Compute Timestamp from current time in Python. + * + * timestamp = Timestamp() + * timestamp.GetCurrentTime() + * + * # JSON Mapping + * + * In JSON format, the Timestamp type is encoded as a string in the + * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the + * format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" + * where {year} is always expressed using four digits while {month}, {day}, + * {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional + * seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), + * are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone + * is required. A proto3 JSON serializer should always use UTC (as indicated by + * "Z") when printing the Timestamp type and a proto3 JSON parser should be + * able to accept both UTC and other timezones (as indicated by an offset). + * + * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past + * 01:30 UTC on January 15, 2017. + * + * In JavaScript, one can convert a Date object to this format using the + * standard + * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) + * method. In Python, a standard `datetime.datetime` object can be converted + * to this format using + * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with + * the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use + * the Joda Time's [`ISODateTimeFormat.dateTime()`]( + * http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D + * ) to obtain a formatter capable of generating timestamps in this format. + */ +export interface Timestamp { + /** + * Represents seconds of UTC time since Unix epoch + * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to + * 9999-12-31T23:59:59Z inclusive. + */ + seconds: bigint; + /** + * Non-negative fractions of a second at nanosecond resolution. Negative + * second values with fractions must still have non-negative nanos values + * that count forward in time. Must be from 0 to 999,999,999 + * inclusive. + */ + nanos: number; +} + +function createBaseTimestamp(): Timestamp { + return { seconds: BigInt("0"), nanos: 0 }; +} + +export const Timestamp = { + encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.seconds !== BigInt("0")) { + if (BigInt.asIntN(64, message.seconds) !== message.seconds) { + throw new globalThis.Error("value provided for field message.seconds of type int64 too large"); + } + writer.uint32(8).int64(message.seconds.toString()); + } + if (message.nanos !== 0) { + writer.uint32(16).int32(message.nanos); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Timestamp { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTimestamp(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.seconds = longToBigint(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.nanos = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Timestamp { + return { + seconds: isSet(object.seconds) ? BigInt(object.seconds) : BigInt("0"), + nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0, + }; + }, + + toJSON(message: Timestamp): unknown { + const obj: any = {}; + if (message.seconds !== BigInt("0")) { + obj.seconds = message.seconds.toString(); + } + if (message.nanos !== 0) { + obj.nanos = Math.round(message.nanos); + } + return obj; + }, + + create, I>>(base?: I): Timestamp { + return Timestamp.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Timestamp { + const message = createBaseTimestamp(); + message.seconds = object.seconds ?? BigInt("0"); + message.nanos = object.nanos ?? 0; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToBigint(long: Long) { + return BigInt(long.toString()); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.proto b/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.proto new file mode 100644 index 000000000..01947639a --- /dev/null +++ b/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.proto @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Wrappers for primitive (non-message) types. These types are useful +// for embedding primitives in the `google.protobuf.Any` type and for places +// where we need to distinguish between the absence of a primitive +// typed field and its default value. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; +option go_package = "github.com/golang/protobuf/ptypes/wrappers"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "WrappersProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; + +// Wrapper message for `double`. +// +// The JSON representation for `DoubleValue` is JSON number. +message DoubleValue { + // The double value. + double value = 1; +} + +// Wrapper message for `float`. +// +// The JSON representation for `FloatValue` is JSON number. +message FloatValue { + // The float value. + float value = 1; +} + +// Wrapper message for `int64`. +// +// The JSON representation for `Int64Value` is JSON string. +message Int64Value { + // The int64 value. + int64 value = 1; +} + +// Wrapper message for `uint64`. +// +// The JSON representation for `UInt64Value` is JSON string. +message UInt64Value { + // The uint64 value. + uint64 value = 1; +} + +// Wrapper message for `int32`. +// +// The JSON representation for `Int32Value` is JSON number. +message Int32Value { + // The int32 value. + int32 value = 1; +} + +// Wrapper message for `uint32`. +// +// The JSON representation for `UInt32Value` is JSON number. +message UInt32Value { + // The uint32 value. + uint32 value = 1; +} + +// Wrapper message for `bool`. +// +// The JSON representation for `BoolValue` is JSON `true` and `false`. +message BoolValue { + // The bool value. + bool value = 1; +} + +// Wrapper message for `string`. +// +// The JSON representation for `StringValue` is JSON string. +message StringValue { + // The string value. + string value = 1; +} + +// Wrapper message for `bytes`. +// +// The JSON representation for `BytesValue` is JSON string. +message BytesValue { + // The bytes value. + bytes value = 1; +} diff --git a/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.ts b/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.ts new file mode 100644 index 000000000..97fd120cc --- /dev/null +++ b/integration/simple-long-bigint-noliteral/google/protobuf/wrappers.ts @@ -0,0 +1,667 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: google/protobuf/wrappers.proto + +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import Long = require("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: bigint; +} + +/** + * Wrapper message for `uint64`. + * + * The JSON representation for `UInt64Value` is JSON string. + */ +export interface UInt64Value { + /** The uint64 value. */ + value: bigint; +} + +/** + * 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; +} + +function createBaseDoubleValue(): DoubleValue { + return { value: 0 }; +} + +export const DoubleValue = { + encode(message: DoubleValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(9).double(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): DoubleValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseDoubleValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.value = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): DoubleValue { + return { value: isSet(object.value) ? globalThis.Number(object.value) : 0 }; + }, + + toJSON(message: DoubleValue): unknown { + const obj: any = {}; + if (message.value !== 0) { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): DoubleValue { + return DoubleValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): DoubleValue { + const message = createBaseDoubleValue(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseFloatValue(): FloatValue { + return { value: 0 }; +} + +export const FloatValue = { + encode(message: FloatValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(13).float(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): FloatValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseFloatValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.value = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): FloatValue { + return { value: isSet(object.value) ? globalThis.Number(object.value) : 0 }; + }, + + toJSON(message: FloatValue): unknown { + const obj: any = {}; + if (message.value !== 0) { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): FloatValue { + return FloatValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): FloatValue { + const message = createBaseFloatValue(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseInt64Value(): Int64Value { + return { value: BigInt("0") }; +} + +export const Int64Value = { + encode(message: Int64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== BigInt("0")) { + if (BigInt.asIntN(64, message.value) !== message.value) { + throw new globalThis.Error("value provided for field message.value of type int64 too large"); + } + writer.uint32(8).int64(message.value.toString()); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Int64Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseInt64Value(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = longToBigint(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Int64Value { + return { value: isSet(object.value) ? BigInt(object.value) : BigInt("0") }; + }, + + toJSON(message: Int64Value): unknown { + const obj: any = {}; + if (message.value !== BigInt("0")) { + obj.value = message.value.toString(); + } + return obj; + }, + + create, I>>(base?: I): Int64Value { + return Int64Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Int64Value { + const message = createBaseInt64Value(); + message.value = object.value ?? BigInt("0"); + return message; + }, +}; + +function createBaseUInt64Value(): UInt64Value { + return { value: BigInt("0") }; +} + +export const UInt64Value = { + encode(message: UInt64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== BigInt("0")) { + if (BigInt.asUintN(64, message.value) !== message.value) { + throw new globalThis.Error("value provided for field message.value of type uint64 too large"); + } + writer.uint32(8).uint64(message.value.toString()); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UInt64Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUInt64Value(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = longToBigint(reader.uint64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UInt64Value { + return { value: isSet(object.value) ? BigInt(object.value) : BigInt("0") }; + }, + + toJSON(message: UInt64Value): unknown { + const obj: any = {}; + if (message.value !== BigInt("0")) { + obj.value = message.value.toString(); + } + return obj; + }, + + create, I>>(base?: I): UInt64Value { + return UInt64Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UInt64Value { + const message = createBaseUInt64Value(); + message.value = object.value ?? BigInt("0"); + return message; + }, +}; + +function createBaseInt32Value(): Int32Value { + return { value: 0 }; +} + +export const Int32Value = { + encode(message: Int32Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(8).int32(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Int32Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseInt32Value(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Int32Value { + return { value: isSet(object.value) ? globalThis.Number(object.value) : 0 }; + }, + + toJSON(message: Int32Value): unknown { + const obj: any = {}; + if (message.value !== 0) { + obj.value = Math.round(message.value); + } + return obj; + }, + + create, I>>(base?: I): Int32Value { + return Int32Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Int32Value { + const message = createBaseInt32Value(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseUInt32Value(): UInt32Value { + return { value: 0 }; +} + +export const UInt32Value = { + encode(message: UInt32Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(8).uint32(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UInt32Value { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUInt32Value(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): UInt32Value { + return { value: isSet(object.value) ? globalThis.Number(object.value) : 0 }; + }, + + toJSON(message: UInt32Value): unknown { + const obj: any = {}; + if (message.value !== 0) { + obj.value = Math.round(message.value); + } + return obj; + }, + + create, I>>(base?: I): UInt32Value { + return UInt32Value.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): UInt32Value { + const message = createBaseUInt32Value(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseBoolValue(): BoolValue { + return { value: false }; +} + +export const BoolValue = { + encode(message: BoolValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== false) { + writer.uint32(8).bool(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BoolValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBoolValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BoolValue { + return { value: isSet(object.value) ? globalThis.Boolean(object.value) : false }; + }, + + toJSON(message: BoolValue): unknown { + const obj: any = {}; + if (message.value !== false) { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): BoolValue { + return BoolValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): BoolValue { + const message = createBaseBoolValue(); + message.value = object.value ?? false; + return message; + }, +}; + +function createBaseStringValue(): StringValue { + return { value: "" }; +} + +export const StringValue = { + encode(message: StringValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== "") { + writer.uint32(10).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): StringValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseStringValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.value = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): StringValue { + return { value: isSet(object.value) ? globalThis.String(object.value) : "" }; + }, + + toJSON(message: StringValue): unknown { + const obj: any = {}; + if (message.value !== "") { + obj.value = message.value; + } + return obj; + }, + + create, I>>(base?: I): StringValue { + return StringValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): StringValue { + const message = createBaseStringValue(); + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseBytesValue(): BytesValue { + return { value: new Uint8Array(0) }; +} + +export const BytesValue = { + encode(message: BytesValue, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value.length !== 0) { + writer.uint32(10).bytes(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BytesValue { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBytesValue(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.value = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BytesValue { + return { value: isSet(object.value) ? bytesFromBase64(object.value) : new Uint8Array(0) }; + }, + + toJSON(message: BytesValue): unknown { + const obj: any = {}; + if (message.value.length !== 0) { + obj.value = base64FromBytes(message.value); + } + return obj; + }, + + create, I>>(base?: I): BytesValue { + return BytesValue.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): BytesValue { + const message = createBaseBytesValue(); + message.value = object.value ?? new Uint8Array(0); + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToBigint(long: Long) { + return BigInt(long.toString()); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/simple-long-bigint-noliteral/numbers-long-string-test.ts b/integration/simple-long-bigint-noliteral/numbers-long-string-test.ts new file mode 100644 index 000000000..1f6e6203f --- /dev/null +++ b/integration/simple-long-bigint-noliteral/numbers-long-string-test.ts @@ -0,0 +1,224 @@ +import { Reader } from "protobufjs"; +import Long = require("long"); +import { Numbers } from "./simple"; +import { simple as pbjs, google } from "./pbjs"; +import INumbers = pbjs.INumbers; +import PbNumbers = pbjs.Numbers; +import UInt64Value = google.protobuf.UInt64Value; +import PbTimestamp = google.protobuf.Timestamp; + + +// 18_446_744_073_709_551_615n +const MAX_UINT64 = BigInt("18446744073709551615") +// 9_223_372_036_854_775_807n +const MAX_INT64 = BigInt("9223372036854775807") + +describe("number", () => { + it("generates types correctly", () => { + const simple: Numbers = { + double: 1, + float: 2, + int32: 3, + int64: BigInt("4"), + uint32: 5, + uint64: BigInt("6"), + sint32: 7, + sint64: BigInt("8"), + fixed32: 9, + fixed64: BigInt("10"), + sfixed32: 11, + sfixed64: BigInt("12"), + guint64: BigInt("13"), + timestamp: new Date("1970-02-03T04:05:06.0071Z"), + uint64s: [BigInt("14"), BigInt("15"), BigInt("16")], + }; + expect(simple.int64).toEqual(BigInt("4")); + expect(simple.uint64).toEqual(BigInt("6")); + expect(simple.guint64).toEqual(BigInt("13")); + expect(simple.timestamp).toEqual(new Date("1970-02-03T04:05:06.0071Z")); + }); + + it("can decode", () => { + const s1: INumbers = { + double: 1, + float: 2, + int32: 3, + int64: Long.fromNumber(4), + uint32: 5, + uint64: Long.fromNumber(6, true), + sint32: 7, + sint64: Long.fromNumber(8), + fixed32: 9, + fixed64: Long.fromNumber(10, true), + sfixed32: 11, + sfixed64: Long.fromNumber(12), + guint64: new UInt64Value({ value: Long.fromNumber(13, true) }), + timestamp: new PbTimestamp({ + nanos: 7000001, + seconds: Long.fromNumber(1), + }), + uint64s: [Long.fromNumber(14), Long.fromNumber(15), Long.fromNumber(16)], + }; + const expected: Numbers = { + double: 1, + float: 2, + int32: 3, + int64: BigInt("4"), + uint32: 5, + uint64: BigInt("6"), + sint32: 7, + sint64: BigInt("8"), + fixed32: 9, + fixed64: BigInt("10"), + sfixed32: 11, + sfixed64: BigInt("12"), + guint64: BigInt("13"), + timestamp: new Date("1970-01-01T00:00:01.007000001Z"), + uint64s: [BigInt("14"), BigInt("15"), BigInt("16")], + }; + const s2 = Numbers.decode(Reader.create(PbNumbers.encode(PbNumbers.fromObject(s1)).finish())); + expect(s2).toEqual(expected); + }); + + it("can encode", () => { + const s1: Numbers = { + double: 1, + float: 2, + int32: 3, + int64: BigInt("4"), + uint32: 5, + uint64: BigInt("6"), + sint32: 7, + sint64: BigInt("8"), + fixed32: 9, + fixed64: BigInt("10"), + sfixed32: 11, + sfixed64: BigInt("12"), + guint64: BigInt("13"), + timestamp: new Date("1980-01-01T00:00:01.123Z"), + uint64s: [BigInt("14"), BigInt("15"), BigInt("16")], + }; + const expected: INumbers = { + double: 1, + float: 2, + int32: 3, + int64: Long.fromNumber(4), + uint32: 5, + uint64: Long.fromNumber(6, true), + sint32: 7, + sint64: Long.fromNumber(8), + fixed32: 9, + fixed64: Long.fromNumber(10, true), + sfixed32: 11, + sfixed64: Long.fromNumber(12), + guint64: new UInt64Value({ value: Long.fromNumber(13, true) }), + timestamp: new PbTimestamp({ + nanos: 123000000, + seconds: Long.fromNumber(315532801), + }), + uint64s: [Long.fromNumber(14, true), Long.fromNumber(15, true), Long.fromNumber(16, true)], + }; + const s2 = PbNumbers.toObject(PbNumbers.decode(Numbers.encode(s1).finish())); + expect(s2).toEqual({ + ...expected, + }); + }); + + it("can decode and fallback to default values", () => { + const s1: INumbers = {}; + const s2 = Numbers.decode(Reader.create(PbNumbers.encode(PbNumbers.fromObject(s1)).finish())); + expect(s2.double).toEqual(0); + expect(s2.float).toEqual(0); + expect(s2.int32).toEqual(0); + expect(s2.int64).toEqual(BigInt("0")); + expect(s2.uint32).toEqual(0); + expect(s2.uint64).toEqual(BigInt("0")); + expect(s2.sint32).toEqual(0); + expect(s2.sint64).toEqual(BigInt("0")); + expect(s2.fixed32).toEqual(0); + expect(s2.fixed64).toEqual(BigInt("0")); + expect(s2.sfixed32).toEqual(0); + expect(s2.guint64).toEqual(undefined); + expect(s2.timestamp).toEqual(undefined); + expect(s2.uint64s).toEqual([]); + }); + + it("observes how pbjs handles null", () => { + // the types are in theory only useful for construction + const s1 = PbNumbers.fromObject({ double: null, float: 1 }); + // as after construction, they return the empty string + expect(s1.double).toEqual(0); + const s2 = PbNumbers.decode(PbNumbers.encode(s1).finish()); + expect(s2.double).toEqual(0); + }); + + it("has fromPartial", () => { + const s1 = Numbers.fromPartial({}); + expect(s1).toMatchInlineSnapshot(` + { + "double": 0, + "fixed32": 0, + "fixed64": 0n, + "float": 0, + "guint64": undefined, + "int32": 0, + "int64": 0n, + "sfixed32": 0, + "sfixed64": 0n, + "sint32": 0, + "sint64": 0n, + "timestamp": undefined, + "uint32": 0, + "uint64": 0n, + "uint64s": [], + } + `); + }); + + it('throws error on too large bigints', () => { + const testCases = [ + { + failValue: { int64: MAX_INT64 + BigInt("1") }, + failMsg: "value provided for field message.int64 of type int64 too large", + passValue: { int64: MAX_INT64 }, + }, + { + failValue: { uint64: MAX_UINT64 + BigInt("1") }, + failMsg: "value provided for field message.uint64 of type uint64 too large", + passValue: { uint64: MAX_UINT64 }, + }, + { + failValue: { sint64: MAX_INT64 + BigInt("1") }, + failMsg: "value provided for field message.sint64 of type sint64 too large", + passValue: { sint64: MAX_INT64 }, + }, + { + failValue: { fixed64: MAX_UINT64 + BigInt("1") }, + failMsg: "value provided for field message.fixed64 of type fixed64 too large", + passValue: { fixed64: MAX_UINT64 }, + }, + { + failValue: { sfixed64: MAX_INT64 + BigInt("1") }, + failMsg: "value provided for field message.sfixed64 of type sfixed64 too large", + passValue: { sfixed64: MAX_INT64 }, + }, + { + failValue: { guint64: MAX_UINT64 + BigInt("1") }, + failMsg: "value provided for field message.value of type uint64 too large", + passValue: { guint64: MAX_UINT64 }, + }, + { + failValue: { uint64s: [MAX_UINT64 + BigInt("1")] }, + failMsg: "a value provided in array field uint64s of type uint64 is too large", + passValue: { uint64s: [MAX_UINT64] }, + } + ]; + + for (const testCase of testCases) { + const failValue = Numbers.fromPartial(testCase.failValue); + expect(() => { Numbers.encode(failValue) }).toThrow(testCase.failMsg); + const passValue = Numbers.fromPartial(testCase.passValue); + expect(() => Numbers.encode(passValue).finish()).not.toThrowError(); + } + }); +}); diff --git a/integration/simple-long-bigint-noliteral/parameters.txt b/integration/simple-long-bigint-noliteral/parameters.txt new file mode 100644 index 000000000..0b72b7622 --- /dev/null +++ b/integration/simple-long-bigint-noliteral/parameters.txt @@ -0,0 +1 @@ +forceLong=bigint,bigIntLiteral=false \ No newline at end of file diff --git a/integration/simple-long-bigint-noliteral/simple.proto b/integration/simple-long-bigint-noliteral/simple.proto new file mode 100644 index 000000000..3affcfd5e --- /dev/null +++ b/integration/simple-long-bigint-noliteral/simple.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; + +package simple; + +message Numbers { + double double = 1; + float float = 2; + int32 int32 = 3; + int64 int64 = 4; + uint32 uint32 = 5; + uint64 uint64 = 6; + sint32 sint32 = 7; + sint64 sint64 = 8; + fixed32 fixed32 = 9; + fixed64 fixed64 = 10; + sfixed32 sfixed32 = 11; + sfixed64 sfixed64 = 12; + google.protobuf.UInt64Value guint64 = 13; + google.protobuf.Timestamp timestamp = 14; + repeated uint64 uint64s = 15; +} diff --git a/integration/simple-long-bigint-noliteral/simple.ts b/integration/simple-long-bigint-noliteral/simple.ts new file mode 100644 index 000000000..8e2cd6273 --- /dev/null +++ b/integration/simple-long-bigint-noliteral/simple.ts @@ -0,0 +1,390 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// source: simple.proto + +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; +import { Timestamp } from "./google/protobuf/timestamp"; +import { UInt64Value } from "./google/protobuf/wrappers"; +import Long = require("long"); + +export const protobufPackage = "simple"; + +export interface Numbers { + double: number; + float: number; + int32: number; + int64: bigint; + uint32: number; + uint64: bigint; + sint32: number; + sint64: bigint; + fixed32: number; + fixed64: bigint; + sfixed32: number; + sfixed64: bigint; + guint64: bigint | undefined; + timestamp: Date | undefined; + uint64s: bigint[]; +} + +function createBaseNumbers(): Numbers { + return { + double: 0, + float: 0, + int32: 0, + int64: BigInt("0"), + uint32: 0, + uint64: BigInt("0"), + sint32: 0, + sint64: BigInt("0"), + fixed32: 0, + fixed64: BigInt("0"), + sfixed32: 0, + sfixed64: BigInt("0"), + guint64: undefined, + timestamp: undefined, + uint64s: [], + }; +} + +export const Numbers = { + encode(message: Numbers, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.double !== 0) { + writer.uint32(9).double(message.double); + } + if (message.float !== 0) { + writer.uint32(21).float(message.float); + } + if (message.int32 !== 0) { + writer.uint32(24).int32(message.int32); + } + if (message.int64 !== BigInt("0")) { + if (BigInt.asIntN(64, message.int64) !== message.int64) { + throw new globalThis.Error("value provided for field message.int64 of type int64 too large"); + } + writer.uint32(32).int64(message.int64.toString()); + } + if (message.uint32 !== 0) { + writer.uint32(40).uint32(message.uint32); + } + if (message.uint64 !== BigInt("0")) { + if (BigInt.asUintN(64, message.uint64) !== message.uint64) { + throw new globalThis.Error("value provided for field message.uint64 of type uint64 too large"); + } + writer.uint32(48).uint64(message.uint64.toString()); + } + if (message.sint32 !== 0) { + writer.uint32(56).sint32(message.sint32); + } + if (message.sint64 !== BigInt("0")) { + if (BigInt.asIntN(64, message.sint64) !== message.sint64) { + throw new globalThis.Error("value provided for field message.sint64 of type sint64 too large"); + } + writer.uint32(64).sint64(message.sint64.toString()); + } + if (message.fixed32 !== 0) { + writer.uint32(77).fixed32(message.fixed32); + } + if (message.fixed64 !== BigInt("0")) { + if (BigInt.asUintN(64, message.fixed64) !== message.fixed64) { + throw new globalThis.Error("value provided for field message.fixed64 of type fixed64 too large"); + } + writer.uint32(81).fixed64(message.fixed64.toString()); + } + if (message.sfixed32 !== 0) { + writer.uint32(93).sfixed32(message.sfixed32); + } + if (message.sfixed64 !== BigInt("0")) { + if (BigInt.asIntN(64, message.sfixed64) !== message.sfixed64) { + throw new globalThis.Error("value provided for field message.sfixed64 of type sfixed64 too large"); + } + writer.uint32(97).sfixed64(message.sfixed64.toString()); + } + if (message.guint64 !== undefined) { + UInt64Value.encode({ value: message.guint64! }, writer.uint32(106).fork()).ldelim(); + } + if (message.timestamp !== undefined) { + Timestamp.encode(toTimestamp(message.timestamp), writer.uint32(114).fork()).ldelim(); + } + writer.uint32(122).fork(); + for (const v of message.uint64s) { + if (BigInt.asUintN(64, v) !== v) { + throw new globalThis.Error("a value provided in array field uint64s of type uint64 is too large"); + } + writer.uint64(v.toString()); + } + writer.ldelim(); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Numbers { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNumbers(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 9) { + break; + } + + message.double = reader.double(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.float = reader.float(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.int32 = reader.int32(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.int64 = longToBigint(reader.int64() as Long); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.uint32 = reader.uint32(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.uint64 = longToBigint(reader.uint64() as Long); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.sint32 = reader.sint32(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.sint64 = longToBigint(reader.sint64() as Long); + continue; + case 9: + if (tag !== 77) { + break; + } + + message.fixed32 = reader.fixed32(); + continue; + case 10: + if (tag !== 81) { + break; + } + + message.fixed64 = longToBigint(reader.fixed64() as Long); + continue; + case 11: + if (tag !== 93) { + break; + } + + message.sfixed32 = reader.sfixed32(); + continue; + case 12: + if (tag !== 97) { + break; + } + + message.sfixed64 = longToBigint(reader.sfixed64() as Long); + continue; + case 13: + if (tag !== 106) { + break; + } + + message.guint64 = UInt64Value.decode(reader, reader.uint32()).value; + continue; + case 14: + if (tag !== 114) { + break; + } + + message.timestamp = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + continue; + case 15: + if (tag === 120) { + message.uint64s.push(longToBigint(reader.uint64() as Long)); + + continue; + } + + if (tag === 122) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.uint64s.push(longToBigint(reader.uint64() as Long)); + } + + continue; + } + + break; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Numbers { + return { + double: isSet(object.double) ? globalThis.Number(object.double) : 0, + float: isSet(object.float) ? globalThis.Number(object.float) : 0, + int32: isSet(object.int32) ? globalThis.Number(object.int32) : 0, + int64: isSet(object.int64) ? BigInt(object.int64) : BigInt("0"), + uint32: isSet(object.uint32) ? globalThis.Number(object.uint32) : 0, + uint64: isSet(object.uint64) ? BigInt(object.uint64) : BigInt("0"), + sint32: isSet(object.sint32) ? globalThis.Number(object.sint32) : 0, + sint64: isSet(object.sint64) ? BigInt(object.sint64) : BigInt("0"), + fixed32: isSet(object.fixed32) ? globalThis.Number(object.fixed32) : 0, + fixed64: isSet(object.fixed64) ? BigInt(object.fixed64) : BigInt("0"), + sfixed32: isSet(object.sfixed32) ? globalThis.Number(object.sfixed32) : 0, + sfixed64: isSet(object.sfixed64) ? BigInt(object.sfixed64) : BigInt("0"), + guint64: isSet(object.guint64) ? BigInt(object.guint64) : undefined, + timestamp: isSet(object.timestamp) ? fromJsonTimestamp(object.timestamp) : undefined, + uint64s: globalThis.Array.isArray(object?.uint64s) ? object.uint64s.map((e: any) => BigInt(e)) : [], + }; + }, + + toJSON(message: Numbers): unknown { + const obj: any = {}; + if (message.double !== 0) { + obj.double = message.double; + } + if (message.float !== 0) { + obj.float = message.float; + } + if (message.int32 !== 0) { + obj.int32 = Math.round(message.int32); + } + if (message.int64 !== BigInt("0")) { + obj.int64 = message.int64.toString(); + } + if (message.uint32 !== 0) { + obj.uint32 = Math.round(message.uint32); + } + if (message.uint64 !== BigInt("0")) { + obj.uint64 = message.uint64.toString(); + } + if (message.sint32 !== 0) { + obj.sint32 = Math.round(message.sint32); + } + if (message.sint64 !== BigInt("0")) { + obj.sint64 = message.sint64.toString(); + } + if (message.fixed32 !== 0) { + obj.fixed32 = Math.round(message.fixed32); + } + if (message.fixed64 !== BigInt("0")) { + obj.fixed64 = message.fixed64.toString(); + } + if (message.sfixed32 !== 0) { + obj.sfixed32 = Math.round(message.sfixed32); + } + if (message.sfixed64 !== BigInt("0")) { + obj.sfixed64 = message.sfixed64.toString(); + } + if (message.guint64 !== undefined) { + obj.guint64 = message.guint64; + } + if (message.timestamp !== undefined) { + obj.timestamp = message.timestamp.toISOString(); + } + if (message.uint64s?.length) { + obj.uint64s = message.uint64s.map((e) => e.toString()); + } + return obj; + }, + + create, I>>(base?: I): Numbers { + return Numbers.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): Numbers { + const message = createBaseNumbers(); + message.double = object.double ?? 0; + message.float = object.float ?? 0; + message.int32 = object.int32 ?? 0; + message.int64 = object.int64 ?? BigInt("0"); + message.uint32 = object.uint32 ?? 0; + message.uint64 = object.uint64 ?? BigInt("0"); + message.sint32 = object.sint32 ?? 0; + message.sint64 = object.sint64 ?? BigInt("0"); + message.fixed32 = object.fixed32 ?? 0; + message.fixed64 = object.fixed64 ?? BigInt("0"); + message.sfixed32 = object.sfixed32 ?? 0; + message.sfixed64 = object.sfixed64 ?? BigInt("0"); + message.guint64 = object.guint64 ?? undefined; + message.timestamp = object.timestamp ?? undefined; + message.uint64s = object.uint64s?.map((e) => e) || []; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function toTimestamp(date: Date): Timestamp { + const seconds = BigInt(Math.trunc(date.getTime() / 1_000)); + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = (globalThis.Number(t.seconds.toString()) || 0) * 1_000; + millis += (t.nanos || 0) / 1_000_000; + return new globalThis.Date(millis); +} + +function fromJsonTimestamp(o: any): Date { + if (o instanceof globalThis.Date) { + return o; + } else if (typeof o === "string") { + return new globalThis.Date(o); + } else { + return fromTimestamp(Timestamp.fromJSON(o)); + } +} + +function longToBigint(long: Long) { + return BigInt(long.toString()); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/integration/simple-long-bigint/google/protobuf/timestamp.ts b/integration/simple-long-bigint/google/protobuf/timestamp.ts index 1760585bb..3eec63bbb 100644 --- a/integration/simple-long-bigint/google/protobuf/timestamp.ts +++ b/integration/simple-long-bigint/google/protobuf/timestamp.ts @@ -115,12 +115,12 @@ export interface Timestamp { } function createBaseTimestamp(): Timestamp { - return { seconds: BigInt("0"), nanos: 0 }; + return { seconds: 0n, nanos: 0 }; } export const Timestamp = { encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.seconds !== BigInt("0")) { + if (message.seconds !== 0n) { if (BigInt.asIntN(64, message.seconds) !== message.seconds) { throw new globalThis.Error("value provided for field message.seconds of type int64 too large"); } @@ -164,14 +164,14 @@ export const Timestamp = { fromJSON(object: any): Timestamp { return { - seconds: isSet(object.seconds) ? BigInt(object.seconds) : BigInt("0"), + seconds: isSet(object.seconds) ? BigInt(object.seconds) : 0n, nanos: isSet(object.nanos) ? globalThis.Number(object.nanos) : 0, }; }, toJSON(message: Timestamp): unknown { const obj: any = {}; - if (message.seconds !== BigInt("0")) { + if (message.seconds !== 0n) { obj.seconds = message.seconds.toString(); } if (message.nanos !== 0) { @@ -185,7 +185,7 @@ export const Timestamp = { }, fromPartial, I>>(object: I): Timestamp { const message = createBaseTimestamp(); - message.seconds = object.seconds ?? BigInt("0"); + message.seconds = object.seconds ?? 0n; message.nanos = object.nanos ?? 0; return message; }, diff --git a/integration/simple-long-bigint/google/protobuf/wrappers.ts b/integration/simple-long-bigint/google/protobuf/wrappers.ts index 97fd120cc..bb1e6d1f5 100644 --- a/integration/simple-long-bigint/google/protobuf/wrappers.ts +++ b/integration/simple-long-bigint/google/protobuf/wrappers.ts @@ -212,12 +212,12 @@ export const FloatValue = { }; function createBaseInt64Value(): Int64Value { - return { value: BigInt("0") }; + return { value: 0n }; } export const Int64Value = { encode(message: Int64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { if (BigInt.asIntN(64, message.value) !== message.value) { throw new globalThis.Error("value provided for field message.value of type int64 too large"); } @@ -250,12 +250,12 @@ export const Int64Value = { }, fromJSON(object: any): Int64Value { - return { value: isSet(object.value) ? BigInt(object.value) : BigInt("0") }; + return { value: isSet(object.value) ? BigInt(object.value) : 0n }; }, toJSON(message: Int64Value): unknown { const obj: any = {}; - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { obj.value = message.value.toString(); } return obj; @@ -266,18 +266,18 @@ export const Int64Value = { }, fromPartial, I>>(object: I): Int64Value { const message = createBaseInt64Value(); - message.value = object.value ?? BigInt("0"); + message.value = object.value ?? 0n; return message; }, }; function createBaseUInt64Value(): UInt64Value { - return { value: BigInt("0") }; + return { value: 0n }; } export const UInt64Value = { encode(message: UInt64Value, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { if (BigInt.asUintN(64, message.value) !== message.value) { throw new globalThis.Error("value provided for field message.value of type uint64 too large"); } @@ -310,12 +310,12 @@ export const UInt64Value = { }, fromJSON(object: any): UInt64Value { - return { value: isSet(object.value) ? BigInt(object.value) : BigInt("0") }; + return { value: isSet(object.value) ? BigInt(object.value) : 0n }; }, toJSON(message: UInt64Value): unknown { const obj: any = {}; - if (message.value !== BigInt("0")) { + if (message.value !== 0n) { obj.value = message.value.toString(); } return obj; @@ -326,7 +326,7 @@ export const UInt64Value = { }, fromPartial, I>>(object: I): UInt64Value { const message = createBaseUInt64Value(); - message.value = object.value ?? BigInt("0"); + message.value = object.value ?? 0n; return message; }, }; diff --git a/integration/simple-long-bigint/simple.ts b/integration/simple-long-bigint/simple.ts index 8e2cd6273..8998fcd38 100644 --- a/integration/simple-long-bigint/simple.ts +++ b/integration/simple-long-bigint/simple.ts @@ -32,15 +32,15 @@ function createBaseNumbers(): Numbers { double: 0, float: 0, int32: 0, - int64: BigInt("0"), + int64: 0n, uint32: 0, - uint64: BigInt("0"), + uint64: 0n, sint32: 0, - sint64: BigInt("0"), + sint64: 0n, fixed32: 0, - fixed64: BigInt("0"), + fixed64: 0n, sfixed32: 0, - sfixed64: BigInt("0"), + sfixed64: 0n, guint64: undefined, timestamp: undefined, uint64s: [], @@ -58,7 +58,7 @@ export const Numbers = { if (message.int32 !== 0) { writer.uint32(24).int32(message.int32); } - if (message.int64 !== BigInt("0")) { + if (message.int64 !== 0n) { if (BigInt.asIntN(64, message.int64) !== message.int64) { throw new globalThis.Error("value provided for field message.int64 of type int64 too large"); } @@ -67,7 +67,7 @@ export const Numbers = { if (message.uint32 !== 0) { writer.uint32(40).uint32(message.uint32); } - if (message.uint64 !== BigInt("0")) { + if (message.uint64 !== 0n) { if (BigInt.asUintN(64, message.uint64) !== message.uint64) { throw new globalThis.Error("value provided for field message.uint64 of type uint64 too large"); } @@ -76,7 +76,7 @@ export const Numbers = { if (message.sint32 !== 0) { writer.uint32(56).sint32(message.sint32); } - if (message.sint64 !== BigInt("0")) { + if (message.sint64 !== 0n) { if (BigInt.asIntN(64, message.sint64) !== message.sint64) { throw new globalThis.Error("value provided for field message.sint64 of type sint64 too large"); } @@ -85,7 +85,7 @@ export const Numbers = { if (message.fixed32 !== 0) { writer.uint32(77).fixed32(message.fixed32); } - if (message.fixed64 !== BigInt("0")) { + if (message.fixed64 !== 0n) { if (BigInt.asUintN(64, message.fixed64) !== message.fixed64) { throw new globalThis.Error("value provided for field message.fixed64 of type fixed64 too large"); } @@ -94,7 +94,7 @@ export const Numbers = { if (message.sfixed32 !== 0) { writer.uint32(93).sfixed32(message.sfixed32); } - if (message.sfixed64 !== BigInt("0")) { + if (message.sfixed64 !== 0n) { if (BigInt.asIntN(64, message.sfixed64) !== message.sfixed64) { throw new globalThis.Error("value provided for field message.sfixed64 of type sfixed64 too large"); } @@ -253,15 +253,15 @@ export const Numbers = { double: isSet(object.double) ? globalThis.Number(object.double) : 0, float: isSet(object.float) ? globalThis.Number(object.float) : 0, int32: isSet(object.int32) ? globalThis.Number(object.int32) : 0, - int64: isSet(object.int64) ? BigInt(object.int64) : BigInt("0"), + int64: isSet(object.int64) ? BigInt(object.int64) : 0n, uint32: isSet(object.uint32) ? globalThis.Number(object.uint32) : 0, - uint64: isSet(object.uint64) ? BigInt(object.uint64) : BigInt("0"), + uint64: isSet(object.uint64) ? BigInt(object.uint64) : 0n, sint32: isSet(object.sint32) ? globalThis.Number(object.sint32) : 0, - sint64: isSet(object.sint64) ? BigInt(object.sint64) : BigInt("0"), + sint64: isSet(object.sint64) ? BigInt(object.sint64) : 0n, fixed32: isSet(object.fixed32) ? globalThis.Number(object.fixed32) : 0, - fixed64: isSet(object.fixed64) ? BigInt(object.fixed64) : BigInt("0"), + fixed64: isSet(object.fixed64) ? BigInt(object.fixed64) : 0n, sfixed32: isSet(object.sfixed32) ? globalThis.Number(object.sfixed32) : 0, - sfixed64: isSet(object.sfixed64) ? BigInt(object.sfixed64) : BigInt("0"), + sfixed64: isSet(object.sfixed64) ? BigInt(object.sfixed64) : 0n, guint64: isSet(object.guint64) ? BigInt(object.guint64) : undefined, timestamp: isSet(object.timestamp) ? fromJsonTimestamp(object.timestamp) : undefined, uint64s: globalThis.Array.isArray(object?.uint64s) ? object.uint64s.map((e: any) => BigInt(e)) : [], @@ -279,31 +279,31 @@ export const Numbers = { if (message.int32 !== 0) { obj.int32 = Math.round(message.int32); } - if (message.int64 !== BigInt("0")) { + if (message.int64 !== 0n) { obj.int64 = message.int64.toString(); } if (message.uint32 !== 0) { obj.uint32 = Math.round(message.uint32); } - if (message.uint64 !== BigInt("0")) { + if (message.uint64 !== 0n) { obj.uint64 = message.uint64.toString(); } if (message.sint32 !== 0) { obj.sint32 = Math.round(message.sint32); } - if (message.sint64 !== BigInt("0")) { + if (message.sint64 !== 0n) { obj.sint64 = message.sint64.toString(); } if (message.fixed32 !== 0) { obj.fixed32 = Math.round(message.fixed32); } - if (message.fixed64 !== BigInt("0")) { + if (message.fixed64 !== 0n) { obj.fixed64 = message.fixed64.toString(); } if (message.sfixed32 !== 0) { obj.sfixed32 = Math.round(message.sfixed32); } - if (message.sfixed64 !== BigInt("0")) { + if (message.sfixed64 !== 0n) { obj.sfixed64 = message.sfixed64.toString(); } if (message.guint64 !== undefined) { @@ -326,15 +326,15 @@ export const Numbers = { message.double = object.double ?? 0; message.float = object.float ?? 0; message.int32 = object.int32 ?? 0; - message.int64 = object.int64 ?? BigInt("0"); + message.int64 = object.int64 ?? 0n; message.uint32 = object.uint32 ?? 0; - message.uint64 = object.uint64 ?? BigInt("0"); + message.uint64 = object.uint64 ?? 0n; message.sint32 = object.sint32 ?? 0; - message.sint64 = object.sint64 ?? BigInt("0"); + message.sint64 = object.sint64 ?? 0n; message.fixed32 = object.fixed32 ?? 0; - message.fixed64 = object.fixed64 ?? BigInt("0"); + message.fixed64 = object.fixed64 ?? 0n; message.sfixed32 = object.sfixed32 ?? 0; - message.sfixed64 = object.sfixed64 ?? BigInt("0"); + message.sfixed64 = object.sfixed64 ?? 0n; message.guint64 = object.guint64 ?? undefined; message.timestamp = object.timestamp ?? undefined; message.uint64s = object.uint64s?.map((e) => e) || []; diff --git a/protos/tsconfig.json b/protos/tsconfig.json index 190c5b8a3..0152ec887 100644 --- a/protos/tsconfig.json +++ b/protos/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": ["es2018"], "module": "commonjs", "esModuleInterop": true, diff --git a/src/options.ts b/src/options.ts index 1ca9afda1..06a5485b4 100644 --- a/src/options.ts +++ b/src/options.ts @@ -102,6 +102,7 @@ export type Options = { useNullAsOptional: boolean; annotateFilesWithVersion: boolean; noDefaultsForOptionals: boolean; + bigIntLiteral: boolean; }; export function defaultOptions(): Options { @@ -169,6 +170,7 @@ export function defaultOptions(): Options { useNullAsOptional: false, annotateFilesWithVersion: true, noDefaultsForOptionals: false, + bigIntLiteral: true, }; } diff --git a/src/types.ts b/src/types.ts index fbbadd150..afad3ea6a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -272,7 +272,7 @@ export function defaultValue(ctx: Context, field: FieldDescriptorProto): any { } else if (options.forceLong === LongOption.STRING) { return `"${numericDefaultVal}"`; } else if (options.forceLong === LongOption.BIGINT) { - return `BigInt("${numericDefaultVal}")`; + return options.bigIntLiteral ? code`${numericDefaultVal}n` : code`BigInt("${numericDefaultVal}")`; } else { return numericDefaultVal; } diff --git a/tests/options-test.ts b/tests/options-test.ts index 04fe379d1..f43cce537 100644 --- a/tests/options-test.ts +++ b/tests/options-test.ts @@ -8,6 +8,7 @@ describe("options", () => { "addGrpcMetadata": false, "addNestjsRestParameter": false, "annotateFilesWithVersion": true, + "bigIntLiteral": true, "comments": true, "constEnums": false, "context": false, diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 63e875178..ac3cc90da 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": ["es2018"], "module": "commonjs", "strict": true, diff --git a/tsconfig.json b/tsconfig.json index cfc0ea4ca..1abc83b82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2018", + "target": "es2020", "lib": ["es2018"], "module": "commonjs", "strict": true,