protobufjs 8.0.0-experimental → 8.0.0

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,6 +1,6 @@
1
1
  /*!
2
- * protobuf.js v8.0.0-experimental (c) 2016, daniel wirtz
3
- * compiled fri, 21 mar 2025 17:14:14 utc
2
+ * protobuf.js v8.0.0 (c) 2016, daniel wirtz
3
+ * compiled tue, 16 dec 2025 22:00:06 utc
4
4
  * licensed under the bsd-3-clause license
5
5
  * see: https://github.com/dcodeio/protobuf.js for details
6
6
  */
@@ -1707,12 +1707,6 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
1707
1707
  */
1708
1708
  this._valuesFeatures = {};
1709
1709
 
1710
- /**
1711
- * Unresolved values features, if any
1712
- * @type {Object<string, Object<string, *>>|undefined}
1713
- */
1714
- this._valuesProtoFeatures = {};
1715
-
1716
1710
  /**
1717
1711
  * Reserved ranges, if any.
1718
1712
  * @type {Array.<number[]|string>}
@@ -1730,21 +1724,20 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
1730
1724
  }
1731
1725
 
1732
1726
  /**
1733
- * Resolves value features
1734
- * @returns {Enum} `this`
1727
+ * @override
1735
1728
  */
1736
- Enum.prototype.resolve = function resolve() {
1737
- ReflectionObject.prototype.resolve.call(this);
1729
+ Enum.prototype._resolveFeatures = function _resolveFeatures(edition) {
1730
+ edition = this._edition || edition;
1731
+ ReflectionObject.prototype._resolveFeatures.call(this, edition);
1738
1732
 
1739
- for (var key of Object.keys(this._valuesProtoFeatures)) {
1733
+ Object.keys(this.values).forEach(key => {
1740
1734
  var parentFeaturesCopy = Object.assign({}, this._features);
1741
- this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this._valuesProtoFeatures[key] || {});
1742
- }
1735
+ this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this.valuesOptions && this.valuesOptions[key] && this.valuesOptions[key].features);
1736
+ });
1743
1737
 
1744
1738
  return this;
1745
1739
  };
1746
1740
 
1747
-
1748
1741
  /**
1749
1742
  * Enum descriptor.
1750
1743
  * @interface IEnum
@@ -1762,6 +1755,9 @@ Enum.prototype.resolve = function resolve() {
1762
1755
  Enum.fromJSON = function fromJSON(name, json) {
1763
1756
  var enm = new Enum(name, json.values, json.options, json.comment, json.comments);
1764
1757
  enm.reserved = json.reserved;
1758
+ if (json.edition)
1759
+ enm._edition = json.edition;
1760
+ enm._defaultEdition = "proto3"; // For backwards-compatibility.
1765
1761
  return enm;
1766
1762
  };
1767
1763
 
@@ -1773,6 +1769,7 @@ Enum.fromJSON = function fromJSON(name, json) {
1773
1769
  Enum.prototype.toJSON = function toJSON(toJSONOptions) {
1774
1770
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
1775
1771
  return util.toObject([
1772
+ "edition" , this._editionToJSON(),
1776
1773
  "options" , this.options,
1777
1774
  "valuesOptions" , this.valuesOptions,
1778
1775
  "values" , this.values,
@@ -1821,21 +1818,6 @@ Enum.prototype.add = function add(name, id, comment, options) {
1821
1818
  if (this.valuesOptions === undefined)
1822
1819
  this.valuesOptions = {};
1823
1820
  this.valuesOptions[name] = options || null;
1824
-
1825
- for (var key of Object.keys(this.valuesOptions)) {
1826
- var features = Array.isArray(this.valuesOptions[key]) ? this.valuesOptions[key].find(x => {return Object.prototype.hasOwnProperty.call(x, "features");}) : this.valuesOptions[key] === "features";
1827
- if (features) {
1828
- this._valuesProtoFeatures[key] = features.features;
1829
- } else {
1830
- this._valuesProtoFeatures[key] = {};
1831
- }
1832
- }
1833
- }
1834
-
1835
- for (var enumValue of Object.keys(this.values)) {
1836
- if (!this._valuesProtoFeatures[enumValue]) {
1837
- this._valuesProtoFeatures[enumValue] = {};
1838
- }
1839
1821
  }
1840
1822
 
1841
1823
  this.comments[name] = comment || null;
@@ -1923,7 +1905,11 @@ var ruleRe = /^required|optional|repeated$/;
1923
1905
  * @throws {TypeError} If arguments are invalid
1924
1906
  */
