@bufbuild/protobuf 2.12.0 → 2.13.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.
Files changed (224) hide show
  1. package/README.md +5 -5
  2. package/dist/commonjs/codegenv1/boot.d.ts +1 -0
  3. package/dist/{cjs/codegenv1/types.js → commonjs/codegenv1/boot.js} +3 -7
  4. package/dist/{cjs → commonjs}/codegenv1/symbols.d.ts +5 -0
  5. package/dist/{cjs → commonjs}/codegenv1/types.d.ts +9 -5
  6. package/dist/commonjs/codegenv2/enum.d.ts +29 -0
  7. package/dist/{cjs → commonjs}/codegenv2/enum.js +18 -0
  8. package/dist/{cjs → commonjs}/codegenv2/symbols.d.ts +10 -0
  9. package/dist/{cjs → commonjs}/codegenv2/symbols.js +2 -0
  10. package/dist/{cjs → commonjs}/codegenv2/types.d.ts +9 -5
  11. package/dist/{cjs → commonjs}/create.js +3 -3
  12. package/dist/{cjs → commonjs}/descriptors.d.ts +2 -2
  13. package/dist/{cjs → commonjs}/equals.d.ts +8 -3
  14. package/dist/{cjs → commonjs}/equals.js +8 -3
  15. package/dist/{cjs → commonjs}/extensions.d.ts +2 -1
  16. package/dist/{cjs → commonjs}/extensions.js +3 -4
  17. package/dist/{cjs → commonjs}/from-binary.d.ts +16 -2
  18. package/dist/{cjs → commonjs}/from-binary.js +30 -24
  19. package/dist/{cjs → commonjs}/from-json.d.ts +19 -2
  20. package/dist/{cjs → commonjs}/from-json.js +174 -54
  21. package/dist/{cjs → commonjs}/index.d.ts +1 -0
  22. package/dist/{cjs → commonjs}/index.js +1 -0
  23. package/dist/commonjs/json-value.js +15 -0
  24. package/dist/commonjs/package.json +4 -0
  25. package/dist/{cjs → commonjs}/proto-int64.js +1 -0
  26. package/dist/{cjs → commonjs}/reflect/reflect-check.js +3 -1
  27. package/dist/{cjs → commonjs}/reflect/reflect.js +3 -2
  28. package/dist/commonjs/reflect/scalar.d.ts +43 -0
  29. package/dist/{cjs → commonjs}/reflect/scalar.js +33 -6
  30. package/dist/{cjs → commonjs}/reflect/unsafe.js +1 -1
  31. package/dist/{cjs → commonjs}/registry.js +20 -20
  32. package/dist/{cjs → commonjs}/to-binary.js +1 -1
  33. package/dist/{cjs → commonjs}/to-json.js +2 -2
  34. package/dist/commonjs/txtpb/from-text.d.ts +35 -0
  35. package/dist/commonjs/txtpb/from-text.js +684 -0
  36. package/dist/commonjs/txtpb/index.d.ts +4 -0
  37. package/dist/commonjs/txtpb/index.js +21 -0
  38. package/dist/commonjs/txtpb/is-group-like.d.ts +19 -0
  39. package/dist/commonjs/txtpb/is-group-like.js +47 -0
  40. package/dist/commonjs/txtpb/reader.d.ts +91 -0
  41. package/dist/commonjs/txtpb/reader.js +481 -0
  42. package/dist/commonjs/txtpb/to-text.d.ts +34 -0
  43. package/dist/commonjs/txtpb/to-text.js +358 -0
  44. package/dist/commonjs/txtpb/writer.d.ts +76 -0
  45. package/dist/commonjs/txtpb/writer.js +220 -0
  46. package/dist/{cjs → commonjs}/types.d.ts +7 -0
  47. package/dist/{cjs/codegenv2 → commonjs}/types.js +0 -7
  48. package/dist/commonjs/unknown-enum.d.ts +10 -0
  49. package/dist/commonjs/unknown-enum.js +26 -0
  50. package/dist/{cjs → commonjs}/wire/binary-encoding.d.ts +4 -2
  51. package/dist/{cjs → commonjs}/wire/binary-encoding.js +8 -3
  52. package/dist/{cjs → commonjs}/wire/size-delimited.d.ts +19 -1
  53. package/dist/{cjs → commonjs}/wire/size-delimited.js +54 -16
  54. package/dist/esm/codegenv1/boot.d.ts +1 -0
  55. package/dist/esm/codegenv1/boot.js +14 -0
  56. package/dist/esm/codegenv1/symbols.d.ts +5 -0
  57. package/dist/esm/codegenv1/types.d.ts +9 -5
  58. package/dist/esm/codegenv1/types.js +0 -7
  59. package/dist/esm/codegenv2/enum.d.ts +15 -4
  60. package/dist/esm/codegenv2/enum.js +17 -0
  61. package/dist/esm/codegenv2/symbols.d.ts +10 -0
  62. package/dist/esm/codegenv2/symbols.js +2 -0
  63. package/dist/esm/codegenv2/types.d.ts +9 -5
  64. package/dist/esm/codegenv2/types.js +0 -7
  65. package/dist/esm/create.js +3 -3
  66. package/dist/esm/descriptors.d.ts +2 -2
  67. package/dist/esm/equals.d.ts +8 -3
  68. package/dist/esm/equals.js +8 -3
  69. package/dist/esm/extensions.d.ts +2 -1
  70. package/dist/esm/extensions.js +4 -5
  71. package/dist/esm/from-binary.d.ts +16 -2
  72. package/dist/esm/from-binary.js +29 -24
  73. package/dist/esm/from-json.d.ts +19 -2
  74. package/dist/esm/from-json.js +174 -54
  75. package/dist/esm/index.d.ts +1 -0
  76. package/dist/esm/index.js +1 -0
  77. package/dist/esm/package.json +4 -0
  78. package/dist/esm/proto-int64.js +1 -0
  79. package/dist/esm/reflect/reflect-check.js +3 -1
  80. package/dist/esm/reflect/reflect.js +3 -2
  81. package/dist/esm/reflect/scalar.d.ts +27 -5
  82. package/dist/esm/reflect/scalar.js +33 -6
  83. package/dist/esm/reflect/unsafe.js +1 -1
  84. package/dist/esm/registry.js +20 -20
  85. package/dist/esm/to-binary.js +1 -1
  86. package/dist/esm/to-json.js +2 -2
  87. package/dist/esm/txtpb/from-text.d.ts +35 -0
  88. package/dist/esm/txtpb/from-text.js +680 -0
  89. package/dist/esm/txtpb/index.d.ts +4 -0
  90. package/dist/esm/txtpb/index.js +15 -0
  91. package/dist/esm/txtpb/is-group-like.d.ts +19 -0
  92. package/dist/esm/txtpb/is-group-like.js +44 -0
  93. package/dist/esm/txtpb/reader.d.ts +91 -0
  94. package/dist/esm/txtpb/reader.js +477 -0
  95. package/dist/esm/txtpb/to-text.d.ts +34 -0
  96. package/dist/esm/txtpb/to-text.js +355 -0
  97. package/dist/esm/txtpb/writer.d.ts +76 -0
  98. package/dist/esm/txtpb/writer.js +214 -0
  99. package/dist/esm/types.d.ts +7 -0
  100. package/dist/esm/unknown-enum.d.ts +10 -0
  101. package/dist/esm/unknown-enum.js +23 -0
  102. package/dist/esm/wire/binary-encoding.d.ts +4 -2
  103. package/dist/esm/wire/binary-encoding.js +8 -3
  104. package/dist/esm/wire/size-delimited.d.ts +19 -1
  105. package/dist/esm/wire/size-delimited.js +54 -16
  106. package/package.json +109 -30
  107. package/dist/cjs/codegenv2/enum.d.ts +0 -18
  108. package/dist/cjs/package.json +0 -1
  109. package/dist/cjs/reflect/scalar.d.ts +0 -21
  110. /package/dist/{cjs → commonjs}/clone.d.ts +0 -0
  111. /package/dist/{cjs → commonjs}/clone.js +0 -0
  112. /package/dist/{cjs → commonjs}/codegenv1/enum.d.ts +0 -0
  113. /package/dist/{cjs → commonjs}/codegenv1/enum.js +0 -0
  114. /package/dist/{cjs → commonjs}/codegenv1/extension.d.ts +0 -0
  115. /package/dist/{cjs → commonjs}/codegenv1/extension.js +0 -0
  116. /package/dist/{cjs → commonjs}/codegenv1/file.d.ts +0 -0
  117. /package/dist/{cjs → commonjs}/codegenv1/file.js +0 -0
  118. /package/dist/{cjs → commonjs}/codegenv1/index.d.ts +0 -0
  119. /package/dist/{cjs → commonjs}/codegenv1/index.js +0 -0
  120. /package/dist/{cjs → commonjs}/codegenv1/message.d.ts +0 -0
  121. /package/dist/{cjs → commonjs}/codegenv1/message.js +0 -0
  122. /package/dist/{cjs → commonjs}/codegenv1/service.d.ts +0 -0
  123. /package/dist/{cjs → commonjs}/codegenv1/service.js +0 -0
  124. /package/dist/{cjs → commonjs}/codegenv1/symbols.js +0 -0
  125. /package/dist/{cjs → commonjs/codegenv1}/types.js +0 -0
  126. /package/dist/{cjs → commonjs}/codegenv2/boot.d.ts +0 -0
  127. /package/dist/{cjs → commonjs}/codegenv2/boot.js +0 -0
  128. /package/dist/{cjs → commonjs}/codegenv2/embed.d.ts +0 -0
  129. /package/dist/{cjs → commonjs}/codegenv2/embed.js +0 -0
  130. /package/dist/{cjs → commonjs}/codegenv2/extension.d.ts +0 -0
  131. /package/dist/{cjs → commonjs}/codegenv2/extension.js +0 -0
  132. /package/dist/{cjs → commonjs}/codegenv2/file.d.ts +0 -0
  133. /package/dist/{cjs → commonjs}/codegenv2/file.js +0 -0
  134. /package/dist/{cjs → commonjs}/codegenv2/index.d.ts +0 -0
  135. /package/dist/{cjs → commonjs}/codegenv2/index.js +0 -0
  136. /package/dist/{cjs → commonjs}/codegenv2/message.d.ts +0 -0
  137. /package/dist/{cjs → commonjs}/codegenv2/message.js +0 -0
  138. /package/dist/{cjs → commonjs}/codegenv2/restore-json-names.d.ts +0 -0
  139. /package/dist/{cjs → commonjs}/codegenv2/restore-json-names.js +0 -0
  140. /package/dist/{cjs → commonjs}/codegenv2/scalar.d.ts +0 -0
  141. /package/dist/{cjs → commonjs}/codegenv2/scalar.js +0 -0
  142. /package/dist/{cjs → commonjs}/codegenv2/service.d.ts +0 -0
  143. /package/dist/{cjs → commonjs}/codegenv2/service.js +0 -0
  144. /package/dist/{cjs/json-value.js → commonjs/codegenv2/types.js} +0 -0
  145. /package/dist/{cjs → commonjs}/create.d.ts +0 -0
  146. /package/dist/{cjs → commonjs}/descriptors.js +0 -0
  147. /package/dist/{cjs → commonjs}/fields.d.ts +0 -0
  148. /package/dist/{cjs → commonjs}/fields.js +0 -0
  149. /package/dist/{cjs → commonjs}/is-message.d.ts +0 -0
  150. /package/dist/{cjs → commonjs}/is-message.js +0 -0
  151. /package/dist/{cjs → commonjs}/json-value.d.ts +0 -0
  152. /package/dist/{cjs → commonjs}/merge.d.ts +0 -0
  153. /package/dist/{cjs → commonjs}/merge.js +0 -0
  154. /package/dist/{cjs → commonjs}/proto-int64.d.ts +0 -0
  155. /package/dist/{cjs → commonjs}/reflect/error.d.ts +0 -0
  156. /package/dist/{cjs → commonjs}/reflect/error.js +0 -0
  157. /package/dist/{cjs → commonjs}/reflect/guard.d.ts +0 -0
  158. /package/dist/{cjs → commonjs}/reflect/guard.js +0 -0
  159. /package/dist/{cjs → commonjs}/reflect/index.d.ts +0 -0
  160. /package/dist/{cjs → commonjs}/reflect/index.js +0 -0
  161. /package/dist/{cjs → commonjs}/reflect/names.d.ts +0 -0
  162. /package/dist/{cjs → commonjs}/reflect/names.js +0 -0
  163. /package/dist/{cjs → commonjs}/reflect/nested-types.d.ts +0 -0
  164. /package/dist/{cjs → commonjs}/reflect/nested-types.js +0 -0
  165. /package/dist/{cjs → commonjs}/reflect/path.d.ts +0 -0
  166. /package/dist/{cjs → commonjs}/reflect/path.js +0 -0
  167. /package/dist/{cjs → commonjs}/reflect/reflect-check.d.ts +0 -0
  168. /package/dist/{cjs → commonjs}/reflect/reflect-types.d.ts +0 -0
  169. /package/dist/{cjs → commonjs}/reflect/reflect-types.js +0 -0
  170. /package/dist/{cjs → commonjs}/reflect/reflect.d.ts +0 -0
  171. /package/dist/{cjs → commonjs}/reflect/unsafe.d.ts +0 -0
  172. /package/dist/{cjs → commonjs}/registry.d.ts +0 -0
  173. /package/dist/{cjs → commonjs}/to-binary.d.ts +0 -0
  174. /package/dist/{cjs → commonjs}/to-json.d.ts +0 -0
  175. /package/dist/{cjs → commonjs}/wire/base64-encoding.d.ts +0 -0
  176. /package/dist/{cjs → commonjs}/wire/base64-encoding.js +0 -0
  177. /package/dist/{cjs → commonjs}/wire/index.d.ts +0 -0
  178. /package/dist/{cjs → commonjs}/wire/index.js +0 -0
  179. /package/dist/{cjs → commonjs}/wire/text-encoding.d.ts +0 -0
  180. /package/dist/{cjs → commonjs}/wire/text-encoding.js +0 -0
  181. /package/dist/{cjs → commonjs}/wire/text-format.d.ts +0 -0
  182. /package/dist/{cjs → commonjs}/wire/text-format.js +0 -0
  183. /package/dist/{cjs → commonjs}/wire/varint.d.ts +0 -0
  184. /package/dist/{cjs → commonjs}/wire/varint.js +0 -0
  185. /package/dist/{cjs → commonjs}/wkt/any.d.ts +0 -0
  186. /package/dist/{cjs → commonjs}/wkt/any.js +0 -0
  187. /package/dist/{cjs → commonjs}/wkt/duration.d.ts +0 -0
  188. /package/dist/{cjs → commonjs}/wkt/duration.js +0 -0
  189. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/any_pb.d.ts +0 -0
  190. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/any_pb.js +0 -0
  191. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/api_pb.d.ts +0 -0
  192. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/api_pb.js +0 -0
  193. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +0 -0
  194. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/compiler/plugin_pb.js +0 -0
  195. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/cpp_features_pb.d.ts +0 -0
  196. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/cpp_features_pb.js +0 -0
  197. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/descriptor_pb.d.ts +0 -0
  198. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/descriptor_pb.js +0 -0
  199. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/duration_pb.d.ts +0 -0
  200. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/duration_pb.js +0 -0
  201. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/empty_pb.d.ts +0 -0
  202. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/empty_pb.js +0 -0
  203. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/field_mask_pb.d.ts +0 -0
  204. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/field_mask_pb.js +0 -0
  205. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/go_features_pb.d.ts +0 -0
  206. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/go_features_pb.js +0 -0
  207. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/java_features_pb.d.ts +0 -0
  208. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/java_features_pb.js +0 -0
  209. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/source_context_pb.d.ts +0 -0
  210. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/source_context_pb.js +0 -0
  211. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/struct_pb.d.ts +0 -0
  212. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/struct_pb.js +0 -0
  213. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/timestamp_pb.d.ts +0 -0
  214. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/timestamp_pb.js +0 -0
  215. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/type_pb.d.ts +0 -0
  216. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/type_pb.js +0 -0
  217. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/wrappers_pb.d.ts +0 -0
  218. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/wrappers_pb.js +0 -0
  219. /package/dist/{cjs → commonjs}/wkt/index.d.ts +0 -0
  220. /package/dist/{cjs → commonjs}/wkt/index.js +0 -0
  221. /package/dist/{cjs → commonjs}/wkt/timestamp.d.ts +0 -0
  222. /package/dist/{cjs → commonjs}/wkt/timestamp.js +0 -0
  223. /package/dist/{cjs → commonjs}/wkt/wrappers.d.ts +0 -0
  224. /package/dist/{cjs → commonjs}/wkt/wrappers.js +0 -0
