protobufjs 8.0.1-experimental → 8.0.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/protobuf.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /*!
2
- * protobuf.js v8.0.1-experimental (c) 2016, daniel wirtz
3
- * compiled mon, 24 mar 2025 19:22:52 utc
2
+ * protobuf.js v8.0.1 (c) 2016, daniel wirtz
3
+ * compiled sat, 04 apr 2026 19:23:15 utc
4
4
  * licensed under the bsd-3-clause license
5
5
  * see: https://github.com/dcodeio/protobuf.js for details
6
6
  */
@@ -2108,12 +2108,6 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
2108
2108
  */
2109
2109
  this._valuesFeatures = {};
2110
2110
 
2111
- /**
2112
- * Unresolved values features, if any
2113
- * @type {Object<string, Object<string, *>>|undefined}
2114
- */
2115
- this._valuesProtoFeatures = {};
2116
-
2117
2111
  /**
2118
2112
  * Reserved ranges, if any.
2119
2113
  * @type {Array.<number[]|string>}
@@ -2131,21 +2125,20 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
2131
2125
  }
2132
2126
 
2133
2127
  /**
2134
- * Resolves value features
2135
- * @returns {Enum} `this`
2128
+ * @override
2136
2129
  */
2137
- Enum.prototype.resolve = function resolve() {
2138
- ReflectionObject.prototype.resolve.call(this);
2130
+ Enum.prototype._resolveFeatures = function _resolveFeatures(edition) {
2131
+ edition = this._edition || edition;
2132
+ ReflectionObject.prototype._resolveFeatures.call(this, edition);
2139
2133
 
2140
- for (var key of Object.keys(this._valuesProtoFeatures)) {
2134
+ Object.keys(this.values).forEach(key => {
2141
2135
  var parentFeaturesCopy = Object.assign({}, this._features);
2142
- this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this._valuesProtoFeatures[key] || {});
2143
- }
2136
+ this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this.valuesOptions && this.valuesOptions[key] && this.valuesOptions[key].features);
2137
+ });
2144
2138
 
2145
2139
  return this;
2146
2140
  };
2147
2141
 
2148
-
2149
2142
  /**
2150
2143
  * Enum descriptor.
2151
2144
  * @interface IEnum
@@ -2163,6 +2156,9 @@ Enum.prototype.resolve = function resolve() {
2163
2156
  Enum.fromJSON = function fromJSON(name, json) {
2164
2157
  var enm = new Enum(name, json.values, json.options, json.comment, json.comments);
2165
2158
  enm.reserved = json.reserved;
2159
+ if (json.edition)
2160
+ enm._edition = json.edition;
2161
+ enm._defaultEdition = "proto3"; // For backwards-compatibility.
2166
2162
  return enm;
2167
2163
  };
2168
2164
 
@@ -2174,6 +2170,7 @@ Enum.fromJSON = function fromJSON(name, json) {
2174
2170
  Enum.prototype.toJSON = function toJSON(toJSONOptions) {
2175
2171
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
2176
2172
  return util.toObject([
2173
+ "edition" , this._editionToJSON(),
2177
2174
  "options" , this.options,
2178
2175
  "valuesOptions" , this.valuesOptions,
2179
2176
  "values" , this.values,
@@ -2222,21 +2219,6 @@ Enum.prototype.add = function add(name, id, comment, options) {
2222
2219
  if (this.valuesOptions === undefined)
2223
2220
  this.valuesOptions = {};
2224
2221
  this.valuesOptions[name] = options || null;
2225
-
2226
- for (var key of Object.keys(this.valuesOptions)) {
2227
- var features = Array.isArray(this.valuesOptions[key]) ? this.valuesOptions[key].find(x => {return Object.prototype.hasOwnProperty.call(x, "features");}) : this.valuesOptions[key] === "features";
2228
- if (features) {
2229
- this._valuesProtoFeatures[key] = features.features;
2230
- } else {
2231
- this._valuesProtoFeatures[key] = {};
2232
- }
2233
- }
2234
- }
2235
-
2236
- for (var enumValue of Object.keys(this.values)) {
2237
- if (!this._valuesProtoFeatures[enumValue]) {
2238
- this._valuesProtoFeatures[enumValue] = {};
2239
- }
2240
2222
  }
2241
2223
 
2242
2224
  this.comments[name] = comment || null;
@@ -2324,7 +2306,11 @@ var ruleRe = /^required|optional|repeated$/;
2324
2306
  * @throws {TypeError} If arguments are invalid
2325
2307
  */
2326
2308
  Field.fromJSON = function fromJSON(name, json) {
2327
- return new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);
2309
+ var field = new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);
2310
+ if (json.edition)
2311
+ field._edition = json.edition;
2312
+ field._defaultEdition = "proto3"; // For backwards-compatibility.
2313
+ return field;
2328
2314
  };
2329
2315
 
2330
2316
  /**
@@ -2565,6 +2551,7 @@ Field.prototype.setOption = function setOption(name, value, ifNotSet) {
2565
2551
  Field.prototype.toJSON = function toJSON(toJSONOptions) {
2566
2552
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
2567
2553
  return util.toObject([
2554
+ "edition" , this._editionToJSON(),
2568
2555
  "rule" , this.rule !== "optional" && this.rule || undefined,
2569
2556
  "type" , this.type,
2570
2557
  "id" , this.id,
@@ -2649,14 +2636,23 @@ Field.prototype.resolve = function resolve() {
2649
2636
  * @returns {object} The feature values to override
2650
2637
  */