1925
1907
  Field.fromJSON = function fromJSON(name, json) {
1926
- return new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);
1908
+ var field = new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);
1909
+ if (json.edition)
1910
+ field._edition = json.edition;
1911
+ field._defaultEdition = "proto3"; // For backwards-compatibility.
1912
+ return field;
1927
1913
  };
1928
1914
 
1929
1915
  /**
@@ -2164,6 +2150,7 @@ Field.prototype.setOption = function setOption(name, value, ifNotSet) {
2164
2150
  Field.prototype.toJSON = function toJSON(toJSONOptions) {
2165
2151
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
2166
2152
  return util.toObject([
2153
+ "edition" , this._editionToJSON(),
2167
2154
  "rule" , this.rule !== "optional" && this.rule || undefined,
2168
2155
  "type" , this.type,
2169
2156
  "id" , this.id,
@@ -2248,14 +2235,23 @@ Field.prototype.resolve = function resolve() {
2248
2235
  * @returns {object} The feature values to override
2249
2236
  */
2250
2237
  Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(edition) {
2251
- if (edition) return {};
2238
+ if (edition !== "proto2" && edition !== "proto3") {
2239
+ return {};
2240
+ }
2252
2241
 
2253
2242
  var features = {};
2243
+
2254
2244
  if (this.rule === "required") {
2255
2245
  features.field_presence = "LEGACY_REQUIRED";
2256
2246
  }
2257
- if (this.resolvedType instanceof Type && this.resolvedType.group) {
2258
- features.message_encoding = "DELIMITED";
2247
+ if (this.parent && types.defaults[this.type] === undefined) {
2248
+ // We can't use resolvedType because types may not have been resolved yet. However,
2249
+ // legacy groups are always in the same scope as the field so we don't have to do a
2250
+ // full scan of the tree.
2251
+ var type = this.parent.get(this.type.split(".").pop());
2252
+ if (type && type instanceof Type && type.group) {
2253
+ features.message_encoding = "DELIMITED";
2254
+ }
2259
2255
  }
2260
2256
  if (this.getOption("packed") === true) {
2261
2257
  features.repeated_field_encoding = "PACKED";
@@ -2265,6 +2261,13 @@ Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(e
2265
2261
  return features;
2266
2262
  };
2267
2263
 
2264
+ /**
2265
+ * @override
2266
+ */
2267
+ Field.prototype._resolveFeatures = function _resolveFeatures(edition) {
2268
+ return ReflectionObject.prototype._resolveFeatures.call(this, this._edition || edition);
2269
+ };
2270
+
2268
2271
  /**
2269
2272
  * Decorator function as returned by {@link Field.d} and {@link MapField.d} (TypeScript).
2270
2273
  * @typedef FieldDecorator
@@ -3004,10 +3007,40 @@ function Namespace(name, options) {
3004
3007
  * @private
3005
3008
  */
3006
3009
  this._nestedArray = null;
3010
+
3011
+ /**
3012
+ * Cache lookup calls for any objects contains anywhere under this namespace.
3013
+ * This drastically speeds up resolve for large cross-linked protos where the same
3014
+ * types are looked up repeatedly.
3015
+ * @type {Object.<string,ReflectionObject|null>}
3016
+ * @private
3017
+ */
3018
+ this._lookupCache = {};
3019
+
3020
+ /**
3021
+ * Whether or not objects contained in this namespace need feature resolution.
3022
+ * @type {boolean}
3023
+ * @protected
3024
+ */
3025
+ this._needsRecursiveFeatureResolution = true;
3026
+
3027
+ /**
3028
+ * Whether or not objects contained in this namespace need a resolve.
3029
+ * @type {boolean}
3030
+ * @protected
3031
+ */
3032
+ this._needsRecursiveResolve = true;
3007
3033
  }
3008
3034
 
3009
3035
  function clearCache(namespace) {
3010
3036
  namespace._nestedArray = null;
3037
+ namespace._lookupCache = {};
3038
+
3039
+ // Also clear parent caches, since they include nested lookups.
3040
+ var parent = namespace;
3041
+ while(parent = parent.parent) {
3042
+ parent._lookupCache = {};
3043
+ }
3011
3044
  return namespace;
3012
3045
  }
3013
3046
 
@@ -3136,6 +3169,25 @@ Namespace.prototype.add = function add(object) {
3136
3169
  }
3137
3170
  }
3138
3171
  this.nested[object.name] = object;
3172
+
3173
+ if (!(this instanceof Type || this instanceof Service || this instanceof Enum || this instanceof Field)) {
3174
+ // This is a package or a root namespace.
3175
+ if (!object._edition) {
3176
+ // Make sure that some edition is set if it hasn't already been specified.
3177
+ object._edition = object._defaultEdition;
3178
+ }
3179
+ }
3180
+
3181
+ this._needsRecursiveFeatureResolution = true;
3182
+ this._needsRecursiveResolve = true;
3183
+
3184
+ // Also clear parent caches, since they need to recurse down.
3185
+ var parent = this;
3186
+ while(parent = parent.parent) {
3187
+ parent._needsRecursiveFeatureResolution = true;
3188
+ parent._needsRecursiveResolve = true;
3189
+ }
3190
+
3139
3191
  object.onAdd(this);
3140
3192
  return clearCache(this);
3141
3193
  };