@@ -16,19 +16,18 @@ import { scalarZeroValue } from "./reflect/scalar.js";
16
16
  import { reflect } from "./reflect/reflect.js";
17
17
  import { BinaryReader, WireType } from "./wire/binary-encoding.js";
18
18
  import { varint32write } from "./wire/varint.js";
19
- // Default options for parsing binary data.
20
- const readDefaults = {
21
- readUnknownFields: true,
22
- };
23
- function makeReadOptions(options) {
24
- return options ? Object.assign(Object.assign({}, readDefaults), options) : readDefaults;
19
+ /**
20
+ * @private Only exported for getExtension()
21
+ */
22
+ export function makeReadContext(options) {
23
+ return Object.assign(Object.assign({ readUnknownFields: true, recursionLimit: 100 }, options), { depth: 0 });
25
24
  }
26
25
  /**
27
26
  * Parse serialized binary data.
28
27
  */
29
28
  export function fromBinary(schema, bytes, options) {
30
29
  const msg = reflect(schema, undefined, false);
31
- readMessage(msg, new BinaryReader(bytes), makeReadOptions(options), false, bytes.byteLength);
30
+ readMessage(msg, new BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
32
31
  return msg.message;
33
32
  }
34
33
  /**
@@ -41,7 +40,7 @@ export function fromBinary(schema, bytes, options) {
41
40
  * new data.
42
41
  */
43
42
  export function mergeFromBinary(schema, target, bytes, options) {
44
- readMessage(reflect(schema, target, false), new BinaryReader(bytes), makeReadOptions(options), false, bytes.byteLength);
43
+ readMessage(reflect(schema, target, false), new BinaryReader(bytes), makeReadContext(options), false, bytes.byteLength);
45
44
  return target;
46
45
  }
47
46
  /**
@@ -52,8 +51,11 @@ export function mergeFromBinary(schema, target, bytes, options) {
52
51
  *
53
52
  * @private
54
53
  */
55
- function readMessage(message, reader, options, delimited, lengthOrDelimitedFieldNo) {
54
+ function readMessage(message, reader, ctx, delimited, lengthOrDelimitedFieldNo) {
56
55
  var _a;
56
+ if (++ctx.depth > ctx.recursionLimit) {
57
+ throw new Error(`cannot decode ${message.desc} from binary: maximum recursion depth of ${ctx.recursionLimit} reached`);
58
+ }
57
59
  const end = delimited ? reader.len : reader.pos + lengthOrDelimitedFieldNo;
58
60
  let fieldNo;
59
61
  let wireType;
@@ -65,13 +67,15 @@ function readMessage(message, reader, options, delimited, lengthOrDelimitedField
65
67
  }
66
68
  const field = message.findNumber(fieldNo);
67
69
  if (!field) {
68
- const data = reader.skip(wireType, fieldNo);
69
- if (options.readUnknownFields) {
70
+ // Use remaining recursion budget for skipping nested groups
71
+ const recursionLimit = ctx.recursionLimit - ctx.depth;
72
+ const data = reader.skip(wireType, fieldNo, recursionLimit);
73
+ if (ctx.readUnknownFields) {
70
74
  unknownFields.push({ no: fieldNo, wireType, data });
71
75
  }
72
76
  continue;
73
77
  }
74
- readField(message, reader, field, wireType, options);
78
+ readField(message, reader, field, wireType, ctx);
75
79
  }
76
80
  if (delimited) {
77
81
  if (wireType != WireType.EndGroup || fieldNo !== lengthOrDelimitedFieldNo) {
@@ -81,11 +85,12 @@ function readMessage(message, reader, options, delimited, lengthOrDelimitedField
81
85
  if (unknownFields.length > 0) {
82
86
  message.setUnknown(unknownFields);
83
87
  }
88
+ ctx.depth--;
84
89
  }
85
90
  /**
86
- * @private
91
+ * @private Only exported for getExtension()
87
92
  */
88
- export function readField(message, reader, field, wireType, options) {
93
+ export function readField(message, reader, field, wireType, ctx) {
89
94
  var _a;
90
95
  switch (field.fieldKind) {
91
96
  case "scalar":
@@ -101,7 +106,7 @@ export function readField(message, reader, field, wireType, options) {
101
106
  if (ok) {
102
107
  message.set(field, val);
103
108
  }
104
- else if (options.readUnknownFields) {
109
+ else if (ctx.readUnknownFields) {
105
110
  const bytes = [];
106
111
  varint32write(val, bytes);
107
112
  const unknownFields = (_a = message.getUnknown()) !== null && _a !== void 0 ? _a : [];
@@ -115,18 +120,18 @@ export function readField(message, reader, field, wireType, options) {
115
120
  }
116
121
  break;
117
122
  case "message":
118
- message.set(field, readMessageField(reader, options, field, message.get(field)));
123
+ message.set(field, readMessageField(reader, ctx, field, message.get(field)));
119
124
  break;
120
125
  case "list":
121
- readListField(reader, wireType, message.get(field), options);
126
+ readListField(reader, wireType, message.get(field), ctx);
122
127
  break;
123
128
  case "map":
124
- readMapEntry(reader, message.get(field), options);
129
+ readMapEntry(reader, message.get(field), ctx);
125
130
  break;
126
131
  }
127
132
  }
128
133
  // Read a map field, expecting key field = 1, value field = 2
129
- function readMapEntry(reader, map, options) {
134
+ function readMapEntry(reader, map, ctx) {
130
135
  const field = map.field();
131
136
  let key;
132
137
  let val;
@@ -150,7 +155,7 @@ function readMapEntry(reader, map, options) {
150
155
  val = reader.int32();
151
156
  break;
152
157
  case "message":
153
- val = readMessageField(reader, options, field);
158
+ val = readMessageField(reader, ctx, field);
154
159
  break;
155
160
  }
156
161
  break;
@@ -174,11 +179,11 @@ function readMapEntry(reader, map, options) {
174
179
  }
175
180
  map.set(key, val);
176
181
  }
177
- function readListField(reader, wireType, list, options) {
182
+ function readListField(reader, wireType, list, ctx) {
178
183
  var _a;
179
184
  const field = list.field();
180
185
  if (field.listKind === "message") {
181
- list.add(readMessageField(reader, options, field));
186
+ list.add(readMessageField(reader, ctx, field));
182
187
  return;
183
188
  }
184
189
  const scalarType = (_a = field.scalar) !== null && _a !== void 0 ? _a : ScalarType.INT32;
@@ -194,10 +199,10 @@ function readListField(reader, wireType, list, options) {
194
199
  list.add(readScalar(reader, scalarType, field.utf8Validation));
195
200
  }
196
201
  }
197
- function readMessageField(reader, options, field, mergeMessage) {
202
+ function readMessageField(reader, ctx, field, mergeMessage) {
198
203
  const delimited = field.delimitedEncoding;
199
204
  const message = mergeMessage !== null && mergeMessage !== void 0 ? mergeMessage : reflect(field.message, undefined, false);
200
- readMessage(message, reader, options, delimited, delimited ? field.number : reader.uint32());
205
+ readMessage(message, reader, ctx, delimited, delimited ? field.number : reader.uint32());
201
206
  return message;
202
207
  }
203
208
  function readScalar(reader, type, validateUtf8 = false) {
@@ -17,33 +17,50 @@ export interface JsonReadOptions {
17
17
  * from JSON format.
18
18
  */
19
19
  registry?: Registry | undefined;
20
+ /**
21
+ * The maximum depth of nested messages to parse. If a message nests deeper
22
+ * than this limit, parsing fails with an error instead of exhausting the
23
+ * call stack. Defaults to 100.
24
+ */
25
+ recursionLimit: number;
20
26
  }
21
27
  /**
22
28
  * Parse a message from a JSON string.
29
+ *
30
+ * Duplicate keys are rejected.
23
31
  */
24
32
  export declare function fromJsonString<Desc extends DescMessage>(schema: Desc, json: string, options?: Partial<JsonReadOptions>): MessageShape<Desc>;
25
33
  /**
26
- * Parse a message from a JSON string, merging fields.
34
+ * Parse a message from a JSON string, merging fields into the target.
27
35
  *
28
36
  * Repeated fields are appended. Map entries are added, overwriting
29
37
  * existing keys.
30
38
  *
31
39
  * If a message field is already present, it will be merged with the
32
40
  * new data.
41
+ *
42
+ * Duplicate keys in the JSON are rejected, as in `fromJsonString`.
33
43
  */
34
44
  export declare function mergeFromJsonString<Desc extends DescMessage>(schema: Desc, target: MessageShape<Desc>, json: string, options?: Partial<JsonReadOptions>): MessageShape<Desc>;
35
45
  /**
36
46
  * Parse a message from a JSON value.
47
+ *
48
+ * Duplicate keys are rejected, but a value parsed by JSON.parse has already
49
+ * dropped duplicates (the last one wins). Use `fromJsonString` for strict
50
+ * duplicate-key checking.
37
51
  */
38
52
  export declare function fromJson<Desc extends DescMessage>(schema: Desc, json: JsonValue, options?: Partial<JsonReadOptions>): MessageShape<Desc>;
39
53
  /**
40
- * Parse a message from a JSON value, merging fields.
54
+ * Parse a message from a JSON value, merging fields into the target.
41
55
  *
42
56
  * Repeated fields are appended. Map entries are added, overwriting
43
57
  * existing keys.
44
58
  *
45
59
  * If a message field is already present, it will be merged with the
46
60
  * new data.
61
+ *
62
+ * Duplicate keys are rejected as in `fromJson`; use `mergeFromJsonString`
63
+ * for strict checking.
47
64
  */
48
65
  export declare function mergeFromJson<Desc extends DescMessage>(schema: Desc, target: MessageShape<Desc>, json: JsonValue, options?: Partial<JsonReadOptions>): MessageShape<Desc>;
49
66
  /**
@@ -21,38 +21,42 @@ import { protoSnakeCase } from "./reflect/names.js";
21
21
  import { base64Decode } from "./wire/base64-encoding.js";
22
22
  import { hasCustomJsonRepresentation, isWrapperDesc, anyPack, ListValueSchema, NullValue, StructSchema, ValueSchema, } from "./wkt/index.js";
23
23
  import { createExtensionContainer, setExtension } from "./extensions.js";
24
- // Default options for parsing JSON.
25
- const jsonReadDefaults = {
26
- ignoreUnknownFields: false,
27
- };
28
- function makeReadOptions(options) {
29
- return options ? Object.assign(Object.assign({}, jsonReadDefaults), options) : jsonReadDefaults;
24
+ function makeReadContext(options) {
25
+ return Object.assign(Object.assign({ ignoreUnknownFields: false, recursionLimit: 100 }, options), { depth: 0 });
30
26
  }
31
27
  /**
32
28
  * Parse a message from a JSON string.
29
+ *
30
+ * Duplicate keys are rejected.
33
31
  */
34
32
  export function fromJsonString(schema, json, options) {
35
33
  return fromJson(schema, parseJsonString(json, schema.typeName), options);
36
34
  }
37
35
  /**
38
- * Parse a message from a JSON string, merging fields.
36
+ * Parse a message from a JSON string, merging fields into the target.
39
37
  *
40
38
  * Repeated fields are appended. Map entries are added, overwriting
41
39
  * existing keys.
42
40
  *
43
41
  * If a message field is already present, it will be merged with the
44
42
  * new data.
43
+ *
44
+ * Duplicate keys in the JSON are rejected, as in `fromJsonString`.
45
45
  */
46
46
  export function mergeFromJsonString(schema, target, json, options) {
47
47
  return mergeFromJson(schema, target, parseJsonString(json, schema.typeName), options);
48
48
  }
49
49
  /**
50
50
  * Parse a message from a JSON value.
51
+ *
52
+ * Duplicate keys are rejected, but a value parsed by JSON.parse has already
53
+ * dropped duplicates (the last one wins). Use `fromJsonString` for strict
54
+ * duplicate-key checking.
51
55
  */
52
56
  export function fromJson(schema, json, options) {
53
57
  const msg = reflect(schema);
54
58
  try {
55
- readMessage(msg, json, makeReadOptions(options));
59
+ readMessage(msg, json, makeReadContext(options));
56
60
  }
57
61
  catch (e) {
58
62
  if (isFieldError(e)) {
@@ -66,17 +70,20 @@ export function fromJson(schema, json, options) {
66
70
  return msg.message;
67
71
  }
68
72
  /**
69
- * Parse a message from a JSON value, merging fields.
73
+ * Parse a message from a JSON value, merging fields into the target.
70
74
  *
71
75
  * Repeated fields are appended. Map entries are added, overwriting
72
76
  * existing keys.
73
77
  *
74
78
  * If a message field is already present, it will be merged with the
75
79
  * new data.
80
+ *
81
+ * Duplicate keys are rejected as in `fromJson`; use `mergeFromJsonString`
82
+ * for strict checking.
76
83
  */
77
84
  export function mergeFromJson(schema, target, json, options) {
78
85
  try {
79
- readMessage(reflect(schema, target), json, makeReadOptions(options));
86
+ readMessage(reflect(schema, target), json, makeReadContext(options));
80
87
  }
81
88
  catch (e) {
82
89
  if (isFieldError(e)) {
@@ -113,82 +120,95 @@ function getJsonField(desc, jsonKey) {
113
120
  }
114
121
  return (_a = messageJsonFields.get(desc)) === null || _a === void 0 ? void 0 : _a.get(jsonKey);
115
122
  }
116
- function readMessage(msg, json, opts) {
123
+ function readMessage(msg, json, ctx) {
117
124
  var _a;
118
- if (tryWktFromJson(msg, json, opts)) {
125
+ if (++ctx.depth > ctx.recursionLimit) {
126
+ throw new Error(`cannot decode ${msg.desc} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
127
+ }
128
+ if (tryWktFromJson(msg, json, ctx)) {
129
+ ctx.depth--;
119
130
  return;
120
131
  }
121
132
  if (json == null || Array.isArray(json) || typeof json != "object") {
122
133
  throw new Error(`cannot decode ${msg.desc} from JSON: ${formatVal(json)}`);
123
134
  }
124
135
  const oneofSeen = new Map();
136
+ const fieldSeen = new Set();
125
137
  for (const [jsonKey, jsonValue] of Object.entries(json)) {
126
138
  const field = getJsonField(msg.desc, jsonKey);
127
139
  if (field) {
140
+ if (fieldSeen.has(field)) {
141
+ // The same field may be set by its proto name and its JSON name, or by
142
+ // a duplicate or unicode-escaped key that JSON.parse already collapsed.
143
+ // Checked before the null-skip below so that a null entry still counts.
144
+ throw new FieldError(field, "set multiple times");
145
+ }
146
+ fieldSeen.add(field);
147
+ if (field.oneof && jsonValue === null && field.fieldKind == "scalar") {
148
+ // see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
149
+ continue;
150
+ }
128
151
  if (field.oneof) {
129
- if (jsonValue === null && field.fieldKind == "scalar") {
130
- // see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
131
- continue;
132
- }
133
152
  const seen = oneofSeen.get(field.oneof);
134
153
  if (seen !== undefined) {
135
154
  throw new FieldError(field.oneof, `oneof set multiple times by ${seen.name} and ${field.name}`);
136
155
  }
137
156
  oneofSeen.set(field.oneof, field);
138
157
  }
139
- readField(msg, field, jsonValue, opts);
158
+ readField(msg, field, jsonValue, ctx);
140
159
  }
141
160
  else {
142
161
  let extension = undefined;
143
162
  if (jsonKey.startsWith("[") &&
144
163
  jsonKey.endsWith("]") &&
145
164
  // biome-ignore lint/suspicious/noAssignInExpressions: no
146
- (extension = (_a = opts.registry) === null || _a === void 0 ? void 0 : _a.getExtension(jsonKey.substring(1, jsonKey.length - 1))) &&
165
+ (extension = (_a = ctx.registry) === null || _a === void 0 ? void 0 : _a.getExtension(jsonKey.substring(1, jsonKey.length - 1))) &&
147
166
  extension.extendee.typeName === msg.desc.typeName) {
148
167
  const [container, field, get] = createExtensionContainer(extension);
149
- readField(container, field, jsonValue, opts);
168
+ readField(container, field, jsonValue, ctx);
150
169
  setExtension(msg.message, extension, get());
151
170
  }
152
- if (!extension && !opts.ignoreUnknownFields) {
171
+ if (!extension && !ctx.ignoreUnknownFields) {
153
172
  throw new Error(`cannot decode ${msg.desc} from JSON: key "${jsonKey}" is unknown`);
154
173
  }
155
174
  }
156
175
  }
176
+ ctx.depth--;
157
177
  }
158
- function readField(msg, field, json, opts) {
178
+ function readField(msg, field, json, ctx) {
159
179
  switch (field.fieldKind) {
160
180
  case "scalar":
161
181
  readScalarField(msg, field, json);
162
182
  break;
163
183
  case "enum":
164
- readEnumField(msg, field, json, opts);
184
+ readEnumField(msg, field, json, ctx);
165
185
  break;
166
186
  case "message":
167
- readMessageField(msg, field, json, opts);
187
+ readMessageField(msg, field, json, ctx);
168
188
  break;
169
189
  case "list":
170
- readListField(msg.get(field), json, opts);
190
+ readListField(msg.get(field), json, ctx);
171
191
  break;
172
192
  case "map":
173
- readMapField(msg.get(field), json, opts);
193
+ readMapField(msg.get(field), json, ctx);
174
194
  break;
175
195
  }
176
196
  }
177
- function readListOrMapItem(field, json, opts) {
197
+ function readListOrMapItem(field, json, ctx) {
178
198
  if (field.scalar && json !== null) {
179
199
  return scalarFromJson(field, json);
180
200
  }
181
201
  if (field.message && !isResetSentinelNullValue(field, json)) {
182
202
  const msgValue = reflect(field.message);
183
- readMessage(msgValue, json, opts);
203
+ readMessage(msgValue, json, ctx);
184
204
  return msgValue;
185
205
  }
186
206
  if (field.enum && !isResetSentinelNullValue(field, json)) {
187
- return readEnum(field.enum, json, opts.ignoreUnknownFields);
207
+ return readEnum(field.enum, json, ctx.ignoreUnknownFields);
188
208
  }
189
209
  throw new FieldError(field, `${field.fieldKind === "list" ? "list item" : "map value"} must not be null`);
190
210
  }
191
- function readMapField(map, json, opts) {
211
+ function readMapField(map, json, ctx) {
192
212
  if (json === null) {
193
213
  return;
194
214
  }
@@ -196,15 +216,20 @@ function readMapField(map, json, opts) {
196
216
  if (typeof json != "object" || Array.isArray(json)) {
197
217
  throw new FieldError(field, "expected object, got " + formatVal(json));
198
218
  }
219
+ const seen = new Set();
199
220
  for (const [jsonMapKey, jsonMapValue] of Object.entries(json)) {
200
221
  const key = mapKeyFromJson(field.mapKey, jsonMapKey);
201
- const value = readListOrMapItem(field, jsonMapValue, opts);
222
+ if (seen.has(key)) {
223
+ throw new FieldError(field, `duplicate map key "${jsonMapKey}"`);
224
+ }
225
+ seen.add(key);
226
+ const value = readListOrMapItem(field, jsonMapValue, ctx);
202
227
  if (value !== tokenIgnoredUnknownEnum) {
203
228
  map.set(key, value);
204
229
  }
205
230
  }
206
231
  }
207
- function readListField(list, json, opts) {
232
+ function readListField(list, json, ctx) {
208
233
  if (json === null) {
209
234
  return;
210
235
  }
@@ -213,27 +238,27 @@ function readListField(list, json, opts) {
213
238
  throw new FieldError(field, "expected Array, got " + formatVal(json));
214
239
  }
215
240
  for (const jsonItem of json) {
216
- const value = readListOrMapItem(field, jsonItem, opts);
241
+ const value = readListOrMapItem(field, jsonItem, ctx);
217
242
  if (value !== tokenIgnoredUnknownEnum) {
218
243
  list.add(value);
219
244
  }
220
245
  }
221
246
  }
222
- function readMessageField(msg, field, json, opts) {
247
+ function readMessageField(msg, field, json, ctx) {
223
248
  if (isResetSentinelNullValue(field, json)) {
224
249
  msg.clear(field);
225
250
  return;
226
251
  }
227
252
  const msgValue = msg.isSet(field) ? msg.get(field) : reflect(field.message);
228
- readMessage(msgValue, json, opts);
253
+ readMessage(msgValue, json, ctx);
229
254
  msg.set(field, msgValue);
230
255
  }
231
- function readEnumField(msg, field, json, opts) {
256
+ function readEnumField(msg, field, json, ctx) {
232
257
  if (isResetSentinelNullValue(field, json)) {
233
258
  msg.clear(field);
234
259
  return;
235
260
  }
236
- const enumValue = readEnum(field.enum, json, opts.ignoreUnknownFields);
261
+ const enumValue = readEnum(field.enum, json, ctx.ignoreUnknownFields);
237
262
  if (enumValue !== tokenIgnoredUnknownEnum) {
238
263
  msg.set(field, enumValue);
239
264
  }
@@ -362,7 +387,8 @@ function scalarFromJson(field, json) {
362
387
  }
363
388
  /**
364
389
  * Try to parse a JSON value to a map key for the reflect API.
365
- *
390
+ * Canonicalizes 64-bit integers given as string, so that "01 and "1" are one
391
+ * key, and duplicates can raise an error.
366
392
  * Returns the input if the JSON value cannot be converted.
367
393
  */
368
394
  function mapKeyFromJson(type, jsonString) {
@@ -381,6 +407,14 @@ function mapKeyFromJson(type, jsonString) {
381
407
  case ScalarType.SFIXED32:
382
408
  case ScalarType.SINT32:
383
409
  return int32FromJson(jsonString);
410
+ case ScalarType.INT64:
411
+ case ScalarType.SINT64:
412
+ case ScalarType.SFIXED64:
413
+ case ScalarType.UINT64:
414
+ case ScalarType.FIXED64:
415
+ return /^-?0+$/.test(jsonString)
416
+ ? "0"
417
+ : jsonString.replace(/^(-?)0+(?=\d)/, "$1");
384
418
  default:
385
419
  return jsonString;
386
420
  }
@@ -409,9 +443,14 @@ function int32FromJson(json) {
409
443
  }
410
444
  return json;
411
445
  }
446
+ /**
447
+ * Parse a JSON string, rejecting duplicate object keys (which JSON.parse would
448
+ * otherwise silently merge).
449
+ */
412
450
  function parseJsonString(jsonString, typeName) {
451
+ let json;
413
452
  try {
414
- return JSON.parse(jsonString);
453
+ json = JSON.parse(jsonString);
415
454
  }
416
455
  catch (e) {
417
456
  const message = e instanceof Error ? e.message : String(e);
@@ -419,14 +458,91 @@ function parseJsonString(jsonString, typeName) {
419
458
  // @ts-expect-error we use the ES2022 error CTOR option "cause" for better stack traces
420
459
  { cause: e });
421
460
  }
461
+ checkDuplicateKeys(jsonString, typeName);
462
+ return json;
422
463
  }
423
- function tryWktFromJson(msg, jsonValue, opts) {
464
+ /**
465
+ * Scan a JSON string for duplicate object member names at any depth, throwing
466
+ * if any are found. JSON.parse() silently keeps the last of duplicate keys, so
467
+ * this raw-string scan is the only way to reject them. It must only be called
468
+ * with a string that JSON.parse() has already accepted, so it can assume the
469
+ * input is well-formed.
470
+ */
471
+ function checkDuplicateKeys(jsonString, typeName) {
472
+ // One Set of seen member names for each open object; arrays push null.
473
+ const stack = [];
474
+ // Whether the next string token is an object member name.
475
+ let expectKey = false;
476
+ let i = 0;
477
+ while (i < jsonString.length) {
478
+ switch (jsonString[i]) {
479
+ case "{":
480
+ stack.push(new Set());
481
+ expectKey = true;
482
+ i++;
483
+ break;
484
+ case "[":
485
+ stack.push(null);
486
+ expectKey = false;
487
+ i++;
488
+ break;
489
+ case "}":
490
+ case "]":
491
+ stack.pop();
492
+ expectKey = false;
493
+ i++;
494
+ break;
495
+ case ",":
496
+ expectKey = stack[stack.length - 1] != null;
497
+ i++;
498
+ break;
499
+ case ":":
500
+ expectKey = false;
501
+ i++;
502
+ break;
503
+ case '"': {
504
+ const open = i++;
505
+ let escaped = false;
506
+ while (i < jsonString.length) {
507
+ if (jsonString[i] == "\\") {
508
+ escaped = true;
509
+ i += 2; // skip the backslash and the character it escapes
510
+ continue;
511
+ }
512
+ if (jsonString[i] == '"') {
513
+ break;
514
+ }
515
+ i++;
516
+ }
517
+ const close = i++;
518
+ const seen = stack[stack.length - 1];
519
+ if (expectKey && seen) {
520
+ // Decode escapes (rare) so that, for example, a key written with a
521
+ // unicode escape collides with the same key written literally.
522
+ const name = escaped
523
+ ? JSON.parse(jsonString.substring(open, close + 1))
524
+ : jsonString.substring(open + 1, close);
525
+ if (seen.has(name)) {
526
+ throw new Error(`cannot decode message ${typeName} from JSON: duplicate object key "${name}"`);
527
+ }
528
+ seen.add(name);
529
+ }
530
+ expectKey = false;
531
+ break;
532
+ }
533
+ default:
534
+ i++;
535
+ break;
536
+ }
537
+ }
538
+ }
539
+ function tryWktFromJson(msg, jsonValue, ctx) {
424
540
  if (!msg.desc.typeName.startsWith("google.protobuf.")) {
425
541
  return false;
426
542
  }
427
543
  switch (msg.desc.typeName) {
428
544
  case "google.protobuf.Any":
429
- anyFromJson(msg.message, jsonValue, opts);
545
+ anyFromJson(msg.message, jsonValue, ctx);
430
546
  return true;
431
547
  case "google.protobuf.Timestamp":
432
548
  timestampFromJson(msg.message, jsonValue);
@@ -438,13 +554,13 @@ function tryWktFromJson(msg, jsonValue, opts) {
438
554
  fieldMaskFromJson(msg.message, jsonValue);
439
555
  return true;
440
556
  case "google.protobuf.Struct":
441
- structFromJson(msg.message, jsonValue);
557
+ structFromJson(msg.message, jsonValue, ctx);
442
558
  return true;
443
559
  case "google.protobuf.Value":
444
- valueFromJson(msg.message, jsonValue);
560
+ valueFromJson(msg.message, jsonValue, ctx);
445
561
  return true;
446
562
  case "google.protobuf.ListValue":
447
- listValueFromJson(msg.message, jsonValue);
563
+ listValueFromJson(msg.message, jsonValue, ctx);
448
564
  return true;
449
565
  default:
450
566
  if (isWrapperDesc(msg.desc)) {
@@ -460,7 +576,7 @@ function tryWktFromJson(msg, jsonValue, opts) {
460
576
  return false;
461
577
  }
462
578
  }
463
- function anyFromJson(any, json, opts) {
579
+ function anyFromJson(any, json, ctx) {
464
580
  var _a;
465
581
  if (json === null || Array.isArray(json) || typeof json != "object") {
466
582
  throw new Error(`cannot decode message ${any.$typeName} from JSON: expected object but got ${formatVal(json)}`);
@@ -478,7 +594,7 @@ function anyFromJson(any, json, opts) {
478
594
  if (!typeName.length) {
479
595
  throw new Error(`cannot decode message ${any.$typeName} from JSON: "@type" is invalid`);
480
596
  }
481
- const desc = (_a = opts.registry) === null || _a === void 0 ? void 0 : _a.getMessage(typeName);
597
+ const desc = (_a = ctx.registry) === null || _a === void 0 ? void 0 : _a.getMessage(typeName);
482
598
  if (!desc) {
483
599
  throw new Error(`cannot decode message ${any.$typeName} from JSON: ${typeUrl} is not in the type registry`);
484
600
  }
@@ -486,13 +602,13 @@ function anyFromJson(any, json, opts) {
486
602
  if (hasCustomJsonRepresentation(desc) &&
487
603
  Object.prototype.hasOwnProperty.call(json, "value")) {
488
604
  const value = json.value;
489
- readMessage(msg, value, opts);
605
+ readMessage(msg, value, ctx);
490
606
  }
491
607
  else {
492
608
  const copy = Object.assign({}, json);
493
609
  // biome-ignore lint/performance/noDelete: <explanation>
494
610
  delete copy["@type"];
495
- readMessage(msg, copy, opts);
611
+ readMessage(msg, copy, ctx);
496
612
  }
497
613
  anyPack(msg.desc, msg.message, any);
498
614
  }
@@ -558,17 +674,20 @@ function fieldMaskFromJson(fieldMask, json) {
558
674
  return protoSnakeCase(path);
559
675
  });
560
676
  }
561
- function structFromJson(struct, json) {
677
+ function structFromJson(struct, json, ctx) {
562
678
  if (typeof json != "object" || json == null || Array.isArray(json)) {
563
679
  throw new Error(`cannot decode message ${struct.$typeName} from JSON ${formatVal(json)}`);
564
680
  }
565
681
  for (const [k, v] of Object.entries(json)) {
566
682
  const parsedV = create(ValueSchema);
567
- valueFromJson(parsedV, v);
683
+ valueFromJson(parsedV, v, ctx);
568
684
  struct.fields[k] = parsedV;
569
685
  }
570
686
  }
571
- function valueFromJson(value, json) {
687
+ function valueFromJson(value, json, ctx) {
688
+ if (++ctx.depth > ctx.recursionLimit) {
689
+ throw new Error(`cannot decode ${value.$typeName} from JSON: maximum recursion depth of ${ctx.recursionLimit} reached`);
690
+ }
572
691
  switch (typeof json) {
573
692
  case "number":
574
693
  value.kind = { case: "numberValue", value: json };
@@ -585,27 +704,28 @@ function valueFromJson(value, json) {
585
704
  }
586
705
  else if (Array.isArray(json)) {
587
706
  const listValue = create(ListValueSchema);
588
- listValueFromJson(listValue, json);
707
+ listValueFromJson(listValue, json, ctx);
589
708
  value.kind = { case: "listValue", value: listValue };
590
709
  }
591
710
  else {
592
711
  const struct = create(StructSchema);
593
- structFromJson(struct, json);
712
+ structFromJson(struct, json, ctx);
594
713
  value.kind = { case: "structValue", value: struct };
595
714
  }
596
715
  break;
597
716
  default:
598
717
  throw new Error(`cannot decode message ${value.$typeName} from JSON ${formatVal(json)}`);
599
718
  }
719
+ ctx.depth--;
600
720
  return value;
601
721
  }
602
- function listValueFromJson(listValue, json) {
722
+ function listValueFromJson(listValue, json, ctx) {
603
723
  if (!Array.isArray(json)) {
604
724
  throw new Error(`cannot decode message ${listValue.$typeName} from JSON ${formatVal(json)}`);
605
725
  }
606
726
  for (const e of json) {
607
727
  const value = create(ValueSchema);
608
- valueFromJson(value, e);
728
+ valueFromJson(value, e, ctx);
609
729
  listValue.values.push(value);
610
730
  }
611
731
  }