2651
2638
  Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(edition) {
2652
- if (edition) return {};
2639
+ if (edition !== "proto2" && edition !== "proto3") {
2640
+ return {};
2641
+ }
2653
2642
 
2654
2643
  var features = {};
2644
+
2655
2645
  if (this.rule === "required") {
2656
2646
  features.field_presence = "LEGACY_REQUIRED";
2657
2647
  }
2658
- if (this.resolvedType instanceof Type && this.resolvedType.group) {
2659
- features.message_encoding = "DELIMITED";
2648
+ if (this.parent && types.defaults[this.type] === undefined) {
2649
+ // We can't use resolvedType because types may not have been resolved yet. However,
2650
+ // legacy groups are always in the same scope as the field so we don't have to do a
2651
+ // full scan of the tree.
2652
+ var type = this.parent.get(this.type.split(".").pop());
2653
+ if (type && type instanceof Type && type.group) {
2654
+ features.message_encoding = "DELIMITED";
2655
+ }
2660
2656
  }
2661
2657
  if (this.getOption("packed") === true) {
2662
2658
  features.repeated_field_encoding = "PACKED";
@@ -2666,6 +2662,13 @@ Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(e
2666
2662
  return features;
2667
2663
  };
2668
2664
 
2665
+ /**
2666
+ * @override
2667
+ */
2668
+ Field.prototype._resolveFeatures = function _resolveFeatures(edition) {
2669
+ return ReflectionObject.prototype._resolveFeatures.call(this, this._edition || edition);
2670
+ };
2671
+
2669
2672
  /**
2670
2673
  * Decorator function as returned by {@link Field.d} and {@link MapField.d} (TypeScript).
2671
2674
  * @typedef FieldDecorator
@@ -2821,7 +2824,7 @@ protobuf.types = require(36);
2821
2824
  protobuf.util = require(37);
2822
2825
 
2823
2826
  // Set up possibly cyclic reflection dependencies
2824
- protobuf.ReflectionObject._configure(protobuf.Root, protobuf.Namespace);
2827
+ protobuf.ReflectionObject._configure(protobuf.Root);
2825
2828
  protobuf.Namespace._configure(protobuf.Type, protobuf.Service, protobuf.Enum);
2826
2829
  protobuf.Root._configure(protobuf.Type);
2827
2830
  protobuf.Field._configure(protobuf.Type);
@@ -3022,8 +3025,12 @@ var util = require(39);
3022
3025
  function Message(properties) {
3023
3026
  // not used internally
3024
3027
  if (properties)
3025
- for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)
3026
- this[keys[i]] = properties[keys[i]];
3028
+ for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) {
3029
+ var key = keys[i];
3030
+ if (key === "__proto__")
3031
+ continue;
3032
+ this[key] = properties[key];
3033
+ }
3027
3034
  }
3028
3035
 
3029
3036
  /**
@@ -3146,6 +3153,7 @@ Message.prototype.toJSON = function toJSON() {
3146
3153
  };
3147
3154
 
3148
3155
  /*eslint-enable valid-jsdoc*/
3156
+
3149
3157
  },{"39":39}],22:[function(require,module,exports){
3150
3158
  "use strict";
3151
3159
  module.exports = Method;
@@ -3419,10 +3427,40 @@ function Namespace(name, options) {
3419
3427
  * @private
3420
3428
  */
3421
3429
  this._nestedArray = null;
3430
+
3431
+ /**
3432
+ * Cache lookup calls for any objects contains anywhere under this namespace.
3433
+ * This drastically speeds up resolve for large cross-linked protos where the same
3434
+ * types are looked up repeatedly.
3435
+ * @type {Object.<string,ReflectionObject|null>}
3436
+ * @private
3437
+ */
3438
+ this._lookupCache = {};
3439
+
3440
+ /**
3441
+ * Whether or not objects contained in this namespace need feature resolution.
3442
+ * @type {boolean}
3443
+ * @protected
3444
+ */
3445
+ this._needsRecursiveFeatureResolution = true;
3446
+
3447
+ /**
3448
+ * Whether or not objects contained in this namespace need a resolve.
3449
+ * @type {boolean}
3450
+ * @protected
3451
+ */
3452
+ this._needsRecursiveResolve = true;
3422
3453
  }
3423
3454
 
3424
3455
  function clearCache(namespace) {
3425
3456
  namespace._nestedArray = null;
3457
+ namespace._lookupCache = {};
3458
+
3459
+ // Also clear parent caches, since they include nested lookups.
3460
+ var parent = namespace;
3461
+ while(parent = parent.parent) {
3462
+ parent._lookupCache = {};
3463
+ }
3426
3464
  return namespace;
3427
3465
  }
3428
3466
 
@@ -3551,6 +3589,25 @@ Namespace.prototype.add = function add(object) {
3551
3589
  }
3552
3590
  }
3553
3591
  this.nested[object.name] = object;
3592
+
3593
+ if (!(this instanceof Type || this instanceof Service || this instanceof Enum || this instanceof Field)) {
3594
+ // This is a package or a root namespace.
3595
+ if (!object._edition) {
3596
+ // Make sure that some edition is set if it hasn't already been specified.
3597
+ object._edition = object._defaultEdition;
3598
+ }
3599
+ }
3600
+
3601
+ this._needsRecursiveFeatureResolution = true;
3602
+ this._needsRecursiveResolve = true;
3603
+
3604
+ // Also clear parent caches, since they need to recurse down.
3605
+ var parent = this;
3606
+ while(parent = parent.parent) {
3607
+ parent._needsRecursiveFeatureResolution = true;
3608
+ parent._needsRecursiveResolve = true;
3609
+ }
3610
+
3554
3611
  object.onAdd(this);
3555
3612
  return clearCache(this);
3556
3613
  };
@@ -3612,6 +3669,10 @@ Namespace.prototype.define = function define(path, json) {
3612
3669
  * @returns {Namespace} `this`
3613
3670
  */
3614
3671
  Namespace.prototype.resolveAll = function resolveAll() {
3672
+ if (!this._needsRecursiveResolve) return this;
3673
+
3674
+ this._resolveFeaturesRecursive(this._edition);
3675
+
3615
3676
  var nested = this.nestedArray, i = 0;
3616
3677
  this.resolve();
3617
3678
  while (i < nested.length)
@@ -3619,6 +3680,23 @@ Namespace.prototype.resolveAll = function resolveAll() {
3619
3680
  nested[i++].resolveAll();
3620
3681
  else
3621
3682
  nested[i++].resolve();
3683
+ this._needsRecursiveResolve = false;
3684
+ return this;
3685
+ };
3686
+
3687
+ /**
3688
+ * @override
3689
+ */
3690
+ Namespace.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
3691
+ if (!this._needsRecursiveFeatureResolution) return this;
3692
+ this._needsRecursiveFeatureResolution = false;
3693
+
3694
+ edition = this._edition || edition;
3695
+
3696
+ ReflectionObject.prototype._resolveFeaturesRecursive.call(this, edition);
3697
+ this.nestedArray.forEach(nested => {
3698
+ nested._resolveFeaturesRecursive(edition);
3699
+ });
3622
3700
  return this;
3623
3701
  };
3624
3702
 
@@ -3630,7 +3708,6 @@ Namespace.prototype.resolveAll = function resolveAll() {
3630
3708
  * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
3631
3709
  */
3632
3710
  Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {
3633
-
3634
3711
  /* istanbul ignore next */
3635
3712
  if (typeof filterTypes === "boolean") {
3636
3713
  parentAlreadyChecked = filterTypes;
@@ -3645,29 +3722,72 @@ Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChe
3645
3722
  } else if (!path.length)
3646
3723
  return this;
3647
3724
 
3725
+ var flatPath = path.join(".");
3726
+
3648
3727
  // Start at root if path is absolute
3649
3728
  if (path[0] === "")
3650
3729
  return this.root.lookup(path.slice(1), filterTypes);
3651
3730
 
3731
+ // Early bailout for objects with matching absolute paths
3732
+ var found = this.root._fullyQualifiedObjects && this.root._fullyQualifiedObjects["." + flatPath];
3733
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3734
+ return found;
3735
+ }
3736
+
3737
+ // Do a regular lookup at this namespace and below
3738
+ found = this._lookupImpl(path, flatPath);
3739
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3740
+ return found;
3741
+ }
3742
+
3743
+ if (parentAlreadyChecked)
3744
+ return null;
3745
+
3746
+ // If there hasn't been a match, walk up the tree and look more broadly
3747
+ var current = this;
3748
+ while (current.parent) {
3749
+ found = current.parent._lookupImpl(path, flatPath);
3750
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3751
+ return found;
3752
+ }
3753
+ current = current.parent;
3754
+ }
3755
+ return null;
3756
+ };
3757
+
3758
+ /**
3759
+ * Internal helper for lookup that handles searching just at this namespace and below along with caching.
3760
+ * @param {string[]} path Path to look up
3761
+ * @param {string} flatPath Flattened version of the path to use as a cache key
3762
+ * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
3763
+ * @private
3764
+ */
3765
+ Namespace.prototype._lookupImpl = function lookup(path, flatPath) {
3766
+ if(Object.prototype.hasOwnProperty.call(this._lookupCache, flatPath)) {
3767
+ return this._lookupCache[flatPath];
3768
+ }
3769
+
3652
3770
  // Test if the first part matches any nested object, and if so, traverse if path contains more
3653
3771
  var found = this.get(path[0]);
3772
+ var exact = null;
3654
3773
  if (found) {
3655
3774
  if (path.length === 1) {
3656
- if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)
3657
- return found;
3658
- } else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))
3659
- return found;
3775
+ exact = found;
3776
+ } else if (found instanceof Namespace) {
3777
+ path = path.slice(1);
3778
+ exact = found._lookupImpl(path, path.join("."));
3779
+ }
3660
3780
 