@@ -3197,6 +3249,10 @@ Namespace.prototype.define = function define(path, json) {
3197
3249
  * @returns {Namespace} `this`
3198
3250
  */
3199
3251
  Namespace.prototype.resolveAll = function resolveAll() {
3252
+ if (!this._needsRecursiveResolve) return this;
3253
+
3254
+ this._resolveFeaturesRecursive(this._edition);
3255
+
3200
3256
  var nested = this.nestedArray, i = 0;
3201
3257
  this.resolve();
3202
3258
  while (i < nested.length)
@@ -3204,6 +3260,23 @@ Namespace.prototype.resolveAll = function resolveAll() {
3204
3260
  nested[i++].resolveAll();
3205
3261
  else
3206
3262
  nested[i++].resolve();
3263
+ this._needsRecursiveResolve = false;
3264
+ return this;
3265
+ };
3266
+
3267
+ /**
3268
+ * @override
3269
+ */
3270
+ Namespace.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
3271
+ if (!this._needsRecursiveFeatureResolution) return this;
3272
+ this._needsRecursiveFeatureResolution = false;
3273
+
3274
+ edition = this._edition || edition;
3275
+
3276
+ ReflectionObject.prototype._resolveFeaturesRecursive.call(this, edition);
3277
+ this.nestedArray.forEach(nested => {
3278
+ nested._resolveFeaturesRecursive(edition);
3279
+ });
3207
3280
  return this;
3208
3281
  };
3209
3282
 
@@ -3215,7 +3288,6 @@ Namespace.prototype.resolveAll = function resolveAll() {
3215
3288
  * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
3216
3289
  */
3217
3290
  Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {
3218
-
3219
3291
  /* istanbul ignore next */
3220
3292
  if (typeof filterTypes === "boolean") {
3221
3293
  parentAlreadyChecked = filterTypes;
@@ -3230,29 +3302,72 @@ Namespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChe
3230
3302
  } else if (!path.length)
3231
3303
  return this;
3232
3304
 
3305
+ var flatPath = path.join(".");
3306
+
3233
3307
  // Start at root if path is absolute
3234
3308
  if (path[0] === "")
3235
3309
  return this.root.lookup(path.slice(1), filterTypes);
3236
3310
 
3311
+ // Early bailout for objects with matching absolute paths
3312
+ var found = this.root._fullyQualifiedObjects && this.root._fullyQualifiedObjects["." + flatPath];
3313
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3314
+ return found;
3315
+ }
3316
+
3317
+ // Do a regular lookup at this namespace and below
3318
+ found = this._lookupImpl(path, flatPath);
3319
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3320
+ return found;
3321
+ }
3322
+
3323
+ if (parentAlreadyChecked)
3324
+ return null;
3325
+
3326
+ // If there hasn't been a match, walk up the tree and look more broadly
3327
+ var current = this;
3328
+ while (current.parent) {
3329
+ found = current.parent._lookupImpl(path, flatPath);
3330
+ if (found && (!filterTypes || filterTypes.indexOf(found.constructor) > -1)) {
3331
+ return found;
3332
+ }
3333
+ current = current.parent;
3334
+ }
3335
+ return null;
3336
+ };
3337
+
3338
+ /**
3339
+ * Internal helper for lookup that handles searching just at this namespace and below along with caching.
3340
+ * @param {string[]} path Path to look up
3341
+ * @param {string} flatPath Flattened version of the path to use as a cache key
3342
+ * @returns {ReflectionObject|null} Looked up object or `null` if none could be found
3343
+ * @private
3344
+ */
3345
+ Namespace.prototype._lookupImpl = function lookup(path, flatPath) {
3346
+ if(Object.prototype.hasOwnProperty.call(this._lookupCache, flatPath)) {
3347
+ return this._lookupCache[flatPath];
3348
+ }
3349
+
3237
3350
  // Test if the first part matches any nested object, and if so, traverse if path contains more
3238
3351
  var found = this.get(path[0]);
3352
+ var exact = null;
3239
3353
  if (found) {
3240
3354
  if (path.length === 1) {
3241
- if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)
3242
- return found;
3243
- } else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))
3244
- return found;
3355
+ exact = found;
3356
+ } else if (found instanceof Namespace) {
3357
+ path = path.slice(1);
3358
+ exact = found._lookupImpl(path, path.join("."));
3359
+ }
3245
3360
 
