protobufjs 7.4.0 → 8.0.1-experimental

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/index.d.ts CHANGED
@@ -179,9 +179,21 @@ export class Enum extends ReflectionObject {
179
179
  /** Values options, if any */
180
180
  public valuesOptions?: { [k: string]: { [k: string]: any } };
181
181
 
182
+ /** Resolved values features, if any */
183
+ public _valuesFeatures?: { [k: string]: { [k: string]: any } };
184
+
185
+ /** Unresolved values features, if any */
186
+ public _valuesProtoFeatures?: { [k: string]: { [k: string]: any } };
187
+
182
188
  /** Reserved ranges, if any. */
183
189
  public reserved: (number[]|string)[];
184
190
 
191
+ /**
192
+ * Resolves value features
193
+ * @returns `this`
194
+ */
195
+ public resolve(): Enum;
196
+
185
197
  /**
186
198
  * Constructs an enum from an enum descriptor.
187
199
  * @param name Enum name
@@ -267,9 +279,24 @@ export class Field extends FieldBase {
267
279
  */
268
280
  public static fromJSON(name: string, json: IField): Field;
269
281
 
270
- /** Determines whether this field is packed. Only relevant when repeated and working with proto2. */
282
+ /** Determines whether this field is required. */
283
+ public readonly required: boolean;
284
+
285
+ /** Determines whether this field is not required. */
286
+ public readonly optional: boolean;
287
+
288
+ /**
289
+ * Determines whether this field uses tag-delimited encoding. In proto2 this
290
+ * corresponded to group syntax.
291
+ */
292
+ public readonly delimited: boolean;
293
+
294
+ /** Determines whether this field is packed. Only relevant when repeated. */
271
295
  public readonly packed: boolean;
272
296
 
297
+ /** Determines whether this field tracks presence. */
298
+ public readonly hasPresence: boolean;
299
+
273
300
  /**
274
301
  * Field decorator (TypeScript).
275
302
  * @param fieldId Field id
@@ -314,12 +341,6 @@ export class FieldBase extends ReflectionObject {
314
341
  /** Extended type if different from parent. */
315
342
  public extend?: string;
316
343
 
317
- /** Whether this field is required. */
318
- public required: boolean;
319
-
320
- /** Whether this field is optional. */
321
- public optional: boolean;
322
-
323
344
  /** Whether this field is repeated. */
324
345
  public repeated: boolean;
325
346
 
@@ -369,6 +390,14 @@ export class FieldBase extends ReflectionObject {
369
390
  * @throws {Error} If any reference cannot be resolved
370
391
  */
371
392
  public resolve(): Field;
393
+
394
+ /**
395
+ * Infers field features from legacy syntax that may have been specified differently.
396
+ * in older editions.
397
+ * @param edition The edition this proto is on, or undefined if pre-editions
398
+ * @returns The feature values to override
399
+ */
400
+ public _inferLegacyProtoFeatures(edition: (string|undefined)): object;
372
401
  }
373
402
 
374
403
  /** Field descriptor. */
@@ -877,6 +906,12 @@ export abstract class ReflectionObject {
877
906
  /** Unique name within its namespace. */
878
907
  public name: string;
879
908
 
909
+ /** Resolved Features. */
910
+ public _features: any;
911
+
912
+ /** Unresolved Features. */
913
+ public _protoFeatures: any;
914
+
880
915
  /** Parent namespace. */
881
916
  public parent: (Namespace|null);
882
917
 
@@ -919,6 +954,17 @@ export abstract class ReflectionObject {
919
954
  */
920
955
  public resolve(): ReflectionObject;
921
956
 
957
+ /** Resolves child features from parent features */
958
+ public _resolveFeatures(): void;
959
+
960
+ /**
961
+ * Infers features from legacy syntax that may have been specified differently.
962
+ * in older editions.
963
+ * @param edition The edition this proto is on, or undefined if pre-editions
964
+ * @returns The feature values to override
965
+ */
966
+ public _inferLegacyProtoFeatures(edition: (string|undefined)): object;
967
+
922
968
  /**
923
969
  * Gets an option value.
924
970
  * @param name Option name
@@ -1010,6 +1056,13 @@ export class OneOf extends ReflectionObject {
1010
1056
  */
1011
1057
  public remove(field: Field): OneOf;
1012
1058
 
1059
+ /**
1060
+ * Determines whether this field corresponds to a synthetic oneof created for
1061
+ * a proto3 optional field. No behavioral logic should depend on this, but it
1062
+ * can be relevant for reflection.
1063
+ */
1064
+ public readonly isProto3Optional: boolean;
1065
+
1013
1066
  /**
1014
1067
  * OneOf decorator (TypeScript).
1015
1068
  * @param fieldNames Field names
@@ -1055,9 +1108,6 @@ export interface IParserResult {
1055
1108
  /** Weak imports, if any */
1056
1109
  weakImports: (string[]|undefined);
1057
1110
 
1058
- /** Syntax, if specified (either `"proto2"` or `"proto3"`) */
1059
- syntax: (string|undefined);
1060
-
1061
1111
  /** Populated root instance */
1062
1112
  root: Root;
1063
1113
  }
@@ -1255,7 +1305,7 @@ export class Root extends NamespaceBase {
1255
1305
 
1256
1306
  /**
1257
1307
  * Loads a namespace descriptor into a root namespace.
1258
- * @param json Nameespace descriptor
1308
+ * @param json Namespace descriptor
1259
1309
  * @param [root] Root namespace, defaults to create a new one if omitted
1260
1310
  * @returns Root namespace
1261
1311
  */
@@ -2194,9 +2244,10 @@ export namespace util {
2194
2244
  * @param dst Destination object
2195
2245
  * @param path dot '.' delimited path of the property to set
2196
2246
  * @param value the value to set
2247
+ * @param overWrite whether or not to concatenate the values into an array or overwrite; defaults to false.
2197
2248
  * @returns Destination object
2198
2249
  */
2199
- function setProperty(dst: { [k: string]: any }, path: string, value: object): { [k: string]: any };
2250
+ function setProperty(dst: { [k: string]: any }, path: string, value: object, overWrite: boolean): { [k: string]: any };
2200
2251
 
2201
2252
  /** Decorator root (TypeScript). */
2202
2253
  let decorateRoot: Root;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protobufjs",
3
- "version": "7.4.0",
3
+ "version": "8.0.1-experimental",
4
4
  "versionScheme": "~",
5
5
  "description": "Protocol Buffers for JavaScript (& TypeScript).",
6
6
  "author": "Daniel Wirtz <dcode+protobufjs@dcode.io>",
package/src/decoder.js CHANGED
@@ -16,16 +16,14 @@ function missing(field) {
16
16
  */
17
17
  function decoder(mtype) {
18
18
  /* eslint-disable no-unexpected-multiline */
19
- var gen = util.codegen(["r", "l"], mtype.name + "$decode")
19
+ var gen = util.codegen(["r", "l", "e"], mtype.name + "$decode")
20
20
  ("if(!(r instanceof Reader))")
21
21
  ("r=Reader.create(r)")
22
22
  ("var c=l===undefined?r.len:r.pos+l,m=new this.ctor" + (mtype.fieldsArray.filter(function(field) { return field.map; }).length ? ",k,value" : ""))
23
23
  ("while(r.pos<c){")
24
- ("var t=r.uint32()");
25
- if (mtype.group) gen
26
- ("if((t&7)===4)")
27
- ("break");
28
- gen
24
+ ("var t=r.uint32()")
25
+ ("if(t===e)")
26
+ ("break")
29
27
  ("switch(t>>>3){");
30
28
 
31
29
  var i = 0;
@@ -91,15 +89,15 @@ function decoder(mtype) {
91
89
  ("}else");
92
90
 
93
91
  // Non-packed
94
- if (types.basic[type] === undefined) gen(field.resolvedType.group
95
- ? "%s.push(types[%i].decode(r))"
92
+ if (types.basic[type] === undefined) gen(field.delimited
93
+ ? "%s.push(types[%i].decode(r,undefined,((t&~7)|4)))"
96
94
  : "%s.push(types[%i].decode(r,r.uint32()))", ref, i);
97
95
  else gen
98
96
  ("%s.push(r.%s())", ref, type);
99
97
 
100
98
  // Non-repeated
101
- } else if (types.basic[type] === undefined) gen(field.resolvedType.group
102
- ? "%s=types[%i].decode(r)"
99
+ } else if (types.basic[type] === undefined) gen(field.delimited
100
+ ? "%s=types[%i].decode(r,undefined,((t&~7)|4))"
103
101
  : "%s=types[%i].decode(r,r.uint32())", ref, i);
104
102
  else gen
105
103
  ("%s=r.%s()", ref, type);
package/src/encoder.js CHANGED
@@ -15,7 +15,7 @@ var Enum = require("./enum"),
15
15
  * @ignore
16
16
  */
17
17
  function genTypePartial(gen, field, fieldIndex, ref) {
18
- return field.resolvedType.group
18
+ return field.delimited
19
19
  ? gen("types[%i].encode(%s,w.uint32(%i)).uint32(%i)", fieldIndex, ref, (field.id << 3 | 3) >>> 0, (field.id << 3 | 4) >>> 0)
20
20
  : gen("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()", fieldIndex, ref, (field.id << 3 | 2) >>> 0);
21
21
  }
package/src/enum.js CHANGED
@@ -56,6 +56,18 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
56
56
  */
57
57
  this.valuesOptions = valuesOptions;
58
58
 
59
+ /**
60
+ * Resolved values features, if any
61
+ * @type {Object<string, Object<string, *>>|undefined}
62
+ */
63
+ this._valuesFeatures = {};
64
+
65
+ /**
66
+ * Unresolved values features, if any
67
+ * @type {Object<string, Object<string, *>>|undefined}
68
+ */
69
+ this._valuesProtoFeatures = {};
70
+
59
71
  /**
60
72
  * Reserved ranges, if any.
61
73
  * @type {Array.<number[]|string>}
@@ -72,6 +84,22 @@ function Enum(name, values, options, comment, comments, valuesOptions) {
72
84
  this.valuesById[ this.values[keys[i]] = values[keys[i]] ] = keys[i];
73
85
  }
74
86
 
87
+ /**
88
+ * Resolves value features
89
+ * @returns {Enum} `this`
90
+ */
91
+ Enum.prototype.resolve = function resolve() {
92
+ ReflectionObject.prototype.resolve.call(this);
93
+
94
+ for (var key of Object.keys(this._valuesProtoFeatures)) {
95
+ var parentFeaturesCopy = Object.assign({}, this._features);
96
+ this._valuesFeatures[key] = Object.assign(parentFeaturesCopy, this._valuesProtoFeatures[key] || {});
97
+ }
98
+
99
+ return this;
100
+ };
101
+
102
+
75
103
  /**
76
104
  * Enum descriptor.
77
105
  * @interface IEnum
@@ -148,6 +176,21 @@ Enum.prototype.add = function add(name, id, comment, options) {
148
176
  if (this.valuesOptions === undefined)
149
177
  this.valuesOptions = {};
150
178
  this.valuesOptions[name] = options || null;
179
+
180
+ for (var key of Object.keys(this.valuesOptions)) {
181
+ var features = Array.isArray(this.valuesOptions[key]) ? this.valuesOptions[key].find(x => {return Object.prototype.hasOwnProperty.call(x, "features");}) : this.valuesOptions[key] === "features";
182
+ if (features) {
183
+ this._valuesProtoFeatures[key] = features.features;
184
+ } else {
185
+ this._valuesProtoFeatures[key] = {};
186
+ }
187
+ }
188
+ }
189
+
190
+ for (var enumValue of Object.keys(this.values)) {
191
+ if (!this._valuesProtoFeatures[enumValue]) {
192
+ this._valuesProtoFeatures[enumValue] = {};
193
+ }
151
194
  }
152
195
 
153
196
  this.comments[name] = comment || null;
package/src/field.js CHANGED
@@ -105,18 +105,6 @@ function Field(name, id, type, rule, extend, options, comment) {
105
105
  */
106
106
  this.extend = extend || undefined; // toJSON
107
107
 
108
- /**
109
- * Whether this field is required.
110
- * @type {boolean}
111
- */
112
- this.required = rule === "required";
113
-
114
- /**
115
- * Whether this field is optional.
116
- * @type {boolean}
117
- */
118
- this.optional = !this.required;
119
-
120
108
  /**
121
109
  * Whether this field is repeated.
122
110
  * @type {boolean}
@@ -183,13 +171,6 @@ function Field(name, id, type, rule, extend, options, comment) {
183
171
  */
184
172
  this.declaringField = null;
185
173
 
186
- /**
187
- * Internally remembers whether this field is packed.
188
- * @type {boolean|null}
189
- * @private
190
- */
191
- this._packed = null;
192
-
193
174
  /**
194
175
  * Comment for this field.
195
176
  * @type {string|null}
@@ -198,17 +179,69 @@ function Field(name, id, type, rule, extend, options, comment) {
198
179
  }
199
180
 
200
181
  /**
201
- * Determines whether this field is packed. Only relevant when repeated and working with proto2.
182
+ * Determines whether this field is required.
183
+ * @name Field#required
184
+ * @type {boolean}
185
+ * @readonly
186
+ */
187
+ Object.defineProperty(Field.prototype, "required", {
188
+ get: function() {
189
+ return this._features.field_presence === "LEGACY_REQUIRED";
190
+ }
191
+ });
192
+
193
+ /**
194
+ * Determines whether this field is not required.
195
+ * @name Field#optional
196
+ * @type {boolean}
197
+ * @readonly
198
+ */
199
+ Object.defineProperty(Field.prototype, "optional", {
200
+ get: function() {
201
+ return !this.required;
202
+ }
203
+ });
204
+
205
+ /**
206
+ * Determines whether this field uses tag-delimited encoding. In proto2 this
207
+ * corresponded to group syntax.
208
+ * @name Field#delimited
209
+ * @type {boolean}
210
+ * @readonly
211
+ */
212
+ Object.defineProperty(Field.prototype, "delimited", {
213
+ get: function() {
214
+ return this.resolvedType instanceof Type &&
215
+ this._features.message_encoding === "DELIMITED";
216
+ }
217
+ });
218
+
219
+ /**
220
+ * Determines whether this field is packed. Only relevant when repeated.
202
221
  * @name Field#packed
203
222
  * @type {boolean}
204
223
  * @readonly
205
224
  */
206
225
  Object.defineProperty(Field.prototype, "packed", {
207
226
  get: function() {
208
- // defaults to packed=true if not explicity set to false
209
- if (this._packed === null)
210
- this._packed = this.getOption("packed") !== false;
211
- return this._packed;
227
+ return this._features.repeated_field_encoding === "PACKED";
228
+ }
229
+ });
230
+
231
+ /**
232
+ * Determines whether this field tracks presence.
233
+ * @name Field#hasPresence
234
+ * @type {boolean}
235
+ * @readonly
236
+ */
237
+ Object.defineProperty(Field.prototype, "hasPresence", {
238
+ get: function() {
239
+ if (this.repeated || this.map) {
240
+ return false;
241
+ }
242
+ return this.partOf || // oneofs
243
+ this.declaringField || this.extensionField || // extensions
244
+ this._features.field_presence !== "IMPLICIT";
212
245
  }
213
246
  });
214
247
 
@@ -216,8 +249,6 @@ Object.defineProperty(Field.prototype, "packed", {
216
249
  * @override
217
250
  */
218
251
  Field.prototype.setOption = function setOption(name, value, ifNotSet) {
219
- if (name === "packed") // clear cached before setting
220
- this._packed = null;
221
252
  return ReflectionObject.prototype.setOption.call(this, name, value, ifNotSet);
222
253
  };
223
254
 
@@ -284,7 +315,7 @@ Field.prototype.resolve = function resolve() {
284
315
 
285
316
  // remove unnecessary options
286
317
  if (this.options) {
287
- if (this.options.packed === true || this.options.packed !== undefined && this.resolvedType && !(this.resolvedType instanceof Enum))
318
+ if (this.options.packed !== undefined && this.resolvedType && !(this.resolvedType instanceof Enum))
288
319
  delete this.options.packed;
289
320
  if (!Object.keys(this.options).length)
290
321
  this.options = undefined;
@@ -322,6 +353,30 @@ Field.prototype.resolve = function resolve() {
322
353
  return ReflectionObject.prototype.resolve.call(this);
323
354
  };
324
355
 
356
+ /**
357
+ * Infers field features from legacy syntax that may have been specified differently.
358
+ * in older editions.
359
+ * @param {string|undefined} edition The edition this proto is on, or undefined if pre-editions
360
+ * @returns {object} The feature values to override
361
+ */
362
+ Field.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(edition) {
363
+ if (edition) return {};
364
+
365
+ var features = {};
366
+ if (this.rule === "required") {
367
+ features.field_presence = "LEGACY_REQUIRED";
368
+ }
369
+ if (this.resolvedType instanceof Type && this.resolvedType.group) {
370
+ features.message_encoding = "DELIMITED";
371
+ }
372
+ if (this.getOption("packed") === true) {
373
+ features.repeated_field_encoding = "PACKED";
374
+ } else if (this.getOption("packed") === false) {
375
+ features.repeated_field_encoding = "EXPANDED";
376
+ }
377
+ return features;
378
+ };
379
+
325
380
  /**
326
381
  * Decorator function as returned by {@link Field.d} and {@link MapField.d} (TypeScript).
327
382
  * @typedef FieldDecorator
@@ -98,7 +98,7 @@ protobuf.types = require("./types");
98
98
  protobuf.util = require("./util");
99
99
 
100
100
  // Set up possibly cyclic reflection dependencies
101
- protobuf.ReflectionObject._configure(protobuf.Root);
101
+ protobuf.ReflectionObject._configure(protobuf.Root, protobuf.Namespace);
102
102
  protobuf.Namespace._configure(protobuf.Type, protobuf.Service, protobuf.Enum);
103
103
  protobuf.Root._configure(protobuf.Type);
104
104
  protobuf.Field._configure(protobuf.Type);
package/src/namespace.js CHANGED
@@ -302,12 +302,13 @@ Namespace.prototype.define = function define(path, json) {
302
302
  */
303
303
  Namespace.prototype.resolveAll = function resolveAll() {
304
304
  var nested = this.nestedArray, i = 0;
305
+ this.resolve();
305
306
  while (i < nested.length)
306
307
  if (nested[i] instanceof Namespace)
307
308
  nested[i++].resolveAll();
308
309
  else
309
310
  nested[i++].resolve();
310
- return this.resolve();
311
+ return this;
311
312
  };
312
313
 
313
314
  /**
package/src/object.js CHANGED
@@ -3,9 +3,16 @@ module.exports = ReflectionObject;
3
3
 
4
4
  ReflectionObject.className = "ReflectionObject";
5
5
 
6
+ const OneOf = require("./oneof");
6
7
  var util = require("./util");
7
8
 
8
- var Root; // cyclic
9
+ var Root, Namespace; // cyclic
10
+
11
+ /* eslint-disable no-warning-comments */
12
+ // TODO: Replace with embedded proto.
13
+ var editions2023Defaults = {enum_type: "OPEN", field_presence: "EXPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
14
+ var proto2Defaults = {enum_type: "CLOSED", field_presence: "EXPLICIT", json_format: "LEGACY_BEST_EFFORT", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "EXPANDED", utf8_validation: "NONE"};
15
+ var proto3Defaults = {enum_type: "OPEN", field_presence: "IMPLICIT", json_format: "ALLOW", message_encoding: "LENGTH_PREFIXED", repeated_field_encoding: "PACKED", utf8_validation: "VERIFY"};
9
16
 
10
17
  /**
11
18
  * Constructs a new reflection object instance.
@@ -41,6 +48,16 @@ function ReflectionObject(name, options) {
41
48
  */
42
49
  this.name = name;
43
50
 
51
+ /**
52
+ * Resolved Features.
53
+ */
54
+ this._features = {};
55
+
56
+ /**
57
+ * Unresolved Features.
58
+ */
59
+ this._protoFeatures = null;
60
+
44
61
  /**
45
62
  * Parent namespace.
46
63
  * @type {Namespace|null}
@@ -146,11 +163,69 @@ ReflectionObject.prototype.onRemove = function onRemove(parent) {
146
163
  ReflectionObject.prototype.resolve = function resolve() {
147
164
  if (this.resolved)
148
165
  return this;
149
- if (this.root instanceof Root)
150
- this.resolved = true; // only if part of a root
166
+ var edition = this.getOption("edition");
167
+ if ((this instanceof Namespace && edition) || (this.parent && this.parent.resolved)) {
168
+ this._resolveFeatures();
169
+ this.resolved = true;
170
+ }
151
171
  return this;
152
172
  };
153
173
 
174
+ /**
175
+ * Resolves child features from parent features
176
+ * @returns {undefined}
177
+ */
178
+ ReflectionObject.prototype._resolveFeatures = function _resolveFeatures() {
179
+ var defaults = {};
180
+
181
+ var protoFeatures = Object.assign(Object.assign({}, this._protoFeatures), this._inferLegacyProtoFeatures(edition));
182
+
183
+ var edition = this.getOption("edition");
184
+ if (this instanceof Namespace && edition) {
185
+ // For a namespace marked with a specific edition, reset defaults.
186
+ if (edition === "proto2") {
187
+ defaults = Object.assign({}, proto2Defaults);
188
+ } else if (edition === "proto3") {
189
+ defaults = Object.assign({}, proto3Defaults);
190
+ } else if (edition === "2023") {
191
+ defaults = Object.assign({}, editions2023Defaults);
192
+ } else {
193
+ throw new Error("Unknown edition: " + edition);
194
+ }
195
+ this._features = Object.assign(defaults, protoFeatures || {});
196
+ return;
197
+ }
198
+
199
+ // fields in Oneofs aren't actually children of them, so we have to
200
+ // special-case it
201
+ if (this.partOf instanceof OneOf) {
202
+ var lexicalParentFeaturesCopy = Object.assign({}, this.partOf._features);
203
+ this._features = Object.assign(lexicalParentFeaturesCopy, protoFeatures || {});
204
+ } else if (this.declaringField) {
205
+ // Skip feature resolution of sister fields.
206
+ } else if (this.parent) {
207
+ var parentFeaturesCopy = Object.assign({}, this.parent._features);
208
+ this._features = Object.assign(parentFeaturesCopy, protoFeatures || {});
209
+ } else {
210
+ this._features = Object.assign({}, protoFeatures);
211
+ }
212
+ if (this.extensionField) {
213
+ // Sister fields should have the same features as their extensions.
214
+ this.extensionField._features = this._features;
215
+ }
216
+ };
217
+
218
+ /**
219
+ * Infers features from legacy syntax that may have been specified differently.
220
+ * in older editions.
221
+ * @param {string|undefined} edition The edition this proto is on, or undefined if pre-editions
222
+ * @returns {object} The feature values to override
223
+ * @abstract
224
+ */
225
+ ReflectionObject.prototype._inferLegacyProtoFeatures = function _inferLegacyProtoFeatures(/*edition*/) {
226
+ return {};
227
+ };
228
+
154
229
  /**
155
230
  * Gets an option value.
156
231
  * @param {string} name Option name
@@ -170,8 +245,10 @@ ReflectionObject.prototype.getOption = function getOption(name) {
170
245
  * @returns {ReflectionObject} `this`
171
246
  */
172
247
  ReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {
173
- if (!ifNotSet || !this.options || this.options[name] === undefined)
248
+ if (!ifNotSet || !this.options || this.options[name] === undefined) {
249
+ if (this.getOption(name) !== value) this.resolved = false;
174
250
  (this.options || (this.options = {}))[name] = value;
251
+ }
175
252
  return this;
176
253
  };
177
254
 
@@ -186,6 +263,7 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
186
263
  if (!this.parsedOptions) {
187
264
  this.parsedOptions = [];
188
265
  }
266
+ var isFeature = /^features$/.test(name);
189
267
  var parsedOptions = this.parsedOptions;
190
268
  if (propName) {
191
269
  // If setting a sub property of an option then try to merge it
@@ -195,10 +273,11 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
195
273
  });
196
274
  if (opt) {
197
275
  // If we found an existing option - just merge the property value
276
+ // (If it's a feature, will just write over)
198
277
  var newValue = opt[name];
199
278
  util.setProperty(newValue, propName, value);
200
279
  } else {
201
- // otherwise, create a new option, set it's property and add it to the list
280
+ // otherwise, create a new option, set its property and add it to the list
202
281
  opt = {};
203
282
  opt[name] = util.setProperty({}, propName, value);
204
283
  parsedOptions.push(opt);
@@ -209,6 +288,13 @@ ReflectionObject.prototype.setParsedOption = function setParsedOption(name, valu
209
288
  newOpt[name] = value;
210
289
  parsedOptions.push(newOpt);
211
290
  }
291
+
292
+
293
+ if (isFeature) {
294
+ var features = parsedOptions.find(x => {return Object.prototype.hasOwnProperty.call(x, "features");});
295
+ this._protoFeatures = features.features || {};
296
+ }
297
+
212
298
  return this;
213
299
  };
214
300
 
@@ -238,6 +324,7 @@ ReflectionObject.prototype.toString = function toString() {
238
324
  };
239
325
 
240
326
  // Sets up cyclic dependencies (called in index-light)
241
- ReflectionObject._configure = function(Root_) {
327
+ ReflectionObject._configure = function(Root_, Namespace_) {
242
328
  Root = Root_;
329
+ Namespace = Namespace_;
243
330
  };
package/src/oneof.js CHANGED
@@ -171,6 +171,25 @@ OneOf.prototype.onRemove = function onRemove(parent) {
171
171
  ReflectionObject.prototype.onRemove.call(this, parent);
172
172
  };
173
173
 
174
+ /**
175
+ * Determines whether this field corresponds to a synthetic oneof created for
176
+ * a proto3 optional field. No behavioral logic should depend on this, but it
177
+ * can be relevant for reflection.
178
+ * @name OneOf#isProto3Optional
179
+ * @type {boolean}
180
+ * @readonly
181
+ */
182
+ Object.defineProperty(OneOf.prototype, "isProto3Optional", {
183
+ get: function() {
184
+ if (this.fieldsArray == null || this.fieldsArray.length !== 1) {
185
+ return false;
186
+ }
187
+
188
+ var field = this.fieldsArray[0];
189
+ return field.options != null && field.options["proto3_optional"] === true;
190
+ }
191
+ });
192
+
174
193
  /**
175
194
  * Decorator function as returned by {@link OneOf.d} (TypeScript).
176
195
  * @typedef OneOfDecorator