3661
3781
  // Otherwise try each nested namespace
3662
- } else
3782
+ } else {
3663
3783
  for (var i = 0; i < this.nestedArray.length; ++i)
3664
- if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i].lookup(path, filterTypes, true)))
3665
- return found;
3784
+ if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i]._lookupImpl(path, flatPath)))
3785
+ exact = found;
3786
+ }
3666
3787
 
3667
- // If there hasn't been a match, try again at the parent
3668
- if (this.parent === null || parentAlreadyChecked)
3669
- return null;
3670
- return this.parent.lookup(path, filterTypes);
3788
+ // Set this even when null, so that when we walk up the tree we can quickly bail on repeated checks back down.
3789
+ this._lookupCache[flatPath] = exact;
3790
+ return exact;
3671
3791
  };
3672
3792
 
3673
3793
  /**
@@ -3753,13 +3873,14 @@ ReflectionObject.className = "ReflectionObject";
3753
3873
  const OneOf = require(25);
3754
3874
  var util = require(37);
3755
3875
 
3756
- var Root, Namespace; // cyclic
3876
+ var Root; // cyclic
3757
3877
 
3758
3878
  /* eslint-disable no-warning-comments */
3759
3879
  // TODO: Replace with embedded proto.
3760
- var editions2023Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
3761
- var proto2Defaults = {enum_type: "CLOSED", field_presence: "EXPLICIT", json_format: "LEGACY_BEST_EFFORT", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "EXPANDED", utf8_validation: "NONE"};
3762
- var proto3Defaults = {enum_type: "OPEN", field_presence: "IMPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
3880
+ var editions2024Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY", enforce_naming_style: "STYLE2024", default_symbol_visibility: "EXPORT_TOP_LEVEL" };
3881
+ var editions2023Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY", enforce_naming_style: "STYLE_LEGACY", default_symbol_visibility: "EXPORT_ALL" };
3882
+ var proto2Defaults = {enum_type: "CLOSED", field_presence: "EXPLICIT", json_format: "LEGACY_BEST_EFFORT", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "EXPANDED", utf8_validation: "NONE", enforce_naming_style: "STYLE_LEGACY", default_symbol_visibility: "EXPORT_ALL" };
3883
+ var proto3Defaults = {enum_type: "OPEN", field_presence: "IMPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY", enforce_naming_style: "STYLE_LEGACY", default_symbol_visibility: "EXPORT_ALL" };
3763
3884
 
3764
3885
  /**
3765
3886
  * Constructs a new reflection object instance.
@@ -3795,15 +3916,34 @@ function ReflectionObject(name, options) {
3795
3916
  */
3796
3917
  this.name = name;
3797
3918
 
3919
+ /**
3920
+ * The edition specified for this object. Only relevant for top-level objects.
3921
+ * @type {string}
3922
+ * @private
3923
+ */
3924
+ this._edition = null;
3925
+
3926
+ /**
3927
+ * The default edition to use for this object if none is specified. For legacy reasons,
3928
+ * this is proto2 except in the JSON parsing case where it was proto3.
3929
+ * @type {string}
3930
+ * @private
3931
+ */
3932
+ this._defaultEdition = "proto2";
3933
+
3798
3934
  /**
3799
3935
  * Resolved Features.
3936
+ * @type {object}
3937
+ * @private
3800
3938
  */
3801
3939
  this._features = {};
3802
3940
 
3803
3941
  /**
3804
- * Unresolved Features.
3942
+ * Whether or not features have been resolved.
3943
+ * @type {boolean}
3944
+ * @private
3805
3945
  */
3806
- this._protoFeatures = null;
3946
+ this._featuresResolved = false;
3807
3947
 
3808
3948
  /**
3809
3949
  * Parent namespace.
@@ -3910,41 +4050,62 @@ ReflectionObject.prototype.onRemove = function onRemove(parent) {
3910
4050
  ReflectionObject.prototype.resolve = function resolve() {
3911
4051
  if (this.resolved)
3912
4052
  return this;
3913
- var edition = this.getOption("edition");
3914
- if ((this instanceof Namespace && edition) || (this.parent && this.parent.resolved)) {
3915
- this._resolveFeatures();
3916
- this.resolved = true;
3917
- }
4053
+ if (this.root instanceof Root)
4054
+ this.resolved = true; // only if part of a root
3918
4055
  return this;
3919
4056
  };
3920
4057
 
4058
+ /**
4059
+ * Resolves this objects editions features.
4060
+ * @param {string} edition The edition we're currently resolving for.
4061
+ * @returns {ReflectionObject} `this`
4062
+ */
4063
+ ReflectionObject.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
4064
+ return this._resolveFeatures(this._edition || edition);
4065
+ };
4066
+
3921
4067
  /**
3922
4068
  * Resolves child features from parent features
4069
+ * @param {string} edition The edition we're currently resolving for.
3923
4070
  * @returns {undefined}
3924
4071
  */
