@aptre/protobuf-es-lite 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/binary.d.ts CHANGED
@@ -50,7 +50,7 @@ declare function readMapEntry(field: FieldInfo & {
50
50
  }, reader: IBinaryReader, options: BinaryReadOptions): [string | number, ScalarValue | AnyMessage | undefined];
51
51
  declare function readScalar(reader: IBinaryReader, type: ScalarType): ScalarValue;
52
52
  declare function readScalarLTString(reader: IBinaryReader, type: ScalarType): Exclude<ScalarValue, bigint>;
53
- declare function readMessage<T>(message: T, fields: FieldList, reader: IBinaryReader, lengthOrEndTagFieldNo: number, options: BinaryReadOptions, delimitedMessageEncoding?: boolean): void;
53
+ declare function readMessage<T>(message: T, fields: FieldList, reader: IBinaryReader, lengthOrEndTagFieldNo: number, options: BinaryReadOptions, delimitedMessageEncoding: boolean): void;
54
54
  /**
55
55
  * Serialize a message to binary data.
56
56
  */
package/dist/binary.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isFieldSet, resolveMessageType, } from "./field.js";
2
2
  import { handleUnknownField, unknownFieldsSymbol } from "./unknown.js";
3
- import { wrapField } from "./field-wrapper.js";
3
+ import { unwrapField, wrapField } from "./field-wrapper.js";
4
4
  import { LongType, ScalarType, scalarZeroValue, } from "./scalar.js";
5
5
  import { assert } from "./assert.js";
6
6
  import { BinaryReader, BinaryWriter, WireType, } from "./binary-encoding.js";