3246
3361
  // Otherwise try each nested namespace
3247
- } else
3362
+ } else {
3248
3363
  for (var i = 0; i < this.nestedArray.length; ++i)
3249
- if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i].lookup(path, filterTypes, true)))
3250
- return found;
3364
+ if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i]._lookupImpl(path, flatPath)))
3365
+ exact = found;
3366
+ }
3251
3367
 
3252
- // If there hasn't been a match, try again at the parent
3253
- if (this.parent === null || parentAlreadyChecked)
3254
- return null;
3255
- return this.parent.lookup(path, filterTypes);
3368
+ // Set this even when null, so that when we walk up the tree we can quickly bail on repeated checks back down.
3369
+ this._lookupCache[flatPath] = exact;
3370
+ return exact;
3256
3371
  };
3257
3372
 
3258
3373
  /**
@@ -3342,9 +3457,10 @@ var Root; // cyclic
3342
3457
 
3343
3458
  /* eslint-disable no-warning-comments */
3344
3459
  // TODO: Replace with embedded proto.
3345
- var editions2023Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
3346
- var proto2Defaults = {enum_type: "CLOSED", field_presence: "EXPLICIT", json_format: "LEGACY_BEST_EFFORT", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "EXPANDED", utf8_validation: "NONE"};
3347
- var proto3Defaults = {enum_type: "OPEN", field_presence: "IMPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
3460
+ 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" };
3461
+ 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" };
3462
+ 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" };
3463
+ 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" };
3348
3464
 
3349
3465
  /**
3350
3466
  * Constructs a new reflection object instance.
@@ -3380,15 +3496,34 @@ function ReflectionObject(name, options) {
3380
3496
  */
3381
3497
  this.name = name;
3382
3498
 
3499
+ /**
3500
+ * The edition specified for this object. Only relevant for top-level objects.
3501
+ * @type {string}
3502
+ * @private
3503
+ */
3504
+ this._edition = null;
3505
+
3506
+ /**
3507
+ * The default edition to use for this object if none is specified. For legacy reasons,
3508
+ * this is proto2 except in the JSON parsing case where it was proto3.
3509
+ * @type {string}
3510
+ * @private
3511
+ */
3512
+ this._defaultEdition = "proto2";
3513
+
3383
3514
  /**
3384
3515
  * Resolved Features.
3516
+ * @type {object}
3517
+ * @private
3385
3518
  */
3386
3519
  this._features = {};
3387
3520
 
3388
3521
  /**
3389
- * Unresolved Features.
3522
+ * Whether or not features have been resolved.
3523
+ * @type {boolean}
3524
+ * @private
3390
3525
  */
3391
- this._protoFeatures = null;
3526
+ this._featuresResolved = false;
3392
3527
 
3393
3528
  /**
3394
3529
  * Parent namespace.
@@ -3495,38 +3630,63 @@ ReflectionObject.prototype.onRemove = function onRemove(parent) {
3495
3630
  ReflectionObject.prototype.resolve = function resolve() {
3496
3631
  if (this.resolved)
3497
3632
  return this;
3498
- if (this instanceof Root || this.parent && this.parent.resolved) {
3499
- this._resolveFeatures();
3500
- this.resolved = true;
3501
- }
3633
+ if (this.root instanceof Root)
3634
+ this.resolved = true; // only if part of a root
3502
3635
  return this;
3503
3636
  };
3504
3637
 
3638
+ /**
3639
+ * Resolves this objects editions features.
3640
+ * @param {string} edition The edition we're currently resolving for.
3641
+ * @returns {ReflectionObject} `this`
3642
+ */
3643
+ ReflectionObject.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
3644
+ return this._resolveFeatures(this._edition || edition);
3645
+ };
3646
+
3505
3647
  /**
3506
3648
  * Resolves child features from parent features
3649
+ * @param {string} edition The edition we're currently resolving for.
3507
3650
  * @returns {undefined}
3508
3651
  */
