From d877b06669fb9dc0b2e176f770df4377c40b33de Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 11 Feb 2021 11:37:26 +0100 Subject: [PATCH 1/4] Start building example for useDate=false --- .../google/protobuf/timestamp.ts | 215 ++++++++++++++++++ integration/use-date-false/metadata.bin | Bin 0 -> 6615 bytes integration/use-date-false/metadata.proto | 7 + integration/use-date-false/metadata.ts | 98 ++++++++ integration/use-date-false/parameters.txt | 1 + integration/use-date-false/use-date-test.ts | 13 ++ 6 files changed, 334 insertions(+) create mode 100644 integration/use-date-false/google/protobuf/timestamp.ts create mode 100644 integration/use-date-false/metadata.bin create mode 100644 integration/use-date-false/metadata.proto create mode 100644 integration/use-date-false/metadata.ts create mode 100644 integration/use-date-false/parameters.txt create mode 100644 integration/use-date-false/use-date-test.ts diff --git a/integration/use-date-false/google/protobuf/timestamp.ts b/integration/use-date-false/google/protobuf/timestamp.ts new file mode 100644 index 000000000..f5cce34ba --- /dev/null +++ b/integration/use-date-false/google/protobuf/timestamp.ts @@ -0,0 +1,215 @@ +/* eslint-disable */ +import * as Long from 'long'; +import { util, configure, Writer, Reader } from 'protobufjs/minimal'; + +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: number; + /** + * 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; +} + +const baseTimestamp: object = { seconds: 0, nanos: 0 }; + +export const Timestamp = { + encode(message: Timestamp, writer: Writer = Writer.create()): Writer { + writer.uint32(8).int64(message.seconds); + writer.uint32(16).int32(message.nanos); + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Timestamp { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseTimestamp } as Timestamp; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.seconds = longToNumber(reader.int64() as Long); + break; + case 2: + message.nanos = reader.int32(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Timestamp { + const message = { ...baseTimestamp } as Timestamp; + if (object.seconds !== undefined && object.seconds !== null) { + message.seconds = Number(object.seconds); + } else { + message.seconds = 0; + } + if (object.nanos !== undefined && object.nanos !== null) { + message.nanos = Number(object.nanos); + } else { + message.nanos = 0; + } + return message; + }, + + fromPartial(object: DeepPartial): Timestamp { + const message = { ...baseTimestamp } as Timestamp; + if (object.seconds !== undefined && object.seconds !== null) { + message.seconds = object.seconds; + } else { + message.seconds = 0; + } + if (object.nanos !== undefined && object.nanos !== null) { + message.nanos = object.nanos; + } else { + message.nanos = 0; + } + return message; + }, + + toJSON(message: Timestamp): unknown { + const obj: any = {}; + message.seconds !== undefined && (obj.seconds = message.seconds); + message.nanos !== undefined && (obj.nanos = message.nanos); + return obj; + }, +}; + +declare var self: any | undefined; +declare var window: any | undefined; +var globalThis: any = (() => { + if (typeof globalThis !== 'undefined') return globalThis; + if (typeof self !== 'undefined') return self; + if (typeof window !== 'undefined') return window; + if (typeof global !== 'undefined') return global; + throw new Error('Unable to locate global object'); +})(); + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error('Value is larger than Number.MAX_SAFE_INTEGER'); + } + return long.toNumber(); +} + +if (util.Long !== Long) { + util.Long = Long as any; + configure(); +} diff --git a/integration/use-date-false/metadata.bin b/integration/use-date-false/metadata.bin new file mode 100644 index 0000000000000000000000000000000000000000..cebb47a0f1e0f20d670b7cfdb0894d0cbc276f96 GIT binary patch literal 6615 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 RpG((^>01tOrS(+h{{Vp+@}2+y literal 0 HcmV?d00001 diff --git a/integration/use-date-false/metadata.proto b/integration/use-date-false/metadata.proto new file mode 100644 index 000000000..96ba61b71 --- /dev/null +++ b/integration/use-date-false/metadata.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +import "google/protobuf/timestamp.proto"; + +message Metadata { + google.protobuf.Timestamp last_edited = 1; +} \ No newline at end of file diff --git a/integration/use-date-false/metadata.ts b/integration/use-date-false/metadata.ts new file mode 100644 index 000000000..764a9f16a --- /dev/null +++ b/integration/use-date-false/metadata.ts @@ -0,0 +1,98 @@ +/* eslint-disable */ +import { Timestamp } from './google/protobuf/timestamp'; +import { Writer, Reader } from 'protobufjs/minimal'; + +export const protobufPackage = ''; + +export interface Metadata { + lastEdited: Timestamp | undefined; +} + +const baseMetadata: object = {}; + +export const Metadata = { + encode(message: Metadata, writer: Writer = Writer.create()): Writer { + if (message.lastEdited !== undefined && message.lastEdited !== undefined) { + Timestamp.encode(toTimestamp(message.lastEdited), writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: Reader | Uint8Array, length?: number): Metadata { + const reader = input instanceof Uint8Array ? new Reader(input) : input; + let end = length === undefined ? reader.len : reader.pos + length; + const message = { ...baseMetadata } as Metadata; + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.lastEdited = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }, + + fromJSON(object: any): Metadata { + const message = { ...baseMetadata } as Metadata; + if (object.lastEdited !== undefined && object.lastEdited !== null) { + message.lastEdited = fromJsonTimestamp(object.lastEdited); + } else { + message.lastEdited = undefined; + } + return message; + }, + + fromPartial(object: DeepPartial): Metadata { + const message = { ...baseMetadata } as Metadata; + if (object.lastEdited !== undefined && object.lastEdited !== null) { + message.lastEdited = object.lastEdited; + } else { + message.lastEdited = undefined; + } + return message; + }, + + toJSON(message: Metadata): unknown { + const obj: any = {}; + message.lastEdited !== undefined && + (obj.lastEdited = message.lastEdited !== undefined ? message.lastEdited.toISOString() : null); + return obj; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | undefined; +export type DeepPartial = T extends Builtin + ? T + : T extends Array + ? Array> + : T extends ReadonlyArray + ? ReadonlyArray> + : T extends {} + ? { [K in keyof T]?: DeepPartial } + : Partial; + +function toTimestamp(date: Date): Timestamp { + const seconds = date.getTime() / 1_000; + const nanos = (date.getTime() % 1_000) * 1_000_000; + return { seconds, nanos }; +} + +function fromTimestamp(t: Timestamp): Date { + let millis = t.seconds * 1_000; + millis += t.nanos / 1_000_000; + return new Date(millis); +} + +function fromJsonTimestamp(o: any): Date { + if (o instanceof Date) { + return o; + } else if (typeof o === 'string') { + return new Date(o); + } else { + return fromTimestamp(Timestamp.fromJSON(o)); + } +} diff --git a/integration/use-date-false/parameters.txt b/integration/use-date-false/parameters.txt new file mode 100644 index 000000000..0a0d69154 --- /dev/null +++ b/integration/use-date-false/parameters.txt @@ -0,0 +1 @@ +useDate=false \ No newline at end of file diff --git a/integration/use-date-false/use-date-test.ts b/integration/use-date-false/use-date-test.ts new file mode 100644 index 000000000..b949f31f7 --- /dev/null +++ b/integration/use-date-false/use-date-test.ts @@ -0,0 +1,13 @@ +import { Metadata } from './metadata'; + +describe('useDate=false', () => { + it('generates types that compile and encode', () => { + const output = Metadata.encode({ + lastEdited: { + seconds: 123456789, + nanos: 234567890, + } + }).finish(); + expect(output.length).toBeGreaterThan(8); + }); +}); \ No newline at end of file From af82b9789818dfa0994fc88ccb28c32c6299d3c0 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Thu, 11 Feb 2021 12:05:12 +0000 Subject: [PATCH 2/4] Fix main.ts for useDate=false --- src/main.ts | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3e5ca8fd9..76d49b319 100644 --- a/src/main.ts +++ b/src/main.ts @@ -400,7 +400,8 @@ function makeTimestampMethods(options: Options, longs: ReturnType>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); writeSnippet = (place) => - code`${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`; + options.useDate + ? code`${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()` + : code`${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`; } else if (isValueType(ctx, field)) { const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); @@ -887,7 +903,9 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP ? code`${from} !== undefined ? ${toJson}(${from}) : undefined` : code`${toJson}(${from})`; } else if (isTimestamp(field)) { - return code`${from} !== undefined ? ${from}.toISOString() : null`; + return options.useDate + ? code`${from} !== undefined ? ${from}.toISOString() : null` + : code`${from} !== undefined ? ${utils.fromTimestamp}(${from}).toISOString() : null`; } else if (isMapType(ctx, messageDesc, field)) { // For map types, drill-in and then admittedly re-hard-code our per-value-type logic const valueType = (typeMap.get(field.typeName)![2] as DescriptorProto).field[1]; @@ -897,7 +915,7 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP } else if (isBytes(valueType)) { return code`${utils.base64FromBytes}(${from})`; } else if (isTimestamp(valueType)) { - return code`${from}.toISOString()`; + return options.useDate ? code`${from}.toISOString()` : code`${utils.fromTimestamp}(${from}).toISOString()`; } else if (isScalar(valueType)) { return code`${from}`; } else { @@ -959,6 +977,7 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP function generateFromPartial(ctx: Context, fullName: string, messageDesc: DescriptorProto): Code { const { options, utils, typeMap } = ctx; const chunks: Code[] = []; + const Timestamp = imp('Timestamp@./google/protobuf/timestamp'); // create the basic function declaration chunks.push(code` @@ -978,8 +997,10 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const fieldName = maybeSnakeToCamel(field.name, options); const readSnippet = (from: string): Code => { - if (isPrimitive(field) || isTimestamp(field) || isValueType(ctx, field)) { + if (isPrimitive(field) || isValueType(ctx, field)) { return code`${from}`; + } else if (isTimestamp(field)) { + return options.useDate ? code`${from}` : code`${Timestamp}.fromPartial(${from})`; } else if (isMessage(field)) { if (isRepeated(field) && isMapType(ctx, messageDesc, field)) { const valueType = (typeMap.get(field.typeName)![2] as DescriptorProto).field[1]; @@ -993,7 +1014,7 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return code`${cstr}(${from})`; } } else if (isTimestamp(valueType)) { - return code`${from}`; + return options.useDate ? code`${from}` : code`${Timestamp}.fromPartial(${from})`; } else { const type = basicTypeName(ctx, valueType); return code`${type}.fromPartial(${from})`; @@ -1015,7 +1036,7 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri chunks.push(code` Object.entries(object.${fieldName}).forEach(([key, value]) => { if (value !== undefined) { - message.${fieldName}[${i}] = ${readSnippet('value')}; + message.${fieldName}[${i}] = ${readSnippet('value')}; } }); `); From 6aefd2dd854ec9bd5cb99fb2cc06dc1dd06a0228 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 16 Feb 2021 11:16:21 +0000 Subject: [PATCH 3/4] Simplify useDate/Timestamp changes --- src/main.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/main.ts b/src/main.ts index 76d49b319..fa185363a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -584,11 +584,9 @@ function generateDecode(ctx: Context, fullName: string, messageDesc: DescriptorP } else if (isValueType(ctx, field)) { const type = basicTypeName(ctx, field, { keepValueType: true }); readSnippet = code`${type}.decode(reader, reader.uint32()).value`; - } else if (isTimestamp(field)) { + } else if (isTimestamp(field) && options.useDate) { const type = basicTypeName(ctx, field, { keepValueType: true }); - readSnippet = options.useDate - ? code`${utils.fromTimestamp}(${type}.decode(reader, reader.uint32()))` - : code`${type}.decode(reader, reader.uint32())`; + readSnippet = code`${utils.fromTimestamp}(${type}.decode(reader, reader.uint32()))`; } else if (isMessage(field)) { const type = basicTypeName(ctx, field); readSnippet = code`${type}.decode(reader, reader.uint32())`; @@ -670,13 +668,11 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP if (isScalar(field) || isEnum(field)) { const tag = ((field.number << 3) | basicWireType(field.type)) >>> 0; writeSnippet = (place) => code`writer.uint32(${tag}).${toReaderCall(field)}(${place})`; - } else if (isTimestamp(field)) { + } else if (isTimestamp(field) && options.useDate) { const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); writeSnippet = (place) => - options.useDate - ? code`${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()` - : code`${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`; + code`${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`; } else if (isValueType(ctx, field)) { const tag = ((field.number << 3) | 2) >>> 0; const type = basicTypeName(ctx, field, { keepValueType: true }); @@ -977,7 +973,6 @@ function generateToJson(ctx: Context, fullName: string, messageDesc: DescriptorP function generateFromPartial(ctx: Context, fullName: string, messageDesc: DescriptorProto): Code { const { options, utils, typeMap } = ctx; const chunks: Code[] = []; - const Timestamp = imp('Timestamp@./google/protobuf/timestamp'); // create the basic function declaration chunks.push(code` @@ -999,8 +994,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const readSnippet = (from: string): Code => { if (isPrimitive(field) || isValueType(ctx, field)) { return code`${from}`; - } else if (isTimestamp(field)) { - return options.useDate ? code`${from}` : code`${Timestamp}.fromPartial(${from})`; + } else if (isTimestamp(field) && options.useDate) { + return code`${from}`; } else if (isMessage(field)) { if (isRepeated(field) && isMapType(ctx, messageDesc, field)) { const valueType = (typeMap.get(field.typeName)![2] as DescriptorProto).field[1]; @@ -1013,8 +1008,8 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri const cstr = capitalize(basicTypeName(ctx, valueType).toCodeString()); return code`${cstr}(${from})`; } - } else if (isTimestamp(valueType)) { - return options.useDate ? code`${from}` : code`${Timestamp}.fromPartial(${from})`; + } else if (isTimestamp(valueType) && options.useDate) { + return code`${from}`; } else { const type = basicTypeName(ctx, valueType); return code`${type}.fromPartial(${from})`; From 6c805d2816909a4bc69a06a4145ef627077d3db2 Mon Sep 17 00:00:00 2001 From: willclarktech Date: Tue, 16 Feb 2021 11:33:11 +0000 Subject: [PATCH 4/4] Update codegen for useDate fix --- .../google/protobuf/timestamp.ts | 14 +++++--- integration/use-date-false/metadata.ts | 32 ++++++++++++------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/integration/use-date-false/google/protobuf/timestamp.ts b/integration/use-date-false/google/protobuf/timestamp.ts index f5cce34ba..3b5664604 100644 --- a/integration/use-date-false/google/protobuf/timestamp.ts +++ b/integration/use-date-false/google/protobuf/timestamp.ts @@ -117,15 +117,19 @@ const baseTimestamp: object = { seconds: 0, nanos: 0 }; export const Timestamp = { encode(message: Timestamp, writer: Writer = Writer.create()): Writer { - writer.uint32(8).int64(message.seconds); - writer.uint32(16).int32(message.nanos); + if (message.seconds !== 0) { + writer.uint32(8).int64(message.seconds); + } + if (message.nanos !== 0) { + writer.uint32(16).int32(message.nanos); + } return writer; }, decode(input: Reader | Uint8Array, length?: number): Timestamp { const reader = input instanceof Uint8Array ? new Reader(input) : input; let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseTimestamp } as Timestamp; + const message = globalThis.Object.create(baseTimestamp) as Timestamp; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -144,7 +148,7 @@ export const Timestamp = { }, fromJSON(object: any): Timestamp { - const message = { ...baseTimestamp } as Timestamp; + const message = globalThis.Object.create(baseTimestamp) as Timestamp; if (object.seconds !== undefined && object.seconds !== null) { message.seconds = Number(object.seconds); } else { @@ -188,7 +192,7 @@ var globalThis: any = (() => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; - throw new Error('Unable to locate global object'); + throw 'Unable to locate global object'; })(); type Builtin = Date | Function | Uint8Array | string | number | undefined; diff --git a/integration/use-date-false/metadata.ts b/integration/use-date-false/metadata.ts index 764a9f16a..d3e38244a 100644 --- a/integration/use-date-false/metadata.ts +++ b/integration/use-date-false/metadata.ts @@ -12,8 +12,8 @@ const baseMetadata: object = {}; export const Metadata = { encode(message: Metadata, writer: Writer = Writer.create()): Writer { - if (message.lastEdited !== undefined && message.lastEdited !== undefined) { - Timestamp.encode(toTimestamp(message.lastEdited), writer.uint32(10).fork()).ldelim(); + if (message.lastEdited !== undefined) { + Timestamp.encode(message.lastEdited, writer.uint32(10).fork()).ldelim(); } return writer; }, @@ -21,12 +21,12 @@ export const Metadata = { decode(input: Reader | Uint8Array, length?: number): Metadata { const reader = input instanceof Uint8Array ? new Reader(input) : input; let end = length === undefined ? reader.len : reader.pos + length; - const message = { ...baseMetadata } as Metadata; + const message = globalThis.Object.create(baseMetadata) as Metadata; while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - message.lastEdited = fromTimestamp(Timestamp.decode(reader, reader.uint32())); + message.lastEdited = Timestamp.decode(reader, reader.uint32()); break; default: reader.skipType(tag & 7); @@ -37,7 +37,7 @@ export const Metadata = { }, fromJSON(object: any): Metadata { - const message = { ...baseMetadata } as Metadata; + const message = globalThis.Object.create(baseMetadata) as Metadata; if (object.lastEdited !== undefined && object.lastEdited !== null) { message.lastEdited = fromJsonTimestamp(object.lastEdited); } else { @@ -49,7 +49,7 @@ export const Metadata = { fromPartial(object: DeepPartial): Metadata { const message = { ...baseMetadata } as Metadata; if (object.lastEdited !== undefined && object.lastEdited !== null) { - message.lastEdited = object.lastEdited; + message.lastEdited = Timestamp.fromPartial(object.lastEdited); } else { message.lastEdited = undefined; } @@ -59,11 +59,21 @@ export const Metadata = { toJSON(message: Metadata): unknown { const obj: any = {}; message.lastEdited !== undefined && - (obj.lastEdited = message.lastEdited !== undefined ? message.lastEdited.toISOString() : null); + (obj.lastEdited = message.lastEdited !== undefined ? fromTimestamp(message.lastEdited).toISOString() : null); return obj; }, }; +declare var self: any | undefined; +declare var window: any | undefined; +var globalThis: any = (() => { + if (typeof globalThis !== 'undefined') return globalThis; + if (typeof self !== 'undefined') return self; + if (typeof window !== 'undefined') return window; + if (typeof global !== 'undefined') return global; + throw 'Unable to locate global object'; +})(); + type Builtin = Date | Function | Uint8Array | string | number | undefined; export type DeepPartial = T extends Builtin ? T @@ -87,12 +97,12 @@ function fromTimestamp(t: Timestamp): Date { return new Date(millis); } -function fromJsonTimestamp(o: any): Date { +function fromJsonTimestamp(o: any): Timestamp { if (o instanceof Date) { - return o; + return toTimestamp(o); } else if (typeof o === 'string') { - return new Date(o); + return toTimestamp(new Date(o)); } else { - return fromTimestamp(Timestamp.fromJSON(o)); + return Timestamp.fromJSON(o); } }