@@ -26,7 +26,7 @@ reader, field, wireType, options) {
26
26
  if (field.oneof) {
27
27
  var oneofMsg = target[field.oneof.localName];
28
28
  if (!oneofMsg) {
29
- oneofMsg = target[field.oneof.localName] = {};
29
+ oneofMsg = target[field.oneof.localName] = Object.create(null);
30
30
  }
31
31
  target = oneofMsg;
32
32
  if (target.case != localName) {
@@ -74,19 +74,16 @@ reader, field, wireType, options) {
74
74
  if (!Array.isArray(tgtArr)) {
75
75
  tgtArr = target[localName] = [];
76
76
  }
77
- tgtArr.push(readMessageField(reader, {}, messageType.fields, options, field));
77
+ tgtArr.push(unwrapField(messageType.fieldWrapper, readMessageField(reader, Object.create(null), messageType.fields, options, field)));
78
78
  }
79
79
  else {
80
- target[localName] = readMessageField(reader, {}, messageType.fields, options, field);
81
- if (messageType.fieldWrapper && !field.oneof && !field.repeated) {
82
- target[localName] = messageType.fieldWrapper.unwrapField(target[localName]);
83
- }
80
+ target[localName] = unwrapField(messageType.fieldWrapper, readMessageField(reader, Object.create(null), messageType.fields, options, field));
84
81
  }
85
82
  break;
86
83
  case "map":
87
84
  let [mapKey, mapVal] = readMapEntry(field, reader, options);
88
85
  if (typeof target[localName] !== "object") {
89
- target[localName] = {};
86
+ target[localName] = Object.create(null);
90
87
  }
91
88
  // safe to assume presence of map object, oneof cannot contain repeated values
92
89
  target[localName][mapKey] = mapVal;
@@ -113,7 +110,7 @@ function readMapEntry(field, reader, options) {
113
110
  break;
114
111
  case "message":
115
112
  const messageType = resolveMessageType(field.V.T);
116
- val = readMessageField(reader, {}, messageType.fields, options, undefined);
113
+ val = readMessageField(reader, Object.create(null), messageType.fields, options, undefined);
117
114
  break;
118
115
  }
119
116
  break;
@@ -135,7 +132,7 @@ function readMapEntry(field, reader, options) {
135
132
  val = field.V.T.values[0].no;
136
133
  break;
137
134
  case "message":
138
- val = {};
135
+ val = Object.create(null);
139
136
  break;
140
137
  }
141
138
  }
@@ -184,13 +181,10 @@ function readScalarLTString(reader, type) {
184
181
  // Read a message, avoiding MessageType.fromBinary() to re-use the
185
182
  // BinaryReadOptions and the IBinaryReader.
186
183
  function readMessageField(reader, message, fields, options, field) {
187
- const delimited = field?.delimited;
188
- readMessage(message, fields, reader, delimited ? field.no : reader.uint32(), // eslint-disable-line @typescript-eslint/strict-boolean-expressions
189
- options, delimited);
184
+ readMessage(message, fields, reader, field?.delimited ? field.no : reader.uint32(), options, field?.delimited ?? false);
190
185
  return message;
191
186
  }
192
187
  function readMessage(message, fields, reader, lengthOrEndTagFieldNo, options, delimitedMessageEncoding) {
193
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
194
188
  const end = delimitedMessageEncoding ? reader.len : reader.pos + lengthOrEndTagFieldNo;
195
189
  let fieldNo, wireType;
196
190
  while (reader.pos < end) {
@@ -227,7 +221,9 @@ function writeMessage(message, fields, writer, options) {
227
221
  const value = field.oneof ?
228
222
  message[field.oneof.localName].value
229
223
  : message[field.localName];
230
- writeField(field, value, writer, options);
224
+ if (value !== undefined) {
225
+ writeField(field, value, writer, options);
226
+ }
231
227
  }
232
228
  if (options.writeUnknownFields) {
233
229
  writeUnknownFields(message, writer);
package/dist/enum.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { DescEnum } from "./index.js";
1
2
  /**
2
3
  * Reflection information for a protobuf enumeration.
3
4
  */
@@ -33,8 +34,17 @@ export interface EnumValueInfo {
33
34
  */
34
35
  readonly localName: string;
35
36
  }
36
- export declare function normalizeEnumValue(value: EnumValueInfo | Omit<EnumValueInfo, "localName">): EnumValueInfo;
37
37
  /**
38
38
  * Create a new EnumType with the given values.
39
39
  */
40
40
  export declare function createEnumType(typeName: string, values: (EnumValueInfo | Omit<EnumValueInfo, "localName">)[], _opt?: {}): EnumType;
41
+ export declare function enumInfoZeroValue(values: (EnumValueInfo | Omit<EnumValueInfo, "localName">)[]): number;
42
+ export declare function enumDescZeroValue(info: DescEnum): number;
43
+ export declare function enumZeroValue<T extends EnumType>(info: T): number;
44
+ /**
45
+ * Returns the normalized version of the enum value.
46
+ * Null is cast to the default value.
47
+ * String names are cast to the number enum.
48
+ * If string and the value is unknown, throws an error.
49
+ */
50
+ export declare function normalizeEnumValue(info: EnumType, value: string | number | null | undefined): number;
package/dist/enum.js CHANGED
@@ -1,9 +1,3 @@
1
- export function normalizeEnumValue(value) {
2
- if ("localName" in value) {
3
- return value;
4
- }
5
- return { ...value, localName: value.name };
6
- }
7
1
  /**
8
2
  * Create a new EnumType with the given values.
9
3
  */
@@ -16,7 +10,7 @@ _opt) {
16
10
  for (const value of values) {
17
11
  // We do not surface options at this time
18
12
  // const value: EnumValueInfo = {...v, options: v.options ?? emptyReadonlyObject};
19
- const n = normalizeEnumValue(value);
13
+ const n = "localName" in value ? value : { ...value, localName: value.name };
20
14
  normalValues.push(n);
21
15
  names[value.name] = n;
22
16
  numbers[value.no] = n;
@@ -34,3 +28,58 @@ _opt) {
34
28
  },
35
29
  };
36
30
  }
31
+ // enumInfoZeroValue returns the zero value for an enum info.
32
+ export function enumInfoZeroValue(values) {
33
+ if (!values?.length) {
34
+ return 0;
35
+ }
36
+ // In proto3, the first enum value must be zero.
37
+ // In proto2, protobuf-go returns the first value as the default.
38
+ const zeroValue = values[0];
39
+ return zeroValue.no;
40
+ }
41
+ // enumDescZeroValue returns the zero value for an enum description.
42
+ export function enumDescZeroValue(info) {
43
+ // In proto3, the first enum value must be zero.
44
+ // In proto2, protobuf-go returns the first value as the default.
45
+ if (info.values.length < 1) {
46
+ throw new Error("invalid enum: missing at least one value");
47
+ }
48
+ const zeroValue = info.values[0];
49
+ return zeroValue.number;
50
+ }
51
+ // enumZeroValue returns the zero value for an enum type.
52
+ export function enumZeroValue(info) {
53
+ // In proto3, the first enum value must be zero.
54
+ // In proto2, protobuf-go returns the first value as the default.
55
+ if (info.values.length < 1) {
56
+ throw new Error("invalid enum: missing at least one value");
57
+ }
58
+ const zeroValue = info.values[0];
59
+ return zeroValue.no;
60
+ }
61
+ /**
62
+ * Returns the normalized version of the enum value.
63
+ * Null is cast to the default value.
64
+ * String names are cast to the number enum.
65
+ * If string and the value is unknown, throws an error.
66
+ */
67
+ export function normalizeEnumValue(info, value) {
68
+ const zeroValue = enumZeroValue(info);
69
+ if (value == null) {
70
+ return zeroValue;
71
+ }
72
+ if (value === "" || value === zeroValue) {
73
+ return zeroValue;
74
+ }
75
+ if (typeof value === "string") {
76
+ // TODO: strip the type name prefix as well? MyEnum_VALUE
77
+ const val = info.findName(value);
78
+ if (!val) {
79
+ throw new Error(`enum ${info.typeName}: invalid value: "${value}"`);
80
+ }
81
+ return val.no;
82
+ }
83
+ // return the number value
84
+ return value;
85
+ }
@@ -5,7 +5,15 @@ import { ScalarType } from "./scalar.js";
5
5
  * ergonomic for use as a message field.
6
6
  */
7
7
  export interface FieldWrapper<T = any, U = any> {
8
+ /**
9
+ * Wrap a primitive message field value in its corresponding wrapper
10
+ * message. This function is idempotent.
11
+ */
8
12
  wrapField(value: U | null | undefined): T;
13
+ /**
14
+ * If the given field uses one of the well-known wrapper types, return
15
+ * the primitive type it wraps.
16
+ */
9
17
  unwrapField(value: T): U | null | undefined;
10
18
  }
11
19
  /**
@@ -13,6 +21,11 @@ export interface FieldWrapper<T = any, U = any> {
13
21
  * message. This function is idempotent.
14
22
  */
15
23
  export declare function wrapField<T>(fieldWrapper: FieldWrapper<T> | undefined, value: any): T;
24
+ /**
25
+ * Wrap a primitive message field value in its corresponding wrapper
26
+ * message. This function is idempotent.
27
+ */
28
+ export declare function unwrapField<T, U = T>(fieldWrapper: FieldWrapper<T> | undefined, value: any): U;
16
29
  /**
17
30
  * If the given field uses one of the well-known wrapper types, return
18
31
  * the primitive type it wraps.
@@ -9,6 +9,13 @@ export function wrapField(fieldWrapper, value) {
9
9
  }
10
10
  return fieldWrapper.wrapField(value);
11
11
  }
12
+ /**
13
+ * Wrap a primitive message field value in its corresponding wrapper
14
+ * message. This function is idempotent.
15
+ */
16
+ export function unwrapField(fieldWrapper, value) {
17
+ return !!fieldWrapper ? fieldWrapper.unwrapField(value) : value;
18
+ }
12
19
  /**
13
20
  * If the given field uses one of the well-known wrapper types, return
14
21
  * the primitive type it wraps.
package/dist/field.d.ts CHANGED
@@ -28,7 +28,42 @@ import { protoCamelCase } from "./names.js";
28
28
  * - "default": Only proto2: An explicit default value.
29
29
  * - "delimited": Only proto2: Use the tag-delimited group encoding.
30
30
  */
31
- export type FieldInfo = fiRules<fiScalar> | fiRules<fiEnum> | fiRules<fiMessage> | fiRules<fiMap>;
31
+ export type FieldInfo = ScalarFieldInfo | EnumFieldInfo | MessageFieldInfo | MapFieldInfo;
32
+ /**
33
+ * ScalarFieldInfo represents a scalar field.
34
+ * It includes rules for field presence, repetition, and packing.
35
+ */
36
+ export type ScalarFieldInfo = fiRules<fiScalar>;
37
+ /**
38
+ * EnumFieldInfo represents an enum field.
39
+ * It includes rules for field presence, repetition, and packing.
40
+ */
41
+ export type EnumFieldInfo = fiRules<fiEnum>;
42
+ /**
43
+ * MessageFieldInfo represents a message field.
44
+ * It includes rules for field presence, repetition, and packing.
45
+ */
46
+ export type MessageFieldInfo = fiRules<fiMessage>;
47
+ /**
48
+ * MapFieldInfo represents a map field with a scalar key type and
49
+ * a scalar, enum, or message value type.
50
+ * It includes rules for field presence, repetition, and packing.
51
+ */
52
+ export type MapFieldInfo = fiRules<fiMap>;
53
+ /**
54
+ * MapValueInfo represents the value type of a map field.
55
+ * The value can be a scalar, enum, or message type.
56
+ */
57
+ export type MapValueInfo = {
58
+ readonly kind: "scalar";
59
+ readonly T: ScalarType;
60
+ } | {
61
+ readonly kind: "enum";
62
+ readonly T: EnumType;
63
+ } | {
64
+ readonly kind: "message";
65
+ readonly T: MessageType<any> | (() => MessageType<any>);
66
+ };
32
67
  /**
33
68
  * Provides convenient access to field information of a message type.
34
69
  */
@@ -78,7 +113,7 @@ export declare function newFieldList(fields: FieldListSource, packedByDefault: b
78
113
  /**
79
114
  * Returns true if the field is set.
80
115
  */
81
- export declare function isFieldSet(field: FieldInfo, target: Record<string, any>): boolean;
116
+ export declare function isFieldSet(field: FieldInfo, target: Record<string, any> | null | undefined): boolean;
82
117
  /**
83
118
  * Returns the JSON name for a protobuf field, exactly like protoc does.
84
119
  */
@@ -283,16 +318,7 @@ interface fiMap extends fiShared {
283
318
  /**
284
319
  * Map value type. Can be scalar, enum, or message.
285
320
  */
286
- readonly V: {
287
- readonly kind: "scalar";
288
- readonly T: ScalarType;
289
- } | {
290
- readonly kind: "enum";
291
- readonly T: EnumType;
292
- } | {
293
- readonly kind: "message";
294
- readonly T: MessageType<any> | (() => MessageType<any>);
295
- };
321
+ readonly V: MapValueInfo;
296
322
  /**
297
323
  * Is the field repeated? Never true for maps.
298
324
  */
package/dist/field.js CHANGED
@@ -95,28 +95,32 @@ export function newFieldList(fields, packedByDefault) {
95
95
  */
96
96
  export function isFieldSet(field, target) {
97
97
  const localName = field.localName;
98
+ if (!target) {
99
+ return false;
100
+ }
98
101
  if (field.repeated) {
99
- return target[localName].length > 0;
102
+ return !!target[localName]?.length;
100
103
  }
101
104
  if (field.oneof) {
102
- return target[field.oneof.localName].case === localName; // eslint-disable-line @typescript-eslint/no-unsafe-member-access
105
+ return target[field.oneof.localName]?.case === localName; // eslint-disable-line @typescript-eslint/no-unsafe-member-access
103
106
  }
104
107
  switch (field.kind) {
105
108
  case "enum":
106
109
  case "scalar":
107
110
  if (field.opt || field.req) {
108
111
  // explicit presence
109
- return target[localName] !== undefined;
112
+ return target[localName] != null;
110
113
  }
111
114
  // implicit presence
112
115
  if (field.kind == "enum") {
113
116
  return target[localName] !== field.T.values[0].no;
114
117
  }
118
+ // not zero value
115
119
  return !isScalarZeroValue(field.T, target[localName]);
116
120
  case "message":
117
- return target[localName] !== undefined;
121
+ return target[localName] != null;
118
122
  case "map":
119
- return Object.keys(target[localName]).length > 0; // eslint-disable-line @typescript-eslint/no-unsafe-argument
123
+ return (target[localName] != null && !!Object.keys(target[localName]).length); // eslint-disable-line @typescript-eslint/no-unsafe-argument
120
124
  }
121
125
  }
122
126
  /**
@@ -118,7 +118,8 @@ export type Timestamp = Message<{
118
118
  declare const Timestamp_Wkt: {
119
119
  fromJson(json: JsonValue): Timestamp;
120
120
  toJson(msg: Timestamp): JsonValue;
121
- toDate(msg: Timestamp): Date;
121
+ toDate(msg: Timestamp | null | undefined): Date | null;
122
+ fromDate(value: Date | null | undefined): Timestamp;
122
123
  };
123
124
  export declare const Timestamp: MessageType<Timestamp> & typeof Timestamp_Wkt;
124
125
  export {};
@@ -89,8 +89,20 @@ const Timestamp_Wkt = {
89
89
  return new Date(ms).toISOString().replace(".000Z", z);
90
90
  },
91
91
  toDate(msg) {
92
+ if (!msg?.seconds && !msg?.nanos) {
93
+ return null;
94
+ }
92
95
  return new Date(Number(msg.seconds ?? 0) * 1000 + Math.ceil((msg.nanos ?? 0) / 1000000));
93
96
  },
97
+ fromDate(value) {
98
+ if (value == null) {
99
+ return {};
100
+ }
101
+ const ms = value.getTime();
102
+ const seconds = Math.floor(ms / 1000);
103
+ const nanos = (ms % 1000) * 1000000;
104
+ return { seconds: protoInt64.parse(seconds), nanos: nanos };
105
+ },
94
106
  };
95
107
  // Timestamp contains the message type declaration for Timestamp.
96
108
  export const Timestamp = createMessageType({
@@ -68,7 +68,7 @@ export const DoubleValue = createMessageType({
68
68
  packedByDefault: true,
69
69
  fieldWrapper: {
70
70
  wrapField(value) {
71
- return DoubleValue.create({ value: value ?? undefined });
71
+ return DoubleValue.createComplete({ value: value ?? undefined });
72
72
  },
73
73
  unwrapField(msg) {
74
74
  return msg.value;
@@ -104,7 +104,7 @@ export const FloatValue = createMessageType({
104
104
  packedByDefault: true,
105
105
  fieldWrapper: {
106
106
  wrapField(value) {
107
- return FloatValue.create({ value: value ?? undefined });
107
+ return FloatValue.createComplete({ value: value ?? undefined });
108
108
  },
109
109
  unwrapField(msg) {
110
110
  return msg.value;
@@ -140,7 +140,7 @@ export const Int64Value = createMessageType({
140
140
  packedByDefault: true,
141
141
  fieldWrapper: {
142
142
  wrapField(value) {
143
- return Int64Value.create({ value: value ?? undefined });
143
+ return Int64Value.createComplete({ value: value ?? undefined });
144
144
  },
145
145
  unwrapField(msg) {
146
146
  return msg.value;
@@ -176,7 +176,7 @@ export const UInt64Value = createMessageType({
176
176
  packedByDefault: true,
177
177
  fieldWrapper: {
178
178
  wrapField(value) {
179
- return UInt64Value.create({ value: value ?? undefined });
179
+ return UInt64Value.createComplete({ value: value ?? undefined });
180
180
  },
181
181
  unwrapField(msg) {
182
182
  return msg.value;
@@ -212,7 +212,7 @@ export const Int32Value = createMessageType({
212
212
  packedByDefault: true,
213
213
  fieldWrapper: {
214
214
  wrapField(value) {
215
- return Int32Value.create({ value: value ?? undefined });
215
+ return Int32Value.createComplete({ value: value ?? undefined });
216
216
  },
217
217
  unwrapField(msg) {
218
218
  return msg.value;
@@ -248,7 +248,7 @@ export const UInt32Value = createMessageType({
248
248
  packedByDefault: true,
249
249
  fieldWrapper: {
250
250
  wrapField(value) {
251
- return UInt32Value.create({ value: value ?? undefined });
251
+ return UInt32Value.createComplete({ value: value ?? undefined });
252
252
  },
253
253
  unwrapField(msg) {
254
254
  return msg.value;
@@ -284,7 +284,7 @@ export const BoolValue = createMessageType({
284
284
  packedByDefault: true,
285
285
  fieldWrapper: {
286
286
  wrapField(value) {
287
- return BoolValue.create({ value: value ?? undefined });
287
+ return BoolValue.createComplete({ value: value ?? undefined });
288
288
  },
289
289
  unwrapField(msg) {
290
290
  return msg.value;
@@ -320,7 +320,7 @@ export const StringValue = createMessageType({
320
320
  packedByDefault: true,
321
321
  fieldWrapper: {
322
322
  wrapField(value) {
323
- return StringValue.create({ value: value ?? undefined });
323
+ return StringValue.createComplete({ value: value ?? undefined });
324
324
  },
325
325
  unwrapField(msg) {
326
326
  return msg.value;
@@ -356,7 +356,7 @@ export const BytesValue = createMessageType({
356
356
  packedByDefault: true,
357
357
  fieldWrapper: {
358
358
  wrapField(value) {
359
- return BytesValue.create({ value: value ?? undefined });
359
+ return BytesValue.createComplete({ value: value ?? undefined });
360
360
  },
361
361
  unwrapField(msg) {
362
362
  return msg.value;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  export { Message, CompleteMessage, AnyMessage, MessageType, Field, compareMessages, createMessageType, } from "./message.js";
2
2
  export { ServiceType, MethodInfo, MethodInfoUnary, MethodInfoServerStreaming, MethodInfoClientStreaming, MethodInfoBiDiStreaming, MethodKind, MethodIdempotency, } from "./service-type.js";
3
3
  export { isCompleteMessage, isCompleteField } from "./is-message.js";
4
- export { newFieldList, FieldList, PartialFieldInfo, FieldInfo, OneofInfo, fieldJsonName, } from "./field.js";
4
+ export { FieldList, PartialFieldInfo, FieldInfo, ScalarFieldInfo, EnumFieldInfo, MessageFieldInfo, MapFieldInfo, OneofInfo, newFieldList, fieldJsonName, } from "./field.js";
5
5
  export { applyPartialMessage } from "./partial.js";
6
6
  export { scalarEquals, scalarZeroValue, isScalarZeroValue, ScalarType, ScalarValue, LongType, } from "./scalar.js";
7
- export { createEnumType, normalizeEnumValue } from "./enum.js";
7
+ export { EnumType, EnumValueInfo, createEnumType, enumInfoZeroValue, enumZeroValue, enumDescZeroValue, normalizeEnumValue, } from "./enum.js";
8
8
  export { localName, localFieldName, localOneofName, findEnumSharedPrefix, camelToSnakeCase, protoCamelCase, safeObjectProperty, safeIdentifier, } from "./names.js";
9
9
  export { int64FromString, int64ToString, uInt64ToString } from "./varint.js";
10
10
  export { protoInt64 } from "./proto-int64.js";
@@ -15,3 +15,4 @@ export { DescComments, AnyDesc, DescEnum, DescEnumValue, DescExtension, DescFiel
15
15
  export { jsonReadEnum, jsonReadField, jsonReadMapKey, jsonReadScalar, jsonReadMessage, jsonWriteEnum, jsonWriteField, jsonWriteScalar, jsonWriteMessage, jsonDebugValue, JsonValue, JsonObject, JsonReadOptions, jsonMakeReadOptions, JsonWriteOptions, JsonWriteStringOptions, jsonMakeWriteOptions, } from "./json.js";
16
16
  export { binaryReadField, binaryReadMapEntry, binaryReadScalar, binaryReadScalarLTString, binaryReadMessage, binaryWriteField, binaryWriteScalar, binaryWritePacked, binaryWriteMapEntry, binaryWriteMessage, binaryMakeReadOptions, binaryMakeWriteOptions, BinaryReadOptions, BinaryWriteOptions, } from "./binary.js";
17
17
  export type { IMessageTypeRegistry, IServiceTypeRegistry, IEnumTypeRegistry, } from "./type-registry.js";
18
+ export { compareFieldZeroValue, isMessageZeroValue, getFieldZeroValue, } from "./zero-value.js";
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  export { compareMessages, createMessageType, } from "./message.js";
2
2
  export { MethodKind, MethodIdempotency, } from "./service-type.js";
3
3
  export { isCompleteMessage, isCompleteField } from "./is-message.js";
4
- export { newFieldList, FieldList, fieldJsonName, } from "./field.js";
4
+ export { FieldList, newFieldList, fieldJsonName, } from "./field.js";
5
5
  export { applyPartialMessage } from "./partial.js";
6
6
  export { scalarEquals, scalarZeroValue, isScalarZeroValue, ScalarType, LongType, } from "./scalar.js";
7
- export { createEnumType, normalizeEnumValue } from "./enum.js";
7
+ export { createEnumType, enumInfoZeroValue, enumZeroValue, enumDescZeroValue, normalizeEnumValue, } from "./enum.js";
8
8
  export { localName, localFieldName, localOneofName, findEnumSharedPrefix, camelToSnakeCase, protoCamelCase, safeObjectProperty, safeIdentifier, } from "./names.js";
9
9
  export { int64FromString, int64ToString, uInt64ToString } from "./varint.js";
10
10
  export { protoInt64 } from "./proto-int64.js";
@@ -13,3 +13,4 @@ export { protoDouble } from "./proto-double.js";
13
13
  export { Timestamp, Duration, Any, Empty, DoubleValue, FloatValue, Int64Value, UInt64Value, Int32Value, UInt32Value, BoolValue, StringValue, BytesValue, Value, NullValue, ListValue, Struct, } from "./google/index.js";
14
14
  export { jsonReadEnum, jsonReadField, jsonReadMapKey, jsonReadScalar, jsonReadMessage, jsonWriteEnum, jsonWriteField, jsonWriteScalar, jsonWriteMessage, jsonDebugValue, jsonMakeReadOptions, jsonMakeWriteOptions, } from "./json.js";
15
15
  export { binaryReadField, binaryReadMapEntry, binaryReadScalar, binaryReadScalarLTString, binaryReadMessage, binaryWriteField, binaryWriteScalar, binaryWritePacked, binaryWriteMapEntry, binaryWriteMessage, binaryMakeReadOptions, binaryMakeWriteOptions, } from "./binary.js";
16
+ export { compareFieldZeroValue, isMessageZeroValue, getFieldZeroValue, } from "./zero-value.js";
package/dist/json.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { isFieldSet, resolveMessageType, } from "./field.js";
2
2
  import { assert, assertFloat32, assertInt32, assertUInt32 } from "./assert.js";
3
- import { LongType, ScalarType, scalarZeroValue, } from "./scalar.js";
4
- import { wrapField } from "./field-wrapper.js";
3
+ import { LongType, ScalarType, isScalarZeroValue, normalizeScalarValue, scalarZeroValue, } from "./scalar.js";
4
+ import { unwrapField, wrapField } from "./field-wrapper.js";
5
+ import { enumZeroValue, normalizeEnumValue } from "./enum.js";
5
6
  import { protoInt64 } from "./proto-int64.js";
6
7
  import { protoBase64 } from "./proto-base64.js";
7
8
  // Default options for parsing JSON.
@@ -65,7 +66,7 @@ function readMessage(fields, typeName, json, options, message) {
65
66
  return message;
66
67
  }
67
68
  function writeMessage(message, fields, options) {
68
- const json = {};
69
+ const json = Object.create(null);
69
70
  let field;
70
71
  try {
71
72
  for (field of fields.byNumber()) {
@@ -123,7 +124,7 @@ function readField(target, jsonValue, field, options) {
123
124
  switch (field.kind) {
124
125
  case "message":
125
126
  const messageType = resolveMessageType(field.T);
126
- targetArray.push(messageType.fromJson(jsonItem, options));
127
+ targetArray.push(unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonItem, options)));
127
128
  break;
128
129
  case "enum":
129
130
  const enumValue = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
@@ -155,7 +156,7 @@ function readField(target, jsonValue, field, options) {
155
156
  }
156
157
  var targetMap = target[localName];
157
158
  if (typeof targetMap !== "object") {
158
- targetMap = target[localName] = {};
159
+ targetMap = target[localName] = Object.create(null);
159
160
  }
160
161
  for (const [jsonMapKey, jsonMapValue] of Object.entries(jsonValue)) {
161
162
  if (jsonMapValue === null) {
@@ -210,11 +211,7 @@ function readField(target, jsonValue, field, options) {
210
211
  messageType.typeName != "google.protobuf.Value") {
211
212
  return;
212
213
  }
213
- let currentValue = target[localName];
214
- target[localName] = currentValue = messageType.fromJson(jsonValue, options);
215
- if (messageType.fieldWrapper && !field.oneof) {
216
- target[localName] = messageType.fieldWrapper.unwrapField(currentValue);
217
- }
214
+ target[localName] = unwrapField(messageType.fieldWrapper, messageType.fromJson(jsonValue, options));
218
215
  break;
219
216
  case "enum":
220
217
  const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, false);
@@ -418,7 +415,7 @@ export function clearField(field, target) {
418
415
  else {
419
416
  switch (field.kind) {
420
417
  case "map":
421
- target[localName] = {};
418
+ target[localName] = Object.create(null);
422
419
  break;
423
420
  case "enum":
424
421
  target[localName] = implicitPresence ? field.T.values[0].no : undefined;
@@ -436,7 +433,6 @@ export function clearField(field, target) {
436
433
  // Decide whether an unset field should be emitted with JSON write option `emitDefaultValues`
437
434
  function canEmitFieldDefaultValue(field) {
438
435
  if (field.repeated || field.kind == "map") {
439
- // maps are {}, repeated fields are []
440
436
  return true;
441
437
  }
442
438
  if (field.oneof) {
@@ -456,9 +452,9 @@ function canEmitFieldDefaultValue(field) {
456
452
  }
457
453
  function writeField(field, value, options) {
458
454
  if (field.kind == "map") {
459
- assert(typeof value == "object" && value != null);
460
- const jsonObj = {};
461
- const entries = Object.entries(value);
455
+ const jsonObj = Object.create(null);
456
+ assert(!value || typeof value === "object");
457
+ const entries = value ? Object.entries(value) : [];
462
458
  switch (field.V.kind) {
463
459
  case "scalar":
464
460
  for (const [entryKey, entryValue] of entries) {
@@ -485,24 +481,28 @@ function writeField(field, value, options) {
485
481
  : undefined;
486
482
  }
487
483
  if (field.repeated) {
488
- assert(Array.isArray(value));
484
+ assert(!value || Array.isArray(value));
489
485
  const jsonArr = [];
490
- switch (field.kind) {
491
- case "scalar":
492
- for (let i = 0; i < value.length; i++) {
493
- jsonArr.push(writeScalar(field.T, value[i]));
494
- }
495
- break;
496
- case "enum":
497
- for (let i = 0; i < value.length; i++) {
498
- jsonArr.push(writeEnum(field.T, value[i], options.enumAsInteger));
499
- }
500
- break;
501
- case "message":
502
- for (let i = 0; i < value.length; i++) {
503
- jsonArr.push(value[i].toJson(options));
504
- }
505
- break;
486
+ const valueArr = value;
487
+ if (valueArr && valueArr.length) {
488
+ switch (field.kind) {
489
+ case "scalar":
490
+ for (let i = 0; i < valueArr.length; i++) {
491
+ jsonArr.push(writeScalar(field.T, valueArr[i]));
492
+ }
493
+ break;
494
+ case "enum":
495
+ for (let i = 0; i < valueArr.length; i++) {
496
+ jsonArr.push(writeEnum(field.T, valueArr[i], options.enumAsInteger));
497
+ }
498
+ break;
499
+ case "message":
500
+ const messageType = resolveMessageType(field.T);
501
+ for (let i = 0; i < valueArr.length; i++) {
502
+ jsonArr.push(messageType.toJson(wrapField(messageType.fieldWrapper, valueArr[i])));
503
+ }
504
+ break;
505
+ }
506
506
  }
507
507
  return options.emitDefaultValues || jsonArr.length > 0 ?
508
508
  jsonArr
@@ -510,10 +510,22 @@ function writeField(field, value, options) {
510
510
  }
511
511
  switch (field.kind) {
512
512
  case "scalar":
513
+ const scalarValue = normalizeScalarValue(field.T, value, false);
514
+ if (!options.emitDefaultValues
515
+ && isScalarZeroValue(field.T, scalarValue)) {
516
+ return undefined;
517
+ }
513
518
  return writeScalar(field.T, value);
514
519
  case "enum":
520
+ const enumValue = normalizeEnumValue(field.T, value);
521
+ if (!options.emitDefaultValues && enumZeroValue(field.T) === enumValue) {
522
+ return undefined;
523
+ }
515
524
  return writeEnum(field.T, value, options.enumAsInteger);
516
525
  case "message":
526
+ if (!options.emitDefaultValues && value == null) {
527
+ return undefined;
528
+ }
517
529
  const messageType = resolveMessageType(field.T);
518
530
  return messageType.toJson(wrapField(messageType.fieldWrapper, value));
519
531
  }