3925
- ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
4072
+ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures(edition) {
4073
+ if (this._featuresResolved) {
4074
+ return;
4075
+ }
4076
+
3926
4077
  var defaults = {};
3927
4078
 
3928
- var protoFeatures = Object.assign(Object.assign({}, this._protoFeatures), this._inferLegacyProtoFeatures(edition));
4079
+ /* istanbul ignore if */
4080
+ if (!edition) {
4081
+ throw new Error("Unknown edition for " + this.fullName);
4082
+ }
3929
4083
 
3930
- var edition = this.getOption("edition");
3931
- if (this instanceof Namespace && edition) {
4084
+ var protoFeatures = Object.assign(this.options ? Object.assign({}, this.options.features) : {},
4085
+ this._inferLegacyProtoFeatures(edition));
4086
+
4087
+ if (this._edition) {
3932
4088
  // For a namespace marked with a specific edition, reset defaults.
4089
+ /* istanbul ignore else */
3933
4090
  if (edition === "proto2") {
3934
4091
  defaults = Object.assign({}, proto2Defaults);
3935
4092
  } else if (edition === "proto3") {
3936
4093
  defaults = Object.assign({}, proto3Defaults);
3937
4094
  } else if (edition === "2023") {
3938
4095
  defaults = Object.assign({}, editions2023Defaults);
4096
+ } else if (edition === "2024") {
4097
+ defaults = Object.assign({}, editions2024Defaults);
3939
4098
  } else {
3940
4099
  throw new Error("Unknown edition: " + edition);
3941
4100
  }
3942
4101
  this._features = Object.assign(defaults, protoFeatures || {});
4102
+ this._featuresResolved = true;
3943
4103
  return;
3944
4104
  }
3945
4105
 
3946
4106
  // fields in Oneofs aren't actually children of them, so we have to
3947
4107
  // special-case it
4108
+ /* istanbul ignore else */
3948
4109
  if (this.partOf instanceof OneOf) {
3949
4110
  var lexicalParentFeaturesCopy = Object.assign({}, this.partOf._features);
3950
4111
  this._features = Object.assign(lexicalParentFeaturesCopy, protoFeatures || {});
@@ -3954,12 +4115,13 @@ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
3954
4115
  var parentFeaturesCopy = Object.assign({}, this.parent._features);
3955
4116
  this._features = Object.assign(parentFeaturesCopy, protoFeatures || {});
3956
4117
  } else {
3957
- this._features = Object.assign({}, protoFeatures);
4118
+ throw new Error("Unable to find a parent for " + this.fullName);
3958
4119
  }
3959
4120
  if (this.extensionField) {
3960
4121
  // Sister fields should have the same features as their extensions.
3961
4122
  this.extensionField._features = this._features;
3962
4123
  }
4124
+ this._featuresResolved = true;
3963
4125
  };
3964
4126
 
3965
4127
  /**
@@ -3967,7 +4129,6 @@ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
3967
4129
  * in older editions.
3968
4130
  * @param {string|undefined} edition The edition this proto is on, or undefined if pre-editions
3969
4131
  * @returns {object} The feature values to override
3970
- * @abstract
3971
4132
  */
3972
4133
  ReflectionObject.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(/*edition*/) {
3973
4134
  return {};
@@ -3988,14 +4149,19 @@ ReflectionObject.prototype.getOption = function getOption(name) {
3988
4149
  * Sets an option.
3989
4150
  * @param {string} name Option name
3990
4151
  * @param {*} value Option value
3991
- * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set
4152
+ * @param {boolean|undefined} [ifNotSet] Sets the option only if it isn't currently set
3992
4153
  * @returns {ReflectionObject} `this`
3993
4154
  */
3994
4155
  ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {
3995
- if (!ifNotSet || !this.options || this.options[name] === undefined) {
4156
+ if (!this.options)
4157
+ this.options = {};
4158
+ if (/^features\./.test(name)) {
4159
+ util.setProperty(this.options, name, value, ifNotSet);
4160
+ } else if (!ifNotSet || this.options[name] === undefined) {
3996
4161
  if (this.getOption(name) !== value) this.resolved = false;
3997
- (this.options || (this.options = {}))[name] = value;
4162
+ this.options[name] = value;
3998
4163
  }
4164
+
3999
4165
  return this;
4000
4166
  };
4001
4167
 
@@ -4010,7 +4176,6 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
4010
4176
  if (!this.parsedOptions) {
4011
4177
  this.parsedOptions = [];
4012
4178
  }
4013
- var isFeature = /^features$/.test(name);
4014
4179
  var parsedOptions = this.parsedOptions;
4015
4180
  if (propName) {
4016
4181
  // If setting a sub property of an option then try to merge it
@@ -4036,12 +4201,6 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
4036
4201
  parsedOptions.push(newOpt);
4037
4202
  }
4038
4203
 
4039
-
4040
- if (isFeature) {
4041
- var features = parsedOptions.find(x => {return Object.prototype.hasOwnProperty.call(x, "features");});
4042
- this._protoFeatures = features.features || {};
4043
- }
4044
-
4045
4204
  return this;
4046
4205
  };
4047
4206
 
@@ -4070,10 +4229,22 @@ ReflectionObject.prototype.toString = function toString() {
4070
4229
  return className;
4071
4230
  };
4072
4231
 
4232
+ /**
4233
+ * Converts the edition this object is pinned to for JSON format.
4234
+ * @returns {string|undefined} The edition string for JSON representation
4235
+ */
4236
+ ReflectionObject.prototype._editionToJSON = function _editionToJSON() {
4237
+ if (!this._edition || this._edition === "proto3") {
4238
+ // Avoid emitting proto3 since we need to default to it for backwards
4239
+ // compatibility anyway.
4240
+ return undefined;
4241
+ }
4242
+ return this._edition;
4243
+ };
4244
+
4073
4245
  // Sets up cyclic dependencies (called in index-light)
4074
- ReflectionObject._configure = function(Root_, Namespace_) {
4246
+ ReflectionObject._configure = function(Root_) {
4075
4247
  Root = Root_;
4076
- Namespace = Namespace_;
4077
4248
  };
4078
4249
 
4079
4250
  },{"25":25,"37":37}],25:[function(require,module,exports){
@@ -4383,14 +4554,25 @@ function parse(source, root, options) {
4383
4554
  pkg,
4384
4555
  imports,
4385
4556
  weakImports,
4386
- edition = "proto2",
4387
- isProto3 = false,
4388
- isProto2 = true;
4557
+ edition = "proto2";
4389
4558
 
4390
4559
  var ptr = root;
4391
4560
 
4561
+ var topLevelObjects = [];
4562
+ var topLevelOptions = {};
4563
+
4392
4564
  var applyCase = options.keepCase ? function(name) { return name; } : util.camelCase;
4393
4565
 
4566
+ function resolveFileFeatures() {
4567
+ topLevelObjects.forEach(obj => {
4568
+ obj._edition = edition;
4569
+ Object.keys(topLevelOptions).forEach(opt => {
4570
+ if (obj.getOption(opt) !== undefined) return;
4571
+ obj.setOption(opt, topLevelOptions[opt], true);
4572
+ });
4573
+ });
4574
+ }
4575
+
4394
4576
  /* istanbul ignore next */
4395
4577
  function illegal(token, name, insideTryCatch) {
4396
4578
  var filename = parse.filename;
@@ -4441,13 +4623,17 @@ function parse(source, root, options) {
4441
4623
  function readRanges(target, acceptStrings) {
4442
4624
  var token, start;
4443
4625
  do {
4444
- if (acceptStrings && ((token = peek()) === "\"" || token === "'"))
4445
- target.push(readString());
4446
- else {
4626
+ if (acceptStrings && ((token = peek()) === "\"" || token === "'")) {
4627
+ var str = readString();
4628
+ target.push(str);
4629
+ if (edition >= 2023) {
4630
+ throw illegal(str, "id");
4631
+ }
4632
+ } else {
4447
4633
  try {
4448
4634
  target.push([ start = parseId(next()), skip("to", true) ? parseId(next()) : start ]);
4449
4635
  } catch (err) {
4450
- if (typeRefRe.test(token) && (!isProto2 && !isProto3)) {
4636
+ if (acceptStrings && typeRefRe.test(token) && edition >= 2023) {
4451
4637
  target.push(token);
4452
4638
  } else {
4453
4639
  throw err;
@@ -4542,11 +4728,6 @@ function parse(source, root, options) {
4542
4728
 
4543
4729
  ptr = ptr.define(pkg);
4544
4730
 
4545
- var oldEdition = ptr.getOption("edition");
4546
- if (oldEdition && oldEdition !== edition) {
4547
- throw new Error("incompatible editions detected in package " + pkg + ": " + edition + " vs " + oldEdition);
4548
- }
4549
- ptr.setOption("edition", edition);
4550
4731
  skip(";");
4551
4732
  }
4552
4733
 
@@ -4554,6 +4735,16 @@ function parse(source, root, options) {
4554
4735
  var token = peek();
4555
4736
  var whichImports;
4556
4737
  switch (token) {
4738
+ case "option":
4739
+ if (edition < "2024") {
4740
+ throw illegal("option");
4741
+ }
4742
+ // Import options are only used for resolving options, which we don't
4743
+ // do. We can just throw them out.
4744
+ next();
4745
+ readString();
4746
+ skip(";");
4747
+ return;
4557
4748
  case "weak":
4558
4749
  whichImports = weakImports || (weakImports = []);
4559
4750
  next();
@@ -4573,33 +4764,23 @@ function parse(source, root, options) {
4573
4764
  function parseSyntax() {
4574
4765
  skip("=");
4575
4766
  edition = readString();
4576
- isProto3 = edition === "proto3";
4577
- isProto2 = edition === "proto2";
4578
4767
 
4579
4768
  /* istanbul ignore if */
4580
- if (!isProto3 && !isProto2)
4769
+ if (edition < 2023)
4581
4770
  throw illegal(edition, "syntax");
4582
4771
 
4583
- // Syntax is needed to understand the meaning of the optional field rule
4584
- // Otherwise the meaning is ambiguous between proto2 and proto3
4585
- root.setOption("edition", edition);
4586
-
4587
4772
  skip(";");
4588
4773
  }
4589
4774
 
4590
4775
  function parseEdition() {
4591
4776
  skip("=");
4592
4777
  edition = readString();
4593
- isProto3 = false;
4594
- isProto2 = false;
4595
- const supportedEditions = ["2023"];
4778
+ const supportedEditions = ["2023", "2024"];
4596
4779
 
4597
4780
  /* istanbul ignore if */
4598
4781
  if (!supportedEditions.includes(edition))
4599
4782
  throw illegal(edition, "edition");
4600
4783
 
4601
- root.setOption("edition", edition);
4602
-
4603
4784
  skip(";");
4604
4785
  }
4605
4786
 
@@ -4620,6 +4801,22 @@ function parse(source, root, options) {
4620
4801
  parseEnum(parent, token);
4621
4802
  return true;
4622
4803
 
4804
+ case "export":
4805
+ case "local":
4806
+ if (edition < "2024") {
4807
+ return false;
4808
+ }
4809
+ token = next();
4810
+ if (token === "export" || token === "local") {
4811
+ return false;
4812
+ }
4813
+ if (token !== "message" && token !== "enum") {
4814
+ return false;
4815
+ }
4816
+ /* eslint-disable no-warning-comments */
4817
+ // TODO: actually enforce visiblity modifiers like protoc does.
4818
+ return parseCommon(parent, token);
4819
+
4623
4820
  case "service":
4624
4821
  parseService(parent, token);
4625
4822
  return true;
@@ -4671,7 +4868,7 @@ function parse(source, root, options) {
4671
4868
  break;
4672
4869
 
4673
4870
  case "required":
4674
- if (!isProto2)
4871
+ if (edition !== "proto2")
4675
4872
  throw illegal(token);
4676
4873
  /* eslint-disable no-fallthrough */
4677
4874
  case "repeated":
@@ -4680,9 +4877,9 @@ function parse(source, root, options) {
4680
4877
 
4681
4878
  case "optional":
4682
4879
  /* istanbul ignore if */
4683
- if (isProto3) {
4880
+ if (edition === "proto3") {
4684
4881
  parseField(type, "proto3_optional");
4685
- } else if (!isProto2) {
4882
+ } else if (edition !== "proto2") {
4686
4883
  throw illegal(token);
4687
4884
  } else {
4688
4885
  parseField(type, "optional");
@@ -4703,7 +4900,7 @@ function parse(source, root, options) {
4703
4900
 
4704
4901
  default:
4705
4902
  /* istanbul ignore if */
4706
- if (isProto2 || !typeRefRe.test(token)) {
4903
+ if (edition === "proto2" || !typeRefRe.test(token)) {
4707
4904
  throw illegal(token);
4708
4905
  }
4709
4906
 
@@ -4713,6 +4910,9 @@ function parse(source, root, options) {
4713
4910
  }
4714
4911
  });
4715
4912
  parent.add(type);
4913
+ if (parent === ptr) {
4914
+ topLevelObjects.push(type);
4915
+ }
4716
4916
  }
4717
4917
 
4718
4918
  function parseField(parent, rule, extend) {
@@ -4770,9 +4970,15 @@ function parse(source, root, options) {
4770
4970
  } else {
4771
4971
  parent.add(field);
4772
4972
  }
4973
+ if (parent === ptr) {
4974
+ topLevelObjects.push(field);
4975
+ }
4773
4976
  }
4774
4977
 
4775
4978
  function parseGroup(parent, rule) {
4979
+ if (edition >= 2023) {
4980
+ throw illegal("group");
4981
+ }
4776
4982
  var name = next();
4777
4983
 
4778
4984
  /* istanbul ignore if */
@@ -4802,7 +5008,7 @@ function parse(source, root, options) {
4802
5008
 
4803
5009
  case "optional":
4804
5010
  /* istanbul ignore if */
4805
- if (isProto3) {
5011
+ if (edition === "proto3") {
4806
5012
  parseField(type, "proto3_optional");
4807
5013
  } else {
4808
5014
  parseField(type, "optional");
@@ -4817,6 +5023,28 @@ function parse(source, root, options) {
4817
5023
  parseEnum(type, token);
4818
5024
  break;
4819
5025
 
5026
+ case "reserved":
5027
+ readRanges(type.reserved || (type.reserved = []), true);
5028
+ break;
5029
+
5030
+ case "export":
5031
+ case "local":
5032
+ if (edition < "2024") {
5033
+ throw illegal(token);
5034
+ }
5035
+ token = next();
5036
+ switch (token) {
5037
+ case "message":
5038
+ parseType(type, token);
5039
+ break;
5040
+ case "enum":
5041
+ parseType(type, token);
5042
+ break;
5043
+ default:
5044
+ throw illegal(token);
5045
+ }
5046
+ break;
5047
+
4820
5048
  /* istanbul ignore next */
4821
5049
  default:
4822
5050
  throw illegal(token); // there are no groups with proto3 semantics
@@ -4900,6 +5128,7 @@ function parse(source, root, options) {
4900
5128
 
4901
5129
  case "reserved":
4902
5130
  readRanges(enm.reserved || (enm.reserved = []), true);
5131
+ if(enm.reserved === undefined) enm.reserved = [];
4903
5132
  break;
4904
5133
 
4905
5134
  default:
@@ -4907,6 +5136,9 @@ function parse(source, root, options) {
4907
5136
  }
4908
5137
  });
4909
5138
  parent.add(enm);
5139
+ if (parent === ptr) {
5140
+ topLevelObjects.push(enm);
5141
+ }
4910
5142
  }
4911
5143
 
4912
5144
  function parseEnumValue(parent, token) {
@@ -4920,18 +5152,13 @@ function parse(source, root, options) {
4920
5152
  dummy = {
4921
5153
  options: undefined
4922
5154
  };
5155
+ dummy.getOption = function(name) {
5156
+ return this.options[name];
5157
+ };
4923
5158
  dummy.setOption = function(name, value) {
4924
- if (this.options === undefined)
4925
- this.options = {};
4926
-
4927
- this.options[name] = value;
5159
+ ReflectionObject.prototype.setOption.call(dummy, name, value);
4928
5160
  };
4929
- dummy.setParsedOption = function(name, value, propName) {
4930
- // In order to not change existing behavior, only calling
4931
- // this for features
4932
- if (/^features$/.test(name)) {
4933
- return ReflectionObject.prototype.setParsedOption.call(dummy, name, value, propName);
4934
- }
5161
+ dummy.setParsedOption = function() {
4935
5162
  return undefined;
4936
5163
  };
4937
5164
  ifBlock(dummy, function parseEnumValue_block(token) {
@@ -5048,6 +5275,10 @@ function parse(source, root, options) {
5048
5275
  }
5049
5276
 
5050
5277
  function setOption(parent, name, value) {
5278
+ if (ptr === parent && /^features\./.test(name)) {
5279
+ topLevelOptions[name] = value;
5280
+ return;
5281
+ }
5051
5282
  if (parent.setOption)
5052
5283
  parent.setOption(name, value);
5053
5284
  }
@@ -5086,6 +5317,9 @@ function parse(source, root, options) {
5086
5317
  throw illegal(token);
5087
5318
  });
5088
5319
  parent.add(service);
5320
+ if (parent === ptr) {
5321
+ topLevelObjects.push(service);
5322
+ }
5089
5323
  }
5090
5324
 
5091
5325
  function parseMethod(parent, token) {
@@ -5155,7 +5389,7 @@ function parse(source, root, options) {
5155
5389
 
5156
5390
  case "optional":
5157
5391
  /* istanbul ignore if */
5158
- if (isProto3) {
5392
+ if (edition === "proto3") {
5159
5393
  parseField(parent, "proto3_optional", reference);
5160
5394
  } else {
5161
5395
  parseField(parent, "optional", reference);
@@ -5164,7 +5398,7 @@ function parse(source, root, options) {
5164
5398
 
5165
5399
  default:
5166
5400
  /* istanbul ignore if */
5167
- if (isProto2 || !typeRefRe.test(token))
5401
+ if (edition === "proto2" || !typeRefRe.test(token))
5168
5402
  throw illegal(token);
5169
5403
  push(token);
5170
5404
  parseField(parent, "optional", reference);
@@ -5229,6 +5463,8 @@ function parse(source, root, options) {
5229
5463
  }
5230
5464
  }
5231
5465
 
5466
+ resolveFileFeatures();
5467
+
5232
5468
  parse.filename = null;
5233
5469
  return {
5234
5470
  "package" : pkg,
@@ -5760,8 +5996,19 @@ function Root(options) {
5760
5996
  */
5761
5997
  this.files = [];
5762
5998
 
5763
- // Default to proto2 if not specified.
5764
- this.setOption("edition", "proto2", true);
5999
+ /**
6000
+ * Edition, defaults to proto2 if unspecified.
6001
+ * @type {string}
6002
+ * @private
6003
+ */
6004
+ this._edition = "proto2";
6005
+
6006
+ /**
6007
+ * Global lookup cache of fully qualified names.
6008
+ * @type {Object.<string,ReflectionObject>}
6009
+ * @private
6010
+ */
6011
+ this._fullyQualifiedObjects = {};
5765
6012
  }
5766
6013
 
5767
6014
  /**
@@ -5775,7 +6022,7 @@ Root.fromJSON = function fromJSON(json, root) {
5775
6022
  root = new Root();
5776
6023
  if (json.options)
5777
6024
  root.setOptions(json.options);
5778
- return root.addJSON(json.nested);
6025
+ return root.addJSON(json.nested).resolveAll();
5779
6026
  };
5780
6027
 
5781
6028
  /**
@@ -5830,11 +6077,11 @@ Root.prototype.load = function load(filename, options, callback) {
5830
6077
  if (sync) {
5831
6078
  throw err;
5832
6079
  }
5833
- var cb = callback;
5834
- callback = null;
5835
6080
  if (root) {
5836
6081
  root.resolveAll();
5837
6082
  }
6083
+ var cb = callback;
6084
+ callback = null;
5838
6085
  cb(err, root);
5839
6086
  }
5840
6087
 
@@ -5942,8 +6189,8 @@ Root.prototype.load = function load(filename, options, callback) {
5942
6189
  for (var i = 0, resolved; i < filename.length; ++i)
5943
6190
  if (resolved = self.resolvePath("", filename[i]))
5944
6191
  fetch(resolved);
5945
- self.resolveAll();
5946
6192
  if (sync) {
6193
+ self.resolveAll();
5947
6194
  return self;
5948
6195
  }
5949
6196
  if (!queued) {
@@ -5992,6 +6239,8 @@ Root.prototype.loadSync = function loadSync(filename, options) {
5992
6239
  * @override
5993
6240
  */
5994
6241
  Root.prototype.resolveAll = function resolveAll() {
6242
+ if (!this._needsRecursiveResolve) return this;
6243
+
5995
6244
  if (this.deferred.length)
5996
6245
  throw Error("unresolvable extensions: " + this.deferred.map(function(field) {
5997
6246
  return "'extend " + field.extend + "' in " + field.parent.fullName;
@@ -6058,6 +6307,11 @@ Root.prototype._handleAdd = function _handleAdd(object) {
6058
6307
  object.parent[object.name] = object; // expose namespace as property of its parent
6059
6308
  }
6060
6309
 
6310
+ if (object instanceof Type || object instanceof Enum || object instanceof Field) {
6311
+ // Only store types and enums for quick lookup during resolve.
6312
+ this._fullyQualifiedObjects[object.fullName] = object;
6313
+ }
6314
+
6061
6315
  // The above also adds uppercased (and thus conflict-free) nested types, services and enums as
6062
6316
  // properties of namespaces just like static code does. This allows using a .d.ts generated for
6063
6317
  // a static module with reflection-based solutions where the condition is met.
@@ -6098,6 +6352,8 @@ Root.prototype._handleRemove = function _handleRemove(object) {
6098
6352
  delete object.parent[object.name]; // unexpose namespaces
6099
6353
 
6100
6354
  }
6355
+
6356
+ delete this._fullyQualifiedObjects[object.fullName];
6101
6357
  };
6102
6358
 
6103
6359
  // Sets up cyclic dependencies (called in index-light)
@@ -6369,7 +6625,10 @@ Service.fromJSON = function fromJSON(name, json) {
6369
6625
  service.add(Method.fromJSON(names[i], json.methods[names[i]]));
6370
6626
  if (json.nested)
6371
6627
  service.addJSON(json.nested);
6628
+ if (json.edition)
6629
+ service._edition = json.edition;
6372
6630
  service.comment = json.comment;
6631
+ service._defaultEdition = "proto3"; // For backwards-compatibility.
6373
6632
  return service;
6374
6633
  };
6375
6634
 
@@ -6382,6 +6641,7 @@ Service.prototype.toJSON = function toJSON(toJSONOptions) {
6382
6641
  var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
6383
6642
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
6384
6643
  return util.toObject([
6644
+ "edition" , this._editionToJSON(),
6385
6645
  "options" , inherited && inherited.options || undefined,
6386
6646
  "methods" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},
6387
6647
  "nested" , inherited && inherited.nested || undefined,
@@ -6418,6 +6678,8 @@ Service.prototype.get = function get(name) {
6418
6678
  * @override
6419
6679
  */
6420
6680
  Service.prototype.resolveAll = function resolveAll() {
6681
+ if (!this._needsRecursiveResolve) return this;
6682
+
6421
6683
  Namespace.prototype.resolve.call(this);
6422
6684
  var methods = this.methodsArray;
6423
6685
  for (var i = 0; i < methods.length; ++i)
@@ -6425,6 +6687,21 @@ Service.prototype.resolveAll = function resolveAll() {
6425
6687
  return this;
6426
6688
  };
6427
6689
 
6690
+ /**
6691
+ * @override
6692
+ */
6693
+ Service.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
6694
+ if (!this._needsRecursiveFeatureResolution) return this;
6695
+
6696
+ edition = this._edition || edition;
6697
+
6698
+ Namespace.prototype._resolveFeaturesRecursive.call(this, edition);
6699
+ this.methodsArray.forEach(method => {
6700
+ method._resolveFeaturesRecursive(edition);
6701
+ });
6702
+ return this;
6703
+ };
6704
+
6428
6705
  /**
6429
6706
  * @override
6430
6707
  */
@@ -6929,6 +7206,7 @@ var Enum = require(15),
6929
7206
  * @param {Object.<string,*>} [options] Declared options
6930
7207
  */
6931
7208
  function Type(name, options) {
7209
+ name = name.replace(/\W/g, "");
6932
7210
  Namespace.call(this, name, options);
6933
7211
 
6934
7212
  /**
@@ -7172,6 +7450,9 @@ Type.fromJSON = function fromJSON(name, json) {
7172
7450
  type.group = true;
7173
7451
  if (json.comment)
7174
7452
  type.comment = json.comment;
7453
+ if (json.edition)
7454
+ type._edition = json.edition;
7455
+ type._defaultEdition = "proto3"; // For backwards-compatibility.
7175
7456
  return type;
7176
7457
  };
7177
7458
 
@@ -7184,6 +7465,7 @@ Type.prototype.toJSON = function toJSON(toJSONOptions) {
7184
7465
  var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
7185
7466
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
7186
7467
  return util.toObject([
7468
+ "edition" , this._editionToJSON(),
7187
7469
  "options" , inherited && inherited.options || undefined,
7188
7470
  "oneofs" , Namespace.arrayToJSON(this.oneofsArray, toJSONOptions),
7189
7471
  "fields" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; }), toJSONOptions) || {},
@@ -7199,6 +7481,8 @@ Type.prototype.toJSON = function toJSON(toJSONOptions) {
7199
7481
  * @override
7200
7482
  */
7201
7483
  Type.prototype.resolveAll = function resolveAll() {
7484
+ if (!this._needsRecursiveResolve) return this;
7485
+
7202
7486
  Namespace.prototype.resolveAll.call(this);
7203
7487
  var oneofs = this.oneofsArray; i = 0;
7204
7488
  while (i < oneofs.length)
@@ -7209,6 +7493,24 @@ Type.prototype.resolveAll = function resolveAll() {
7209
7493
  return this;
7210
7494
  };
7211
7495
 
7496
+ /**
7497
+ * @override
7498
+ */
7499
+ Type.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
7500
+ if (!this._needsRecursiveFeatureResolution) return this;
7501
+
7502
+ edition = this._edition || edition;
7503
+
7504
+ Namespace.prototype._resolveFeaturesRecursive.call(this, edition);
7505
+ this.oneofsArray.forEach(oneof => {
7506
+ oneof._resolveFeatures(edition);
7507
+ });
7508
+ this.fieldsArray.forEach(field => {
7509
+ field._resolveFeatures(edition);
7510
+ });
7511
+ return this;
7512
+ };
7513
+
7212
7514
  /**
7213
7515
  * @override
7214
7516
  */
@@ -7861,10 +8163,10 @@ util.decorateEnum = function decorateEnum(object) {
7861
8163
  * @param {Object.<string,*>} dst Destination object
7862
8164
  * @param {string} path dot '.' delimited path of the property to set
7863
8165
  * @param {Object} value the value to set
7864
- * @param {boolean} overWrite whether or not to concatenate the values into an array or overwrite; defaults to false.
8166
+ * @param {boolean|undefined} [ifNotSet] Sets the option only if it isn't currently set
7865
8167
  * @returns {Object.<string,*>} Destination object
7866
8168
  */
7867
- util.setProperty = function setProperty(dst, path, value) {
8169
+ util.setProperty = function setProperty(dst, path, value, ifNotSet) {
7868
8170
  function setProp(dst, path, value) {
7869
8171
  var part = path.shift();
7870
8172
  if (part === "__proto__" || part === "prototype") {
@@ -7874,6 +8176,8 @@ util.setProperty = function setProperty(dst, path, value) {
7874
8176
  dst[part] = setProp(dst[part] || {}, path, value);
7875
8177
  } else {
7876
8178
  var prevValue = dst[part];
8179
+ if (prevValue && ifNotSet)
8180
+ return dst;
7877
8181
  if (prevValue)
7878
8182
  value = [].concat(prevValue).concat(value);
7879
8183
  dst[part] = value;