3509
- ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
3652
+ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures(edition) {
3653
+ if (this._featuresResolved) {
3654
+ return;
3655
+ }
3656
+
3510
3657
  var defaults = {};
3511
3658
 
3512
- var edition = this.root.getOption("edition");
3513
- if (this instanceof Root) {
3514
- if (this.root.getOption("syntax") === "proto3") {
3659
+ /* istanbul ignore if */
3660
+ if (!edition) {
3661
+ throw new Error("Unknown edition for " + this.fullName);
3662
+ }
3663
+
3664
+ var protoFeatures = Object.assign(this.options ? Object.assign({}, this.options.features) : {},
3665
+ this._inferLegacyProtoFeatures(edition));
3666
+
3667
+ if (this._edition) {
3668
+ // For a namespace marked with a specific edition, reset defaults.
3669
+ /* istanbul ignore else */
3670
+ if (edition === "proto2") {
3671
+ defaults = Object.assign({}, proto2Defaults);
3672
+ } else if (edition === "proto3") {
3515
3673
  defaults = Object.assign({}, proto3Defaults);
3516
3674
  } else if (edition === "2023") {
3517
3675
  defaults = Object.assign({}, editions2023Defaults);
3676
+ } else if (edition === "2024") {
3677
+ defaults = Object.assign({}, editions2024Defaults);
3518
3678
  } else {
3519
- defaults = Object.assign({}, proto2Defaults);
3679
+ throw new Error("Unknown edition: " + edition);
3520
3680
  }
3681
+ this._features = Object.assign(defaults, protoFeatures || {});
3682
+ this._featuresResolved = true;
3683
+ return;
3521
3684
  }
3522
3685
 
3523
- var protoFeatures = Object.assign(Object.assign({}, this._protoFeatures), this._inferLegacyProtoFeatures(edition));
3524
-
3525
- if (this instanceof Root) {
3526
- this._features = Object.assign(defaults, protoFeatures || {});
3527
3686
  // fields in Oneofs aren't actually children of them, so we have to
3528
3687
  // special-case it
3529
- } else if (this.partOf instanceof OneOf) {
3688
+ /* istanbul ignore else */
3689
+ if (this.partOf instanceof OneOf) {
3530
3690
  var lexicalParentFeaturesCopy = Object.assign({}, this.partOf._features);
3531
3691
  this._features = Object.assign(lexicalParentFeaturesCopy, protoFeatures || {});
3532
3692
  } else if (this.declaringField) {
@@ -3535,12 +3695,13 @@ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
3535
3695
  var parentFeaturesCopy = Object.assign({}, this.parent._features);
3536
3696
  this._features = Object.assign(parentFeaturesCopy, protoFeatures || {});
3537
3697
  } else {
3538
- this._features = Object.assign({}, protoFeatures);
3698
+ throw new Error("Unable to find a parent for " + this.fullName);
3539
3699
  }
3540
3700
  if (this.extensionField) {
3541
3701
  // Sister fields should have the same features as their extensions.
3542
3702
  this.extensionField._features = this._features;
3543
3703
  }
3704
+ this._featuresResolved = true;
3544
3705
  };
3545
3706
 
3546
3707
  /**
@@ -3548,7 +3709,6 @@ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
3548
3709
  * in older editions.
3549
3710
  * @param {string|undefined} edition The edition this proto is on, or undefined if pre-editions
3550
3711
  * @returns {object} The feature values to override
3551
- * @abstract
3552
3712
  */
3553
3713
  ReflectionObject.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(/*edition*/) {
3554
3714
  return {};
@@ -3569,12 +3729,19 @@ ReflectionObject.prototype.getOption = function getOption(name) {
3569
3729
  * Sets an option.
3570
3730
  * @param {string} name Option name
3571
3731
  * @param {*} value Option value
3572
- * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set
3732
+ * @param {boolean|undefined} [ifNotSet] Sets the option only if it isn't currently set
3573
3733
  * @returns {ReflectionObject} `this`
3574
3734
  */
3575
3735
  ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {
3576
- if (!ifNotSet || !this.options || this.options[name] === undefined)
3577
- (this.options || (this.options = {}))[name] = value;
3736
+ if (!this.options)
3737
+ this.options = {};
3738
+ if (/^features\./.test(name)) {
3739
+ util.setProperty(this.options, name, value, ifNotSet);
3740
+ } else if (!ifNotSet || this.options[name] === undefined) {
3741
+ if (this.getOption(name) !== value) this.resolved = false;
3742
+ this.options[name] = value;
3743
+ }
3744
+
3578
3745
  return this;
3579
3746
  };
3580
3747
 
@@ -3589,7 +3756,6 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
3589
3756
  if (!this.parsedOptions) {
3590
3757
  this.parsedOptions = [];
3591
3758
  }
3592
- var isFeature = /^features$/.test(name);
3593
3759
  var parsedOptions = this.parsedOptions;
3594
3760
  if (propName) {
3595
3761
  // If setting a sub property of an option then try to merge it
@@ -3615,12 +3781,6 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
3615
3781
  parsedOptions.push(newOpt);
3616
3782
  }
3617
3783
 
3618
-
3619
- if (isFeature) {
3620
- var features = parsedOptions.find(x => {return Object.prototype.hasOwnProperty.call(x, "features");});
3621
- this._protoFeatures = features.features || {};
3622
- }
3623
-
3624
3784
  return this;
3625
3785
  };
3626
3786
 
@@ -3649,6 +3809,19 @@ ReflectionObject.prototype.toString = function toString() {
3649
3809
  return className;
3650
3810
  };
3651
3811
 
3812
+ /**
3813
+ * Converts the edition this object is pinned to for JSON format.
3814
+ * @returns {string|undefined} The edition string for JSON representation
3815
+ */
3816
+ ReflectionObject.prototype._editionToJSON = function _editionToJSON() {
3817
+ if (!this._edition || this._edition === "proto3") {
3818
+ // Avoid emitting proto3 since we need to default to it for backwards
3819
+ // compatibility anyway.
3820
+ return undefined;
3821
+ }
3822
+ return this._edition;
3823
+ };
3824
+
3652
3825
  // Sets up cyclic dependencies (called in index-light)
3653
3826
  ReflectionObject._configure = function(Root_) {
3654
3827
  Root = Root_;
@@ -4387,6 +4560,20 @@ function Root(options) {
4387
4560
  * @type {string[]}
4388
4561
  */
4389
4562
  this.files = [];
4563
+
4564
+ /**
4565
+ * Edition, defaults to proto2 if unspecified.
4566
+ * @type {string}
4567
+ * @private
4568
+ */
4569
+ this._edition = "proto2";
4570
+
4571
+ /**
4572
+ * Global lookup cache of fully qualified names.
4573
+ * @type {Object.<string,ReflectionObject>}
4574
+ * @private
4575
+ */
4576
+ this._fullyQualifiedObjects = {};
4390
4577
  }
4391
4578
 
4392
4579
  /**
@@ -4400,7 +4587,7 @@ Root.fromJSON = function fromJSON(json, root) {
4400
4587
  root = new Root();
4401
4588
  if (json.options)
4402
4589
  root.setOptions(json.options);
4403
- return root.addJSON(json.nested);
4590
+ return root.addJSON(json.nested).resolveAll();
4404
4591
  };
4405
4592
 
4406
4593
  /**
@@ -4455,11 +4642,11 @@ Root.prototype.load = function load(filename, options, callback) {
4455
4642
  if (sync) {
4456
4643
  throw err;
4457
4644
  }
4458
- var cb = callback;
4459
- callback = null;
4460
4645
  if (root) {
4461
4646
  root.resolveAll();
4462
4647
  }
4648
+ var cb = callback;
4649
+ callback = null;
4463
4650
  cb(err, root);
4464
4651
  }
4465
4652
 
@@ -4567,8 +4754,8 @@ Root.prototype.load = function load(filename, options, callback) {
4567
4754
  for (var i = 0, resolved; i < filename.length; ++i)
4568
4755
  if (resolved = self.resolvePath("", filename[i]))
4569
4756
  fetch(resolved);
4570
- self.resolveAll();
4571
4757
  if (sync) {
4758
+ self.resolveAll();
4572
4759
  return self;
4573
4760
  }
4574
4761
  if (!queued) {
@@ -4617,6 +4804,8 @@ Root.prototype.loadSync = function loadSync(filename, options) {
4617
4804
  * @override
4618
4805
  */
4619
4806
  Root.prototype.resolveAll = function resolveAll() {
4807
+ if (!this._needsRecursiveResolve) return this;
4808
+
4620
4809
  if (this.deferred.length)
4621
4810
  throw Error("unresolvable extensions: " + this.deferred.map(function(field) {
4622
4811
  return "'extend " + field.extend + "' in " + field.parent.fullName;
@@ -4683,6 +4872,11 @@ Root.prototype._handleAdd = function _handleAdd(object) {
4683
4872
  object.parent[object.name] = object; // expose namespace as property of its parent
4684
4873
  }
4685
4874
 
4875
+ if (object instanceof Type || object instanceof Enum || object instanceof Field) {
4876
+ // Only store types and enums for quick lookup during resolve.
4877
+ this._fullyQualifiedObjects[object.fullName] = object;
4878
+ }
4879
+
4686
4880
  // The above also adds uppercased (and thus conflict-free) nested types, services and enums as
4687
4881
  // properties of namespaces just like static code does. This allows using a .d.ts generated for
4688
4882
  // a static module with reflection-based solutions where the condition is met.
@@ -4723,6 +4917,8 @@ Root.prototype._handleRemove = function _handleRemove(object) {
4723
4917
  delete object.parent[object.name]; // unexpose namespaces
4724
4918
 
4725
4919
  }
4920
+
4921
+ delete this._fullyQualifiedObjects[object.fullName];
4726
4922
  };
4727
4923
 
4728
4924
  // Sets up cyclic dependencies (called in index-light)
@@ -4994,7 +5190,10 @@ Service.fromJSON = function fromJSON(name, json) {
4994
5190
  service.add(Method.fromJSON(names[i], json.methods[names[i]]));
4995
5191
  if (json.nested)
4996
5192
  service.addJSON(json.nested);
5193
+ if (json.edition)
5194
+ service._edition = json.edition;
4997
5195
  service.comment = json.comment;
5196
+ service._defaultEdition = "proto3"; // For backwards-compatibility.
4998
5197
  return service;
4999
5198
  };
5000
5199
 
@@ -5007,6 +5206,7 @@ Service.prototype.toJSON = function toJSON(toJSONOptions) {
5007
5206
  var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
5008
5207
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
5009
5208
  return util.toObject([
5209
+ "edition" , this._editionToJSON(),
5010
5210
  "options" , inherited && inherited.options || undefined,
5011
5211
  "methods" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},
5012
5212
  "nested" , inherited && inherited.nested || undefined,
@@ -5043,6 +5243,8 @@ Service.prototype.get = function get(name) {
5043
5243
  * @override
5044
5244
  */
5045
5245
  Service.prototype.resolveAll = function resolveAll() {
5246
+ if (!this._needsRecursiveResolve) return this;
5247
+
5046
5248
  Namespace.prototype.resolve.call(this);
5047
5249
  var methods = this.methodsArray;
5048
5250
  for (var i = 0; i < methods.length; ++i)
@@ -5050,6 +5252,21 @@ Service.prototype.resolveAll = function resolveAll() {
5050
5252
  return this;
5051
5253
  };
5052
5254
 
5255
+ /**
5256
+ * @override
5257
+ */
5258
+ Service.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
5259
+ if (!this._needsRecursiveFeatureResolution) return this;
5260
+
5261
+ edition = this._edition || edition;
5262
+
5263
+ Namespace.prototype._resolveFeaturesRecursive.call(this, edition);
5264
+ this.methodsArray.forEach(method => {
5265
+ method._resolveFeaturesRecursive(edition);
5266
+ });
5267
+ return this;
5268
+ };
5269
+
5053
5270
  /**
5054
5271
  * @override
5055
5272
  */
@@ -5379,6 +5596,9 @@ Type.fromJSON = function fromJSON(name, json) {
5379
5596
  type.group = true;
5380
5597
  if (json.comment)
5381
5598
  type.comment = json.comment;
5599
+ if (json.edition)
5600
+ type._edition = json.edition;
5601
+ type._defaultEdition = "proto3"; // For backwards-compatibility.
5382
5602
  return type;
5383
5603
  };
5384
5604
 
@@ -5391,6 +5611,7 @@ Type.prototype.toJSON = function toJSON(toJSONOptions) {
5391
5611
  var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);
5392
5612
  var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;
5393
5613
  return util.toObject([
5614
+ "edition" , this._editionToJSON(),
5394
5615
  "options" , inherited && inherited.options || undefined,
5395
5616
  "oneofs" , Namespace.arrayToJSON(this.oneofsArray, toJSONOptions),
5396
5617
  "fields" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; }), toJSONOptions) || {},
@@ -5406,6 +5627,8 @@ Type.prototype.toJSON = function toJSON(toJSONOptions) {
5406
5627
  * @override
5407
5628
  */
5408
5629
  Type.prototype.resolveAll = function resolveAll() {
5630
+ if (!this._needsRecursiveResolve) return this;
5631
+
5409
5632
  Namespace.prototype.resolveAll.call(this);
5410
5633
  var oneofs = this.oneofsArray; i = 0;
5411
5634
  while (i < oneofs.length)
@@ -5416,6 +5639,24 @@ Type.prototype.resolveAll = function resolveAll() {
5416
5639
  return this;
5417
5640
  };
5418
5641
 
5642
+ /**
5643
+ * @override
5644
+ */
5645
+ Type.prototype._resolveFeaturesRecursive = function _resolveFeaturesRecursive(edition) {
5646
+ if (!this._needsRecursiveFeatureResolution) return this;
5647
+
5648
+ edition = this._edition || edition;
5649
+
5650
+ Namespace.prototype._resolveFeaturesRecursive.call(this, edition);
5651
+ this.oneofsArray.forEach(oneof => {
5652
+ oneof._resolveFeatures(edition);
5653
+ });
5654
+ this.fieldsArray.forEach(field => {
5655
+ field._resolveFeatures(edition);
5656
+ });
5657
+ return this;
5658
+ };
5659
+
5419
5660
  /**
5420
5661
  * @override
5421
5662
  */
@@ -6068,10 +6309,10 @@ util.decorateEnum = function decorateEnum(object) {
6068
6309
  * @param {Object.<string,*>} dst Destination object
6069
6310
  * @param {string} path dot '.' delimited path of the property to set
6070
6311
  * @param {Object} value the value to set
6071
- * @param {boolean} overWrite whether or not to concatenate the values into an array or overwrite; defaults to false.
6312
+ * @param {boolean|undefined} [ifNotSet] Sets the option only if it isn't currently set
6072
6313
  * @returns {Object.<string,*>} Destination object
6073
6314
  */
6074
- util.setProperty = function setProperty(dst, path, value) {
6315
+ util.setProperty = function setProperty(dst, path, value, ifNotSet) {
6075
6316
  function setProp(dst, path, value) {
6076
6317
  var part = path.shift();
6077
6318
  if (part === "__proto__" || part === "prototype") {
@@ -6081,6 +6322,8 @@ util.setProperty = function setProperty(dst, path, value) {
6081
6322
  dst[part] = setProp(dst[part] || {}, path, value);
6082
6323
  } else {
6083
6324
  var prevValue = dst[part];
6325
+ if (prevValue && ifNotSet)
6326
+ return dst;
6084
6327
  if (prevValue)
6085
6328
  value = [].concat(prevValue).concat(value);
6086
6329
  dst[part] = value;