@bufbuild/protobuf 1.7.0 → 1.7.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.
@@ -1,5 +1,6 @@
1
1
  import { FeatureSetDefaults, FileDescriptorProto, FileDescriptorSet } from "./google/protobuf/descriptor_pb.js";
2
2
  import type { DescriptorSet } from "./descriptor-set.js";
3
+ import type { BinaryReadOptions, BinaryWriteOptions } from "./binary-format.js";
3
4
  /**
4
5
  * Create a DescriptorSet, a convenient interface for working with a set of
5
6
  * google.protobuf.FileDescriptorProto.
@@ -26,5 +27,10 @@ interface CreateDescriptorSetOptions {
26
27
  * `--experimental_edition_defaults_out`.
27
28
  */
28
29
  featureSetDefaults?: FeatureSetDefaults;
30
+ /**
31
+ * Internally, data is serialized when features are resolved. The
32
+ * serialization options given here will be used for feature resolution.
33
+ */
34
+ serializationOptions?: Partial<BinaryReadOptions & BinaryWriteOptions>;
29
35
  }
30
36
  export {};
@@ -44,11 +44,11 @@ function createDescriptorSet(input, options) {
44
44
  : input;
45
45
  const resolverByEdition = new Map();
46
46
  const files = fileDescriptors.map((proto) => {
47
- var _a, _b;
47
+ var _a;
48
48
  const edition = (_a = proto.edition) !== null && _a !== void 0 ? _a : parseFileSyntax(proto.syntax, proto.edition).edition;
49
49
  let resolveFeatures = resolverByEdition.get(edition);
50
50
  if (resolveFeatures === undefined) {
51
- resolveFeatures = (0, feature_set_js_1.createFeatureResolver)((_b = options === null || options === void 0 ? void 0 : options.featureSetDefaults) !== null && _b !== void 0 ? _b : feature_set_js_1.featureSetDefaults, edition);
51
+ resolveFeatures = (0, feature_set_js_1.createFeatureResolver)(edition, options === null || options === void 0 ? void 0 : options.featureSetDefaults, options === null || options === void 0 ? void 0 : options.serializationOptions);
52
52
  resolverByEdition.set(edition, resolveFeatures);
53
53
  }
54
54
  return newFile(proto, cart, resolveFeatures);
@@ -21,7 +21,7 @@ exports.GeneratedCodeInfo_Annotation_Semantic = exports.GeneratedCodeInfo_Annota
21
21
  // The messages in this file describe the definitions found in .proto files.
22
22
  // A valid .proto file can be translated directly to a FileDescriptorProto
23
23
  // without any other information (e.g. without reading its imports).
24
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
24
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
25
25
  // @generated from file google/protobuf/descriptor.proto (package google.protobuf, syntax proto2)
26
26
  /* eslint-disable */
27
27
  const proto2_js_1 = require("../../proto2.js");
@@ -14,7 +14,7 @@
14
14
  // limitations under the License.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.ListValue = exports.Value = exports.Struct = exports.NullValue = void 0;
17
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
17
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
18
18
  // @generated from file google/protobuf/struct.proto (package google.protobuf, syntax proto3)
19
19
  /* eslint-disable */
20
20
  const proto3_js_1 = require("../../proto3.js");
@@ -14,7 +14,7 @@
14
14
  // limitations under the License.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.Option = exports.EnumValue = exports.Enum = exports.Field_Cardinality = exports.Field_Kind = exports.Field = exports.Type = exports.Syntax = void 0;
17
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
17
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
18
18
  // @generated from file google/protobuf/type.proto (package google.protobuf, syntax proto3)
19
19
  /* eslint-disable */
20
20
  const proto3_js_1 = require("../../proto3.js");
@@ -1,8 +1,5 @@
1
1
  import { Edition, FeatureSet, FeatureSetDefaults } from "../google/protobuf/descriptor_pb.js";
2
- /**
3
- * Static edition feature defaults supported by @bufbuild/protobuf.
4
- */
5
- export declare const featureSetDefaults: FeatureSetDefaults;
2
+ import type { BinaryReadOptions, BinaryWriteOptions } from "../binary-format.js";
6
3
  /**
7
4
  * A merged google.protobuf.FeaturesSet, with all fields guaranteed to be set.
8
5
  */
@@ -16,6 +13,7 @@ export type MergedFeatureSet = FeatureSet & Required<FeatureSet>;
16
13
  */
17
14
  export type FeatureResolverFn = (a?: FeatureSet, b?: FeatureSet) => MergedFeatureSet;
18
15
  /**
19
- * Create an edition feature resolver with the given feature set defaults.
16
+ * Create an edition feature resolver with the given feature set defaults, or
17
+ * the feature set defaults supported by @bufbuild/protobuf.
20
18
  */
21
- export declare function createFeatureResolver(compiledFeatureSetDefaults: FeatureSetDefaults, edition: Edition): FeatureResolverFn;
19
+ export declare function createFeatureResolver(edition: Edition, compiledFeatureSetDefaults?: FeatureSetDefaults, serializationOptions?: Partial<BinaryReadOptions & BinaryWriteOptions>): FeatureResolverFn;
@@ -13,24 +13,28 @@
13
13
  // See the License for the specific language governing permissions and
14
14
  // limitations under the License.
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.createFeatureResolver = exports.featureSetDefaults = void 0;
16
+ exports.createFeatureResolver = void 0;
17
17
  const descriptor_pb_js_1 = require("../google/protobuf/descriptor_pb.js");
18
18
  const proto_base64_js_1 = require("../proto-base64.js");
19
19
  /**
20
- * Static edition feature defaults supported by @bufbuild/protobuf.
20
+ * Return the edition feature defaults supported by @bufbuild/protobuf.
21
21
  */
22
- exports.featureSetDefaults = descriptor_pb_js_1.FeatureSetDefaults.fromBinary(proto_base64_js_1.protoBase64.dec(
23
- /*upstream-inject-feature-defaults-start*/ "ChESDAgBEAIYAiABKAEwAhjmBwoREgwIAhABGAEgAigBMAEY5wcKERIMCAEQARgBIAIoATABGOgHIOYHKOgH" /*upstream-inject-feature-defaults-end*/));
22
+ function getFeatureSetDefaults(options) {
23
+ return descriptor_pb_js_1.FeatureSetDefaults.fromBinary(proto_base64_js_1.protoBase64.dec(
24
+ /*upstream-inject-feature-defaults-start*/ "ChESDAgBEAIYAiABKAEwAhjmBwoREgwIAhABGAEgAigBMAEY5wcKERIMCAEQARgBIAIoATABGOgHIOYHKOgH" /*upstream-inject-feature-defaults-end*/), options);
25
+ }
24
26
  /**
25
- * Create an edition feature resolver with the given feature set defaults.
27
+ * Create an edition feature resolver with the given feature set defaults, or
28
+ * the feature set defaults supported by @bufbuild/protobuf.
26
29
  */
27
- function createFeatureResolver(compiledFeatureSetDefaults, edition) {
30
+ function createFeatureResolver(edition, compiledFeatureSetDefaults, serializationOptions) {
28
31
  var _a, _b;
29
- const min = compiledFeatureSetDefaults.minimumEdition;
30
- const max = compiledFeatureSetDefaults.maximumEdition;
32
+ const fds = compiledFeatureSetDefaults !== null && compiledFeatureSetDefaults !== void 0 ? compiledFeatureSetDefaults : getFeatureSetDefaults(serializationOptions);
33
+ const min = fds.minimumEdition;
34
+ const max = fds.maximumEdition;
31
35
  if (min === undefined ||
32
36
  max === undefined ||
33
- compiledFeatureSetDefaults.defaults.some((d) => d.edition === undefined)) {
37
+ fds.defaults.some((d) => d.edition === undefined)) {
34
38
  throw new Error("Invalid FeatureSetDefaults");
35
39
  }
36
40
  if (edition < min) {
@@ -40,7 +44,7 @@ function createFeatureResolver(compiledFeatureSetDefaults, edition) {
40
44
  throw new Error(`Edition ${descriptor_pb_js_1.Edition[edition]} is later than the maximum supported edition ${descriptor_pb_js_1.Edition[max]}`);
41
45
  }
42
46
  let highestMatch = undefined;
43
- for (const c of compiledFeatureSetDefaults.defaults) {
47
+ for (const c of fds.defaults) {
44
48
  const e = (_a = c.edition) !== null && _a !== void 0 ? _a : 0;
45
49
  if (e > edition) {
46
50
  continue;
@@ -56,12 +60,12 @@ function createFeatureResolver(compiledFeatureSetDefaults, edition) {
56
60
  if (highestMatch === undefined) {
57
61
  throw new Error(`No valid default found for edition ${descriptor_pb_js_1.Edition[edition]}`);
58
62
  }
59
- const defaultsBin = highestMatch.f.toBinary();
63
+ const featureSetBin = highestMatch.f.toBinary(serializationOptions);
60
64
  return (...rest) => {
61
- const f = descriptor_pb_js_1.FeatureSet.fromBinary(defaultsBin);
65
+ const f = descriptor_pb_js_1.FeatureSet.fromBinary(featureSetBin, serializationOptions);
62
66
  for (const c of rest) {
63
67
  if (c !== undefined) {
64
- f.fromBinary(c.toBinary());
68
+ f.fromBinary(c.toBinary(serializationOptions), serializationOptions);
65
69
  }
66
70
  }
67
71
  if (!validateMergedFeatures(f)) {
@@ -3,7 +3,7 @@ import type { FieldInfo } from "../field.js";
3
3
  import { ScalarType } from "../field.js";
4
4
  import type { EnumType } from "../enum.js";
5
5
  type JsonFormatWriteFieldFn = (field: FieldInfo, value: any, options: JsonWriteOptions) => JsonValue | undefined;
6
- export declare function makeJsonFormatCommon(makeWriteField: (writeEnumFn: typeof writeEnum, writeScalarFn: typeof writeScalar) => JsonFormatWriteFieldFn): JsonFormat;
7
- declare function writeEnum(type: EnumType, value: number | undefined, emitIntrinsicDefault: boolean, enumAsInteger: boolean): JsonValue | undefined;
8
- declare function writeScalar(type: ScalarType, value: any, emitIntrinsicDefault: boolean): JsonValue | undefined;
6
+ export declare function makeJsonFormatCommon(nullAsZeroValue: boolean, makeWriteField: (writeEnumFn: typeof writeEnum, writeScalarFn: typeof writeScalar) => JsonFormatWriteFieldFn): JsonFormat;
7
+ declare function writeEnum(type: EnumType, value: number | undefined, emitZeroValue: boolean, enumAsInteger: boolean): JsonValue | undefined;
8
+ declare function writeScalar(type: ScalarType, value: any, emitZeroValue: boolean): JsonValue | undefined;
9
9
  export {};
@@ -39,7 +39,7 @@ function makeReadOptions(options) {
39
39
  function makeWriteOptions(options) {
40
40
  return options ? Object.assign(Object.assign({}, jsonWriteDefaults), options) : jsonWriteDefaults;
41
41
  }
42
- function makeJsonFormatCommon(makeWriteField) {
42
+ function makeJsonFormatCommon(nullAsZeroValue, makeWriteField) {
43
43
  const writeField = makeWriteField(writeEnum, writeScalar);
44
44
  return {
45
45
  makeReadOptions,
@@ -65,7 +65,7 @@ function makeJsonFormatCommon(makeWriteField) {
65
65
  }
66
66
  oneofSeen.set(field.oneof, jsonKey);
67
67
  }
68
- readField(message, jsonValue, field, options, type);
68
+ readField(message, jsonValue, field, options, type, nullAsZeroValue);
69
69
  }
70
70
  else {
71
71
  let found = false;
@@ -76,7 +76,7 @@ function makeJsonFormatCommon(makeWriteField) {
76
76
  if (ext && ext.extendee.typeName == type.typeName) {
77
77
  found = true;
78
78
  const [container, get] = (0, extensions_js_1.createExtensionContainer)(ext);
79
- readField(container, jsonValue, ext.field, options, ext);
79
+ readField(container, jsonValue, ext.field, options, ext, true);
80
80
  // We pass on the options as BinaryReadOptions/BinaryWriteOptions,
81
81
  // so that users can bring their own binary reader and writer factories
82
82
  // if necessary.
@@ -142,7 +142,7 @@ function makeJsonFormatCommon(makeWriteField) {
142
142
  }
143
143
  return json;
144
144
  },
145
- readScalar,
145
+ readScalar: (type, json, longType) => readScalar(type, json, longType, nullAsZeroValue), // eslint-disable-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
146
146
  writeScalar,
147
147
  debug: debugJsonValue,
148
148
  };
@@ -163,8 +163,11 @@ function debugJsonValue(json) {
163
163
  }
164
164
  // Read a JSON value for a field.
165
165
  // The "type" argument is only used to provide context in errors.
166
+ // The argument "nullAsZeroValue" applies to singular fields (not repeated, map,
167
+ // or oneof). If the argument is `true`, JSON null is parsed as the fields
168
+ // zero-value. If the argument is false, JSON null sets the field to `undefined`.
166
169
  function readField(target, // eslint-disable-line @typescript-eslint/no-explicit-any -- `any` is the best choice for dynamic access
167
- jsonValue, field, options, type) {
170
+ jsonValue, field, options, type, nullAsZeroValue) {
168
171
  let localName = field.localName;
169
172
  if (field.oneof) {
170
173
  if (jsonValue === null && field.kind == "scalar") {
@@ -193,13 +196,13 @@ jsonValue, field, options, type) {
193
196
  val = field.T.fromJson(jsonItem, options);
194
197
  break;
195
198
  case "enum":
196
- val = readEnum(field.T, jsonItem, options.ignoreUnknownFields);
199
+ val = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
197
200
  if (val === undefined)
198
201
  continue;
199
202
  break;
200
203
  case "scalar":
201
204
  try {
202
- val = readScalar(field.T, jsonItem, field.L);
205
+ val = readScalar(field.T, jsonItem, field.L, true);
203
206
  }
204
207
  catch (e) {
205
208
  let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonItem)}`;
@@ -231,13 +234,13 @@ jsonValue, field, options, type) {
231
234
  val = field.V.T.fromJson(jsonMapValue, options);
232
235
  break;
233
236
  case "enum":
234
- val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields);
237
+ val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields, true);
235
238
  if (val === undefined)
236
239
  continue;
237
240
  break;
238
241
  case "scalar":
239
242
  try {
240
- val = readScalar(field.V.T, jsonMapValue, field_js_1.LongType.BIGINT);
243
+ val = readScalar(field.V.T, jsonMapValue, field_js_1.LongType.BIGINT, true);
241
244
  }
242
245
  catch (e) {
243
246
  let m = `cannot decode map value for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -255,7 +258,7 @@ jsonValue, field, options, type) {
255
258
  : jsonMapKey == "false"
256
259
  ? false
257
260
  : jsonMapKey
258
- : jsonMapKey, field_js_1.LongType.BIGINT).toString()] = val;
261
+ : jsonMapKey, field_js_1.LongType.BIGINT, true).toString()] = val;
259
262
  }
260
263
  catch (e) {
261
264
  let m = `cannot decode map key for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -288,14 +291,14 @@ jsonValue, field, options, type) {
288
291
  }
289
292
  break;
290
293
  case "enum":
291
- const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields);
294
+ const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, nullAsZeroValue);
292
295
  if (enumValue !== undefined) {
293
296
  target[localName] = enumValue;
294
297
  }
295
298
  break;
296
299
  case "scalar":
297
300
  try {
298
- target[localName] = readScalar(field.T, jsonValue, field.L);
301
+ target[localName] = readScalar(field.T, jsonValue, field.L, nullAsZeroValue);
299
302
  }
300
303
  catch (e) {
301
304
  let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -310,7 +313,10 @@ jsonValue, field, options, type) {
310
313
  }
311
314
  // May throw an error. If the error message is non-blank, it should be shown.
312
315
  // It is up to the caller to provide context.
313
- function readScalar(type, json, longType) {
316
+ // If the argument "nullAsZeroValue" is `true`, this function returns the zero-value
317
+ // for JSON null. If the argument is `false`, this function returns `undefined`
318
+ // for JSON null.
319
+ function readScalar(type, json, longType, nullAsZeroValue) {
314
320
  // every valid case in the switch below returns, and every fall
315
321
  // through is regarded as a failure.
316
322
  switch (type) {
@@ -319,7 +325,7 @@ function readScalar(type, json, longType) {
319
325
  case field_js_1.ScalarType.DOUBLE:
320
326
  case field_js_1.ScalarType.FLOAT:
321
327
  if (json === null)
322
- return 0.0; // TODO field presence: this should reset the field with explicit presence
328
+ return nullAsZeroValue ? 0.0 : undefined;
323
329
  if (json === "NaN")
324
330
  return Number.NaN;
325
331
  if (json === "Infinity")
@@ -356,7 +362,7 @@ function readScalar(type, json, longType) {
356
362
  case field_js_1.ScalarType.SINT32:
357
363
  case field_js_1.ScalarType.UINT32:
358
364
  if (json === null)
359
- return 0; // TODO field presence: this should reset the field with explicit presence
365
+ return nullAsZeroValue ? 0 : undefined;
360
366
  let int32;
361
367
  if (typeof json == "number")
362
368
  int32 = json;
@@ -376,7 +382,7 @@ function readScalar(type, json, longType) {
376
382
  case field_js_1.ScalarType.SFIXED64:
377
383
  case field_js_1.ScalarType.SINT64:
378
384
  if (json === null)
379
- return proto_int64_js_1.protoInt64.zero; // TODO field presence: this should reset the field with explicit presence
385
+ return nullAsZeroValue ? proto_int64_js_1.protoInt64.zero : undefined;
380
386
  if (typeof json != "number" && typeof json != "string")
381
387
  break;
382
388
  const long = proto_int64_js_1.protoInt64.parse(json);
@@ -385,7 +391,7 @@ function readScalar(type, json, longType) {
385
391
  case field_js_1.ScalarType.FIXED64:
386
392
  case field_js_1.ScalarType.UINT64:
387
393
  if (json === null)
388
- return proto_int64_js_1.protoInt64.zero; // TODO field presence: this should reset the field with explicit presence
394
+ return nullAsZeroValue ? proto_int64_js_1.protoInt64.zero : undefined;
389
395
  if (typeof json != "number" && typeof json != "string")
390
396
  break;
391
397
  const uLong = proto_int64_js_1.protoInt64.uParse(json);
@@ -394,14 +400,14 @@ function readScalar(type, json, longType) {
394
400
  // bool:
395
401
  case field_js_1.ScalarType.BOOL:
396
402
  if (json === null)
397
- return false; // TODO field presence: this should reset the field with explicit presence
403
+ return nullAsZeroValue ? false : undefined;
398
404
  if (typeof json !== "boolean")
399
405
  break;
400
406
  return json;
401
407
  // string:
402
408
  case field_js_1.ScalarType.STRING:
403
409
  if (json === null)
404
- return ""; // TODO field presence: this should reset the field with explicit presence
410
+ return nullAsZeroValue ? "" : undefined;
405
411
  if (typeof json !== "string") {
406
412
  break;
407
413
  }
@@ -417,19 +423,24 @@ function readScalar(type, json, longType) {
417
423
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
418
424
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
419
425
  case field_js_1.ScalarType.BYTES:
420
- if (json === null || json === "")
421
- return new Uint8Array(0); // TODO field presence: this should reset the field with explicit presence
426
+ if (json === null)
427
+ return nullAsZeroValue ? new Uint8Array(0) : undefined;
428
+ if (json === "")
429
+ return new Uint8Array(0);
422
430
  if (typeof json !== "string")
423
431
  break;
424
432
  return proto_base64_js_1.protoBase64.dec(json);
425
433
  }
426
434
  throw new Error();
427
435
  }
428
- function readEnum(type, json, ignoreUnknownFields) {
436
+ // If the argument "nullAsZeroValue" is `true`, this function returns the first
437
+ // enum value for JSON null. If the argument is `false`, this function returns
438
+ // `undefined`.
439
+ function readEnum(type, json, ignoreUnknownFields, nullAsZeroValue) {
429
440
  if (json === null) {
430
- // proto3 requires 0 to be default value for all enums
431
- // TODO field presence: this should reset the field with explicit presence
432
- return 0;
441
+ // proto3 requires 0 to be default value for all enums (which is required to be the first value).
442
+ // proto2 uses the first value (at least with protoc-gen-go).
443
+ return nullAsZeroValue ? type.values[0].no : undefined;
433
444
  }
434
445
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
435
446
  switch (typeof json) {
@@ -448,12 +459,12 @@ function readEnum(type, json, ignoreUnknownFields) {
448
459
  }
449
460
  throw new Error(`cannot decode enum ${type.typeName} from JSON: ${debugJsonValue(json)}`);
450
461
  }
451
- function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
462
+ function writeEnum(type, value, emitZeroValue, enumAsInteger) {
452
463
  var _a;
453
464
  if (value === undefined) {
454
465
  return value;
455
466
  }
456
- if (value === 0 && !emitIntrinsicDefault) {
467
+ if (!emitZeroValue && type.values[0].no === value) {
457
468
  // proto3 requires 0 to be default value for all enums
458
469
  return undefined;
459
470
  }
@@ -466,7 +477,7 @@ function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
466
477
  const val = type.findNumber(value);
467
478
  return (_a = val === null || val === void 0 ? void 0 : val.name) !== null && _a !== void 0 ? _a : value; // if we don't know the enum value, just return the number
468
479
  }
469
- function writeScalar(type, value, emitIntrinsicDefault) {
480
+ function writeScalar(type, value, emitZeroValue) {
470
481
  if (value === undefined) {
471
482
  return undefined;
472
483
  }
@@ -478,7 +489,7 @@ function writeScalar(type, value, emitIntrinsicDefault) {
478
489
  case field_js_1.ScalarType.FIXED32:
479
490
  case field_js_1.ScalarType.UINT32:
480
491
  (0, assert_js_1.assert)(typeof value == "number");
481
- return value != 0 || emitIntrinsicDefault ? value : undefined;
492
+ return value != 0 || emitZeroValue ? value : undefined;
482
493
  // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".
483
494
  // Either numbers or strings are accepted. Exponent notation is also accepted.
484
495
  case field_js_1.ScalarType.FLOAT:
@@ -491,15 +502,15 @@ function writeScalar(type, value, emitIntrinsicDefault) {
491
502
  return "Infinity";
492
503
  if (value === Number.NEGATIVE_INFINITY)
493
504
  return "-Infinity";
494
- return value !== 0 || emitIntrinsicDefault ? value : undefined;
505
+ return value !== 0 || emitZeroValue ? value : undefined;
495
506
  // string:
496
507
  case field_js_1.ScalarType.STRING:
497
508
  (0, assert_js_1.assert)(typeof value == "string");
498
- return value.length > 0 || emitIntrinsicDefault ? value : undefined;
509
+ return value.length > 0 || emitZeroValue ? value : undefined;
499
510
  // bool:
500
511
  case field_js_1.ScalarType.BOOL:
501
512
  (0, assert_js_1.assert)(typeof value == "boolean");
502
- return value || emitIntrinsicDefault ? value : undefined;
513
+ return value || emitZeroValue ? value : undefined;
503
514
  // JSON value will be a decimal string. Either numbers or strings are accepted.
504
515
  case field_js_1.ScalarType.UINT64:
505
516
  case field_js_1.ScalarType.FIXED64:
@@ -512,14 +523,12 @@ function writeScalar(type, value, emitIntrinsicDefault) {
512
523
  // We use implicit conversion with `value != 0` to catch both 0n and "0"
513
524
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
514
525
  // @ts-ignore
515
- return emitIntrinsicDefault || value != 0
516
- ? value.toString(10)
517
- : undefined;
526
+ return emitZeroValue || value != 0 ? value.toString(10) : undefined;
518
527
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
519
528
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
520
529
  case field_js_1.ScalarType.BYTES:
521
530
  (0, assert_js_1.assert)(value instanceof Uint8Array);
522
- return emitIntrinsicDefault || value.byteLength > 0
531
+ return emitZeroValue || value.byteLength > 0
523
532
  ? proto_base64_js_1.protoBase64.enc(value)
524
533
  : undefined;
525
534
  }
@@ -19,8 +19,8 @@ const assert_js_1 = require("./assert.js");
19
19
  const json_format_common_js_1 = require("./json-format-common.js");
20
20
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
21
21
  function makeJsonFormatProto2() {
22
- // TODO field presence: merge this function with proto2
23
- return (0, json_format_common_js_1.makeJsonFormatCommon)((writeEnum, writeScalar) => {
22
+ // TODO field presence: merge this function with proto3. the reading side already switches behavior on the nullAsZeroValue argument.
23
+ return (0, json_format_common_js_1.makeJsonFormatCommon)(false, (writeEnum, writeScalar) => {
24
24
  return function writeField(field, value, options) {
25
25
  if (field.kind == "map") {
26
26
  const jsonObj = {};
@@ -19,8 +19,8 @@ const assert_js_1 = require("./assert.js");
19
19
  const json_format_common_js_1 = require("./json-format-common.js");
20
20
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
21
21
  function makeJsonFormatProto3() {
22
- // TODO field presence: merge this function with proto2
23
- return (0, json_format_common_js_1.makeJsonFormatCommon)((writeEnum, writeScalar) => {
22
+ // TODO field presence: merge this function with proto2. the reading side already switches behavior on the nullAsZeroValue argument.
23
+ return (0, json_format_common_js_1.makeJsonFormatCommon)(true, (writeEnum, writeScalar) => {
24
24
  return function writeField(field, value, options) {
25
25
  if (field.kind == "map") {
26
26
  const jsonObj = {};
@@ -1,5 +1,6 @@
1
1
  import { FeatureSetDefaults, FileDescriptorProto, FileDescriptorSet } from "./google/protobuf/descriptor_pb.js";
2
2
  import type { DescriptorSet } from "./descriptor-set.js";
3
+ import type { BinaryReadOptions, BinaryWriteOptions } from "./binary-format.js";
3
4
  /**
4
5
  * Create a DescriptorSet, a convenient interface for working with a set of
5
6
  * google.protobuf.FileDescriptorProto.
@@ -26,5 +27,10 @@ interface CreateDescriptorSetOptions {
26
27
  * `--experimental_edition_defaults_out`.
27
28
  */
28
29
  featureSetDefaults?: FeatureSetDefaults;
30
+ /**
31
+ * Internally, data is serialized when features are resolved. The
32
+ * serialization options given here will be used for feature resolution.
33
+ */
34
+ serializationOptions?: Partial<BinaryReadOptions & BinaryWriteOptions>;
29
35
  }
30
36
  export {};
@@ -17,7 +17,7 @@ import { LongType, ScalarType } from "./field.js";
17
17
  import { MethodIdempotency, MethodKind } from "./service-type.js";
18
18
  import { fieldJsonName, findEnumSharedPrefix } from "./private/names.js";
19
19
  import { parseTextFormatEnumValue, parseTextFormatScalarValue, } from "./private/text-format.js";
20
- import { createFeatureResolver, featureSetDefaults, } from "./private/feature-set.js";
20
+ import { createFeatureResolver } from "./private/feature-set.js";
21
21
  /**
22
22
  * Create a DescriptorSet, a convenient interface for working with a set of
23
23
  * google.protobuf.FileDescriptorProto.
@@ -41,11 +41,11 @@ export function createDescriptorSet(input, options) {
41
41
  : input;
42
42
  const resolverByEdition = new Map();
43
43
  const files = fileDescriptors.map((proto) => {
44
- var _a, _b;
44
+ var _a;
45
45
  const edition = (_a = proto.edition) !== null && _a !== void 0 ? _a : parseFileSyntax(proto.syntax, proto.edition).edition;
46
46
  let resolveFeatures = resolverByEdition.get(edition);
47
47
  if (resolveFeatures === undefined) {
48
- resolveFeatures = createFeatureResolver((_b = options === null || options === void 0 ? void 0 : options.featureSetDefaults) !== null && _b !== void 0 ? _b : featureSetDefaults, edition);
48
+ resolveFeatures = createFeatureResolver(edition, options === null || options === void 0 ? void 0 : options.featureSetDefaults, options === null || options === void 0 ? void 0 : options.serializationOptions);
49
49
  resolverByEdition.set(edition, resolveFeatures);
50
50
  }
51
51
  return newFile(proto, cart, resolveFeatures);
@@ -18,7 +18,7 @@
18
18
  // The messages in this file describe the definitions found in .proto files.
19
19
  // A valid .proto file can be translated directly to a FileDescriptorProto
20
20
  // without any other information (e.g. without reading its imports).
21
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
21
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
22
22
  // @generated from file google/protobuf/descriptor.proto (package google.protobuf, syntax proto2)
23
23
  /* eslint-disable */
24
24
  import { proto2 } from "../../proto2.js";
@@ -11,7 +11,7 @@
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
14
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
15
15
  // @generated from file google/protobuf/struct.proto (package google.protobuf, syntax proto3)
16
16
  /* eslint-disable */
17
17
  import { proto3 } from "../../proto3.js";
@@ -11,7 +11,7 @@
11
11
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
- // @generated by protoc-gen-es v1.7.0 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
14
+ // @generated by protoc-gen-es v1.7.1 with parameter "bootstrap_wkt=true,ts_nocheck=false,target=ts"
15
15
  // @generated from file google/protobuf/type.proto (package google.protobuf, syntax proto3)
16
16
  /* eslint-disable */
17
17
  import { proto3 } from "../../proto3.js";
@@ -1,8 +1,5 @@
1
1
  import { Edition, FeatureSet, FeatureSetDefaults } from "../google/protobuf/descriptor_pb.js";
2
- /**
3
- * Static edition feature defaults supported by @bufbuild/protobuf.
4
- */
5
- export declare const featureSetDefaults: FeatureSetDefaults;
2
+ import type { BinaryReadOptions, BinaryWriteOptions } from "../binary-format.js";
6
3
  /**
7
4
  * A merged google.protobuf.FeaturesSet, with all fields guaranteed to be set.
8
5
  */
@@ -16,6 +13,7 @@ export type MergedFeatureSet = FeatureSet & Required<FeatureSet>;
16
13
  */
17
14
  export type FeatureResolverFn = (a?: FeatureSet, b?: FeatureSet) => MergedFeatureSet;
18
15
  /**
19
- * Create an edition feature resolver with the given feature set defaults.
16
+ * Create an edition feature resolver with the given feature set defaults, or
17
+ * the feature set defaults supported by @bufbuild/protobuf.
20
18
  */
21
- export declare function createFeatureResolver(compiledFeatureSetDefaults: FeatureSetDefaults, edition: Edition): FeatureResolverFn;
19
+ export declare function createFeatureResolver(edition: Edition, compiledFeatureSetDefaults?: FeatureSetDefaults, serializationOptions?: Partial<BinaryReadOptions & BinaryWriteOptions>): FeatureResolverFn;
@@ -14,20 +14,24 @@
14
14
  import { Edition, FeatureSet, FeatureSetDefaults, } from "../google/protobuf/descriptor_pb.js";
15
15
  import { protoBase64 } from "../proto-base64.js";
16
16
  /**
17
- * Static edition feature defaults supported by @bufbuild/protobuf.
17
+ * Return the edition feature defaults supported by @bufbuild/protobuf.
18
18
  */
19
- export const featureSetDefaults = FeatureSetDefaults.fromBinary(protoBase64.dec(
20
- /*upstream-inject-feature-defaults-start*/ "ChESDAgBEAIYAiABKAEwAhjmBwoREgwIAhABGAEgAigBMAEY5wcKERIMCAEQARgBIAIoATABGOgHIOYHKOgH" /*upstream-inject-feature-defaults-end*/));
19
+ function getFeatureSetDefaults(options) {
20
+ return FeatureSetDefaults.fromBinary(protoBase64.dec(
21
+ /*upstream-inject-feature-defaults-start*/ "ChESDAgBEAIYAiABKAEwAhjmBwoREgwIAhABGAEgAigBMAEY5wcKERIMCAEQARgBIAIoATABGOgHIOYHKOgH" /*upstream-inject-feature-defaults-end*/), options);
22
+ }
21
23
  /**
22
- * Create an edition feature resolver with the given feature set defaults.
24
+ * Create an edition feature resolver with the given feature set defaults, or
25
+ * the feature set defaults supported by @bufbuild/protobuf.
23
26
  */
24
- export function createFeatureResolver(compiledFeatureSetDefaults, edition) {
27
+ export function createFeatureResolver(edition, compiledFeatureSetDefaults, serializationOptions) {
25
28
  var _a, _b;
26
- const min = compiledFeatureSetDefaults.minimumEdition;
27
- const max = compiledFeatureSetDefaults.maximumEdition;
29
+ const fds = compiledFeatureSetDefaults !== null && compiledFeatureSetDefaults !== void 0 ? compiledFeatureSetDefaults : getFeatureSetDefaults(serializationOptions);
30
+ const min = fds.minimumEdition;
31
+ const max = fds.maximumEdition;
28
32
  if (min === undefined ||
29
33
  max === undefined ||
30
- compiledFeatureSetDefaults.defaults.some((d) => d.edition === undefined)) {
34
+ fds.defaults.some((d) => d.edition === undefined)) {
31
35
  throw new Error("Invalid FeatureSetDefaults");
32
36
  }
33
37
  if (edition < min) {
@@ -37,7 +41,7 @@ export function createFeatureResolver(compiledFeatureSetDefaults, edition) {
37
41
  throw new Error(`Edition ${Edition[edition]} is later than the maximum supported edition ${Edition[max]}`);
38
42
  }
39
43
  let highestMatch = undefined;
40
- for (const c of compiledFeatureSetDefaults.defaults) {
44
+ for (const c of fds.defaults) {
41
45
  const e = (_a = c.edition) !== null && _a !== void 0 ? _a : 0;
42
46
  if (e > edition) {
43
47
  continue;
@@ -53,12 +57,12 @@ export function createFeatureResolver(compiledFeatureSetDefaults, edition) {
53
57
  if (highestMatch === undefined) {
54
58
  throw new Error(`No valid default found for edition ${Edition[edition]}`);
55
59
  }
56
- const defaultsBin = highestMatch.f.toBinary();
60
+ const featureSetBin = highestMatch.f.toBinary(serializationOptions);
57
61
  return (...rest) => {
58
- const f = FeatureSet.fromBinary(defaultsBin);
62
+ const f = FeatureSet.fromBinary(featureSetBin, serializationOptions);
59
63
  for (const c of rest) {
60
64
  if (c !== undefined) {
61
- f.fromBinary(c.toBinary());
65
+ f.fromBinary(c.toBinary(serializationOptions), serializationOptions);
62
66
  }
63
67
  }
64
68
  if (!validateMergedFeatures(f)) {
@@ -3,7 +3,7 @@ import type { FieldInfo } from "../field.js";
3
3
  import { ScalarType } from "../field.js";
4
4
  import type { EnumType } from "../enum.js";
5
5
  type JsonFormatWriteFieldFn = (field: FieldInfo, value: any, options: JsonWriteOptions) => JsonValue | undefined;
6
- export declare function makeJsonFormatCommon(makeWriteField: (writeEnumFn: typeof writeEnum, writeScalarFn: typeof writeScalar) => JsonFormatWriteFieldFn): JsonFormat;
7
- declare function writeEnum(type: EnumType, value: number | undefined, emitIntrinsicDefault: boolean, enumAsInteger: boolean): JsonValue | undefined;
8
- declare function writeScalar(type: ScalarType, value: any, emitIntrinsicDefault: boolean): JsonValue | undefined;
6
+ export declare function makeJsonFormatCommon(nullAsZeroValue: boolean, makeWriteField: (writeEnumFn: typeof writeEnum, writeScalarFn: typeof writeScalar) => JsonFormatWriteFieldFn): JsonFormat;
7
+ declare function writeEnum(type: EnumType, value: number | undefined, emitZeroValue: boolean, enumAsInteger: boolean): JsonValue | undefined;
8
+ declare function writeScalar(type: ScalarType, value: any, emitZeroValue: boolean): JsonValue | undefined;
9
9
  export {};
@@ -36,7 +36,7 @@ function makeReadOptions(options) {
36
36
  function makeWriteOptions(options) {
37
37
  return options ? Object.assign(Object.assign({}, jsonWriteDefaults), options) : jsonWriteDefaults;
38
38
  }
39
- export function makeJsonFormatCommon(makeWriteField) {
39
+ export function makeJsonFormatCommon(nullAsZeroValue, makeWriteField) {
40
40
  const writeField = makeWriteField(writeEnum, writeScalar);
41
41
  return {
42
42
  makeReadOptions,
@@ -62,7 +62,7 @@ export function makeJsonFormatCommon(makeWriteField) {
62
62
  }
63
63
  oneofSeen.set(field.oneof, jsonKey);
64
64
  }
65
- readField(message, jsonValue, field, options, type);
65
+ readField(message, jsonValue, field, options, type, nullAsZeroValue);
66
66
  }
67
67
  else {
68
68
  let found = false;
@@ -73,7 +73,7 @@ export function makeJsonFormatCommon(makeWriteField) {
73
73
  if (ext && ext.extendee.typeName == type.typeName) {
74
74
  found = true;
75
75
  const [container, get] = createExtensionContainer(ext);
76
- readField(container, jsonValue, ext.field, options, ext);
76
+ readField(container, jsonValue, ext.field, options, ext, true);
77
77
  // We pass on the options as BinaryReadOptions/BinaryWriteOptions,
78
78
  // so that users can bring their own binary reader and writer factories
79
79
  // if necessary.
@@ -139,7 +139,7 @@ export function makeJsonFormatCommon(makeWriteField) {
139
139
  }
140
140
  return json;
141
141
  },
142
- readScalar,
142
+ readScalar: (type, json, longType) => readScalar(type, json, longType, nullAsZeroValue), // eslint-disable-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
143
143
  writeScalar,
144
144
  debug: debugJsonValue,
145
145
  };
@@ -159,8 +159,11 @@ function debugJsonValue(json) {
159
159
  }
160
160
  // Read a JSON value for a field.
161
161
  // The "type" argument is only used to provide context in errors.
162
+ // The argument "nullAsZeroValue" applies to singular fields (not repeated, map,
163
+ // or oneof). If the argument is `true`, JSON null is parsed as the fields
164
+ // zero-value. If the argument is false, JSON null sets the field to `undefined`.
162
165
  function readField(target, // eslint-disable-line @typescript-eslint/no-explicit-any -- `any` is the best choice for dynamic access
163
- jsonValue, field, options, type) {
166
+ jsonValue, field, options, type, nullAsZeroValue) {
164
167
  let localName = field.localName;
165
168
  if (field.oneof) {
166
169
  if (jsonValue === null && field.kind == "scalar") {
@@ -189,13 +192,13 @@ jsonValue, field, options, type) {
189
192
  val = field.T.fromJson(jsonItem, options);
190
193
  break;
191
194
  case "enum":
192
- val = readEnum(field.T, jsonItem, options.ignoreUnknownFields);
195
+ val = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
193
196
  if (val === undefined)
194
197
  continue;
195
198
  break;
196
199
  case "scalar":
197
200
  try {
198
- val = readScalar(field.T, jsonItem, field.L);
201
+ val = readScalar(field.T, jsonItem, field.L, true);
199
202
  }
200
203
  catch (e) {
201
204
  let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonItem)}`;
@@ -227,13 +230,13 @@ jsonValue, field, options, type) {
227
230
  val = field.V.T.fromJson(jsonMapValue, options);
228
231
  break;
229
232
  case "enum":
230
- val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields);
233
+ val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields, true);
231
234
  if (val === undefined)
232
235
  continue;
233
236
  break;
234
237
  case "scalar":
235
238
  try {
236
- val = readScalar(field.V.T, jsonMapValue, LongType.BIGINT);
239
+ val = readScalar(field.V.T, jsonMapValue, LongType.BIGINT, true);
237
240
  }
238
241
  catch (e) {
239
242
  let m = `cannot decode map value for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -251,7 +254,7 @@ jsonValue, field, options, type) {
251
254
  : jsonMapKey == "false"
252
255
  ? false
253
256
  : jsonMapKey
254
- : jsonMapKey, LongType.BIGINT).toString()] = val;
257
+ : jsonMapKey, LongType.BIGINT, true).toString()] = val;
255
258
  }
256
259
  catch (e) {
257
260
  let m = `cannot decode map key for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -284,14 +287,14 @@ jsonValue, field, options, type) {
284
287
  }
285
288
  break;
286
289
  case "enum":
287
- const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields);
290
+ const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, nullAsZeroValue);
288
291
  if (enumValue !== undefined) {
289
292
  target[localName] = enumValue;
290
293
  }
291
294
  break;
292
295
  case "scalar":
293
296
  try {
294
- target[localName] = readScalar(field.T, jsonValue, field.L);
297
+ target[localName] = readScalar(field.T, jsonValue, field.L, nullAsZeroValue);
295
298
  }
296
299
  catch (e) {
297
300
  let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
@@ -306,7 +309,10 @@ jsonValue, field, options, type) {
306
309
  }
307
310
  // May throw an error. If the error message is non-blank, it should be shown.
308
311
  // It is up to the caller to provide context.
309
- function readScalar(type, json, longType) {
312
+ // If the argument "nullAsZeroValue" is `true`, this function returns the zero-value
313
+ // for JSON null. If the argument is `false`, this function returns `undefined`
314
+ // for JSON null.
315
+ function readScalar(type, json, longType, nullAsZeroValue) {
310
316
  // every valid case in the switch below returns, and every fall
311
317
  // through is regarded as a failure.
312
318
  switch (type) {
@@ -315,7 +321,7 @@ function readScalar(type, json, longType) {
315
321
  case ScalarType.DOUBLE:
316
322
  case ScalarType.FLOAT:
317
323
  if (json === null)
318
- return 0.0; // TODO field presence: this should reset the field with explicit presence
324
+ return nullAsZeroValue ? 0.0 : undefined;
319
325
  if (json === "NaN")
320
326
  return Number.NaN;
321
327
  if (json === "Infinity")
@@ -352,7 +358,7 @@ function readScalar(type, json, longType) {
352
358
  case ScalarType.SINT32:
353
359
  case ScalarType.UINT32:
354
360
  if (json === null)
355
- return 0; // TODO field presence: this should reset the field with explicit presence
361
+ return nullAsZeroValue ? 0 : undefined;
356
362
  let int32;
357
363
  if (typeof json == "number")
358
364
  int32 = json;
@@ -372,7 +378,7 @@ function readScalar(type, json, longType) {
372
378
  case ScalarType.SFIXED64:
373
379
  case ScalarType.SINT64:
374
380
  if (json === null)
375
- return protoInt64.zero; // TODO field presence: this should reset the field with explicit presence
381
+ return nullAsZeroValue ? protoInt64.zero : undefined;
376
382
  if (typeof json != "number" && typeof json != "string")
377
383
  break;
378
384
  const long = protoInt64.parse(json);
@@ -381,7 +387,7 @@ function readScalar(type, json, longType) {
381
387
  case ScalarType.FIXED64:
382
388
  case ScalarType.UINT64:
383
389
  if (json === null)
384
- return protoInt64.zero; // TODO field presence: this should reset the field with explicit presence
390
+ return nullAsZeroValue ? protoInt64.zero : undefined;
385
391
  if (typeof json != "number" && typeof json != "string")
386
392
  break;
387
393
  const uLong = protoInt64.uParse(json);
@@ -390,14 +396,14 @@ function readScalar(type, json, longType) {
390
396
  // bool:
391
397
  case ScalarType.BOOL:
392
398
  if (json === null)
393
- return false; // TODO field presence: this should reset the field with explicit presence
399
+ return nullAsZeroValue ? false : undefined;
394
400
  if (typeof json !== "boolean")
395
401
  break;
396
402
  return json;
397
403
  // string:
398
404
  case ScalarType.STRING:
399
405
  if (json === null)
400
- return ""; // TODO field presence: this should reset the field with explicit presence
406
+ return nullAsZeroValue ? "" : undefined;
401
407
  if (typeof json !== "string") {
402
408
  break;
403
409
  }
@@ -413,19 +419,24 @@ function readScalar(type, json, longType) {
413
419
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
414
420
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
415
421
  case ScalarType.BYTES:
416
- if (json === null || json === "")
417
- return new Uint8Array(0); // TODO field presence: this should reset the field with explicit presence
422
+ if (json === null)
423
+ return nullAsZeroValue ? new Uint8Array(0) : undefined;
424
+ if (json === "")
425
+ return new Uint8Array(0);
418
426
  if (typeof json !== "string")
419
427
  break;
420
428
  return protoBase64.dec(json);
421
429
  }
422
430
  throw new Error();
423
431
  }
424
- function readEnum(type, json, ignoreUnknownFields) {
432
+ // If the argument "nullAsZeroValue" is `true`, this function returns the first
433
+ // enum value for JSON null. If the argument is `false`, this function returns
434
+ // `undefined`.
435
+ function readEnum(type, json, ignoreUnknownFields, nullAsZeroValue) {
425
436
  if (json === null) {
426
- // proto3 requires 0 to be default value for all enums
427
- // TODO field presence: this should reset the field with explicit presence
428
- return 0;
437
+ // proto3 requires 0 to be default value for all enums (which is required to be the first value).
438
+ // proto2 uses the first value (at least with protoc-gen-go).
439
+ return nullAsZeroValue ? type.values[0].no : undefined;
429
440
  }
430
441
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
431
442
  switch (typeof json) {
@@ -444,12 +455,12 @@ function readEnum(type, json, ignoreUnknownFields) {
444
455
  }
445
456
  throw new Error(`cannot decode enum ${type.typeName} from JSON: ${debugJsonValue(json)}`);
446
457
  }
447
- function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
458
+ function writeEnum(type, value, emitZeroValue, enumAsInteger) {
448
459
  var _a;
449
460
  if (value === undefined) {
450
461
  return value;
451
462
  }
452
- if (value === 0 && !emitIntrinsicDefault) {
463
+ if (!emitZeroValue && type.values[0].no === value) {
453
464
  // proto3 requires 0 to be default value for all enums
454
465
  return undefined;
455
466
  }
@@ -462,7 +473,7 @@ function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
462
473
  const val = type.findNumber(value);
463
474
  return (_a = val === null || val === void 0 ? void 0 : val.name) !== null && _a !== void 0 ? _a : value; // if we don't know the enum value, just return the number
464
475
  }
465
- function writeScalar(type, value, emitIntrinsicDefault) {
476
+ function writeScalar(type, value, emitZeroValue) {
466
477
  if (value === undefined) {
467
478
  return undefined;
468
479
  }
@@ -474,7 +485,7 @@ function writeScalar(type, value, emitIntrinsicDefault) {
474
485
  case ScalarType.FIXED32:
475
486
  case ScalarType.UINT32:
476
487
  assert(typeof value == "number");
477
- return value != 0 || emitIntrinsicDefault ? value : undefined;
488
+ return value != 0 || emitZeroValue ? value : undefined;
478
489
  // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".
479
490
  // Either numbers or strings are accepted. Exponent notation is also accepted.
480
491
  case ScalarType.FLOAT:
@@ -487,15 +498,15 @@ function writeScalar(type, value, emitIntrinsicDefault) {
487
498
  return "Infinity";
488
499
  if (value === Number.NEGATIVE_INFINITY)
489
500
  return "-Infinity";
490
- return value !== 0 || emitIntrinsicDefault ? value : undefined;
501
+ return value !== 0 || emitZeroValue ? value : undefined;
491
502
  // string:
492
503
  case ScalarType.STRING:
493
504
  assert(typeof value == "string");
494
- return value.length > 0 || emitIntrinsicDefault ? value : undefined;
505
+ return value.length > 0 || emitZeroValue ? value : undefined;
495
506
  // bool:
496
507
  case ScalarType.BOOL:
497
508
  assert(typeof value == "boolean");
498
- return value || emitIntrinsicDefault ? value : undefined;
509
+ return value || emitZeroValue ? value : undefined;
499
510
  // JSON value will be a decimal string. Either numbers or strings are accepted.
500
511
  case ScalarType.UINT64:
501
512
  case ScalarType.FIXED64:
@@ -508,14 +519,12 @@ function writeScalar(type, value, emitIntrinsicDefault) {
508
519
  // We use implicit conversion with `value != 0` to catch both 0n and "0"
509
520
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
510
521
  // @ts-ignore
511
- return emitIntrinsicDefault || value != 0
512
- ? value.toString(10)
513
- : undefined;
522
+ return emitZeroValue || value != 0 ? value.toString(10) : undefined;
514
523
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
515
524
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
516
525
  case ScalarType.BYTES:
517
526
  assert(value instanceof Uint8Array);
518
- return emitIntrinsicDefault || value.byteLength > 0
527
+ return emitZeroValue || value.byteLength > 0
519
528
  ? protoBase64.enc(value)
520
529
  : undefined;
521
530
  }
@@ -16,8 +16,8 @@ import { assert } from "./assert.js";
16
16
  import { makeJsonFormatCommon } from "./json-format-common.js";
17
17
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
18
18
  export function makeJsonFormatProto2() {
19
- // TODO field presence: merge this function with proto2
20
- return makeJsonFormatCommon((writeEnum, writeScalar) => {
19
+ // TODO field presence: merge this function with proto3. the reading side already switches behavior on the nullAsZeroValue argument.
20
+ return makeJsonFormatCommon(false, (writeEnum, writeScalar) => {
21
21
  return function writeField(field, value, options) {
22
22
  if (field.kind == "map") {
23
23
  const jsonObj = {};
@@ -16,8 +16,8 @@ import { assert } from "./assert.js";
16
16
  import { makeJsonFormatCommon } from "./json-format-common.js";
17
17
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
18
18
  export function makeJsonFormatProto3() {
19
- // TODO field presence: merge this function with proto2
20
- return makeJsonFormatCommon((writeEnum, writeScalar) => {
19
+ // TODO field presence: merge this function with proto2. the reading side already switches behavior on the nullAsZeroValue argument.
20
+ return makeJsonFormatCommon(true, (writeEnum, writeScalar) => {
21
21
  return function writeField(field, value, options) {
22
22
  if (field.kind == "map") {
23
23
  const jsonObj = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bufbuild/protobuf",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "license": "(Apache-2.0 AND BSD-3-Clause)",
5
5
  "description": "A complete implementation of Protocol Buffers in TypeScript, suitable for web browsers and Node.js.",
6
6
  "repository": {