@bufbuild/protobuf 2.11.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +3 -3
  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/types.d.ts +9 -5
  5. package/dist/{cjs → commonjs}/codegenv2/types.d.ts +9 -5
  6. package/dist/{cjs → commonjs}/descriptors.d.ts +12 -5
  7. package/dist/{cjs → commonjs}/equals.d.ts +8 -3
  8. package/dist/{cjs → commonjs}/equals.js +8 -3
  9. package/dist/{cjs → commonjs}/extensions.d.ts +2 -1
  10. package/dist/{cjs → commonjs}/extensions.js +3 -4
  11. package/dist/{cjs → commonjs}/from-binary.d.ts +16 -2
  12. package/dist/{cjs → commonjs}/from-binary.js +37 -31
  13. package/dist/{cjs → commonjs}/from-json.d.ts +20 -3
  14. package/dist/{cjs → commonjs}/from-json.js +175 -55
  15. package/dist/commonjs/json-value.js +15 -0
  16. package/dist/commonjs/package.json +4 -0
  17. package/dist/{cjs → commonjs}/proto-int64.js +1 -0
  18. package/dist/{cjs → commonjs}/reflect/path.d.ts +1 -1
  19. package/dist/{cjs → commonjs}/reflect/reflect-check.js +3 -1
  20. package/dist/commonjs/reflect/scalar.d.ts +43 -0
  21. package/dist/{cjs → commonjs}/reflect/scalar.js +33 -6
  22. package/dist/{cjs → commonjs}/registry.js +24 -1
  23. package/dist/{cjs → commonjs}/to-json.d.ts +1 -1
  24. package/dist/{cjs → commonjs}/to-json.js +9 -10
  25. package/dist/{cjs → commonjs}/types.d.ts +1 -1
  26. package/dist/{cjs/codegenv2 → commonjs}/types.js +0 -7
  27. package/dist/{cjs → commonjs}/wire/binary-encoding.d.ts +13 -9
  28. package/dist/{cjs → commonjs}/wire/binary-encoding.js +26 -11
  29. package/dist/{cjs → commonjs}/wire/size-delimited.d.ts +1 -1
  30. package/dist/{cjs → commonjs}/wire/text-encoding.d.ts +4 -2
  31. package/dist/{cjs → commonjs}/wire/text-encoding.js +8 -1
  32. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/api_pb.d.ts +1 -1
  33. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +2 -2
  34. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/descriptor_pb.d.ts +37 -23
  35. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/descriptor_pb.js +1 -1
  36. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/field_mask_pb.d.ts +32 -36
  37. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/go_features_pb.d.ts +65 -0
  38. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/go_features_pb.js +38 -2
  39. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/timestamp_pb.d.ts +5 -5
  40. package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/type_pb.d.ts +3 -3
  41. package/dist/{cjs → commonjs}/wkt/wrappers.d.ts +10 -0
  42. package/dist/{cjs → commonjs}/wkt/wrappers.js +24 -0
  43. package/dist/esm/codegenv1/boot.d.ts +1 -0
  44. package/dist/esm/codegenv1/boot.js +14 -0
  45. package/dist/esm/codegenv1/types.d.ts +9 -5
  46. package/dist/esm/codegenv1/types.js +0 -7
  47. package/dist/esm/codegenv2/types.d.ts +9 -5
  48. package/dist/esm/codegenv2/types.js +0 -7
  49. package/dist/esm/descriptors.d.ts +12 -5
  50. package/dist/esm/equals.d.ts +8 -3
  51. package/dist/esm/equals.js +8 -3
  52. package/dist/esm/extensions.d.ts +2 -1
  53. package/dist/esm/extensions.js +4 -5
  54. package/dist/esm/from-binary.d.ts +16 -2
  55. package/dist/esm/from-binary.js +36 -31
  56. package/dist/esm/from-json.d.ts +20 -3
  57. package/dist/esm/from-json.js +176 -56
  58. package/dist/esm/package.json +4 -0
  59. package/dist/esm/proto-int64.js +1 -0
  60. package/dist/esm/reflect/path.d.ts +1 -1
  61. package/dist/esm/reflect/reflect-check.js +3 -1
  62. package/dist/esm/reflect/scalar.d.ts +27 -5
  63. package/dist/esm/reflect/scalar.js +33 -6
  64. package/dist/esm/registry.js +24 -1
  65. package/dist/esm/to-json.d.ts +1 -1
  66. package/dist/esm/to-json.js +10 -11
  67. package/dist/esm/types.d.ts +1 -1
  68. package/dist/esm/wire/binary-encoding.d.ts +13 -9
  69. package/dist/esm/wire/binary-encoding.js +26 -11
  70. package/dist/esm/wire/size-delimited.d.ts +1 -1
  71. package/dist/esm/wire/text-encoding.d.ts +4 -2
  72. package/dist/esm/wire/text-encoding.js +8 -1
  73. package/dist/esm/wkt/gen/google/protobuf/api_pb.d.ts +1 -1
  74. package/dist/esm/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +2 -2
  75. package/dist/esm/wkt/gen/google/protobuf/descriptor_pb.d.ts +37 -23
  76. package/dist/esm/wkt/gen/google/protobuf/descriptor_pb.js +1 -1
  77. package/dist/esm/wkt/gen/google/protobuf/field_mask_pb.d.ts +32 -36
  78. package/dist/esm/wkt/gen/google/protobuf/go_features_pb.d.ts +65 -0
  79. package/dist/esm/wkt/gen/google/protobuf/go_features_pb.js +37 -1
  80. package/dist/esm/wkt/gen/google/protobuf/timestamp_pb.d.ts +5 -5
  81. package/dist/esm/wkt/gen/google/protobuf/type_pb.d.ts +3 -3
  82. package/dist/esm/wkt/wrappers.d.ts +10 -0
  83. package/dist/esm/wkt/wrappers.js +23 -0
  84. package/package.json +97 -31
  85. package/dist/cjs/package.json +0 -1
  86. package/dist/cjs/reflect/scalar.d.ts +0 -21
  87. /package/dist/{cjs → commonjs}/clone.d.ts +0 -0
  88. /package/dist/{cjs → commonjs}/clone.js +0 -0
  89. /package/dist/{cjs → commonjs}/codegenv1/enum.d.ts +0 -0
  90. /package/dist/{cjs → commonjs}/codegenv1/enum.js +0 -0
  91. /package/dist/{cjs → commonjs}/codegenv1/extension.d.ts +0 -0
  92. /package/dist/{cjs → commonjs}/codegenv1/extension.js +0 -0
  93. /package/dist/{cjs → commonjs}/codegenv1/file.d.ts +0 -0
  94. /package/dist/{cjs → commonjs}/codegenv1/file.js +0 -0
  95. /package/dist/{cjs → commonjs}/codegenv1/index.d.ts +0 -0
  96. /package/dist/{cjs → commonjs}/codegenv1/index.js +0 -0
  97. /package/dist/{cjs → commonjs}/codegenv1/message.d.ts +0 -0
  98. /package/dist/{cjs → commonjs}/codegenv1/message.js +0 -0
  99. /package/dist/{cjs → commonjs}/codegenv1/service.d.ts +0 -0
  100. /package/dist/{cjs → commonjs}/codegenv1/service.js +0 -0
  101. /package/dist/{cjs → commonjs}/codegenv1/symbols.d.ts +0 -0
  102. /package/dist/{cjs → commonjs}/codegenv1/symbols.js +0 -0
  103. /package/dist/{cjs → commonjs/codegenv1}/types.js +0 -0
  104. /package/dist/{cjs → commonjs}/codegenv2/boot.d.ts +0 -0
  105. /package/dist/{cjs → commonjs}/codegenv2/boot.js +0 -0
  106. /package/dist/{cjs → commonjs}/codegenv2/embed.d.ts +0 -0
  107. /package/dist/{cjs → commonjs}/codegenv2/embed.js +0 -0
  108. /package/dist/{cjs → commonjs}/codegenv2/enum.d.ts +0 -0
  109. /package/dist/{cjs → commonjs}/codegenv2/enum.js +0 -0
  110. /package/dist/{cjs → commonjs}/codegenv2/extension.d.ts +0 -0
  111. /package/dist/{cjs → commonjs}/codegenv2/extension.js +0 -0
  112. /package/dist/{cjs → commonjs}/codegenv2/file.d.ts +0 -0
  113. /package/dist/{cjs → commonjs}/codegenv2/file.js +0 -0
  114. /package/dist/{cjs → commonjs}/codegenv2/index.d.ts +0 -0
  115. /package/dist/{cjs → commonjs}/codegenv2/index.js +0 -0
  116. /package/dist/{cjs → commonjs}/codegenv2/message.d.ts +0 -0
  117. /package/dist/{cjs → commonjs}/codegenv2/message.js +0 -0
  118. /package/dist/{cjs → commonjs}/codegenv2/restore-json-names.d.ts +0 -0
  119. /package/dist/{cjs → commonjs}/codegenv2/restore-json-names.js +0 -0
  120. /package/dist/{cjs → commonjs}/codegenv2/scalar.d.ts +0 -0
  121. /package/dist/{cjs → commonjs}/codegenv2/scalar.js +0 -0
  122. /package/dist/{cjs → commonjs}/codegenv2/service.d.ts +0 -0
  123. /package/dist/{cjs → commonjs}/codegenv2/service.js +0 -0
  124. /package/dist/{cjs → commonjs}/codegenv2/symbols.d.ts +0 -0
  125. /package/dist/{cjs → commonjs}/codegenv2/symbols.js +0 -0
  126. /package/dist/{cjs/json-value.js → commonjs/codegenv2/types.js} +0 -0
  127. /package/dist/{cjs → commonjs}/create.d.ts +0 -0
  128. /package/dist/{cjs → commonjs}/create.js +0 -0
  129. /package/dist/{cjs → commonjs}/descriptors.js +0 -0
  130. /package/dist/{cjs → commonjs}/fields.d.ts +0 -0
  131. /package/dist/{cjs → commonjs}/fields.js +0 -0
  132. /package/dist/{cjs → commonjs}/index.d.ts +0 -0
  133. /package/dist/{cjs → commonjs}/index.js +0 -0
  134. /package/dist/{cjs → commonjs}/is-message.d.ts +0 -0
  135. /package/dist/{cjs → commonjs}/is-message.js +0 -0
  136. /package/dist/{cjs → commonjs}/json-value.d.ts +0 -0
  137. /package/dist/{cjs → commonjs}/merge.d.ts +0 -0
  138. /package/dist/{cjs → commonjs}/merge.js +0 -0
  139. /package/dist/{cjs → commonjs}/proto-int64.d.ts +0 -0
  140. /package/dist/{cjs → commonjs}/reflect/error.d.ts +0 -0
  141. /package/dist/{cjs → commonjs}/reflect/error.js +0 -0
  142. /package/dist/{cjs → commonjs}/reflect/guard.d.ts +0 -0
  143. /package/dist/{cjs → commonjs}/reflect/guard.js +0 -0
  144. /package/dist/{cjs → commonjs}/reflect/index.d.ts +0 -0
  145. /package/dist/{cjs → commonjs}/reflect/index.js +0 -0
  146. /package/dist/{cjs → commonjs}/reflect/names.d.ts +0 -0
  147. /package/dist/{cjs → commonjs}/reflect/names.js +0 -0
  148. /package/dist/{cjs → commonjs}/reflect/nested-types.d.ts +0 -0
  149. /package/dist/{cjs → commonjs}/reflect/nested-types.js +0 -0
  150. /package/dist/{cjs → commonjs}/reflect/path.js +0 -0
  151. /package/dist/{cjs → commonjs}/reflect/reflect-check.d.ts +0 -0
  152. /package/dist/{cjs → commonjs}/reflect/reflect-types.d.ts +0 -0
  153. /package/dist/{cjs → commonjs}/reflect/reflect-types.js +0 -0
  154. /package/dist/{cjs → commonjs}/reflect/reflect.d.ts +0 -0
  155. /package/dist/{cjs → commonjs}/reflect/reflect.js +0 -0
  156. /package/dist/{cjs → commonjs}/reflect/unsafe.d.ts +0 -0
  157. /package/dist/{cjs → commonjs}/reflect/unsafe.js +0 -0
  158. /package/dist/{cjs → commonjs}/registry.d.ts +0 -0
  159. /package/dist/{cjs → commonjs}/to-binary.d.ts +0 -0
  160. /package/dist/{cjs → commonjs}/to-binary.js +0 -0
  161. /package/dist/{cjs → commonjs}/wire/base64-encoding.d.ts +0 -0
  162. /package/dist/{cjs → commonjs}/wire/base64-encoding.js +0 -0
  163. /package/dist/{cjs → commonjs}/wire/index.d.ts +0 -0
  164. /package/dist/{cjs → commonjs}/wire/index.js +0 -0
  165. /package/dist/{cjs → commonjs}/wire/size-delimited.js +0 -0
  166. /package/dist/{cjs → commonjs}/wire/text-format.d.ts +0 -0
  167. /package/dist/{cjs → commonjs}/wire/text-format.js +0 -0
  168. /package/dist/{cjs → commonjs}/wire/varint.d.ts +0 -0
  169. /package/dist/{cjs → commonjs}/wire/varint.js +0 -0
  170. /package/dist/{cjs → commonjs}/wkt/any.d.ts +0 -0
  171. /package/dist/{cjs → commonjs}/wkt/any.js +0 -0
  172. /package/dist/{cjs → commonjs}/wkt/duration.d.ts +0 -0
  173. /package/dist/{cjs → commonjs}/wkt/duration.js +0 -0
  174. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/any_pb.d.ts +0 -0
  175. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/any_pb.js +0 -0
  176. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/api_pb.js +0 -0
  177. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/compiler/plugin_pb.js +0 -0
  178. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/cpp_features_pb.d.ts +0 -0
  179. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/cpp_features_pb.js +0 -0
  180. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/duration_pb.d.ts +0 -0
  181. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/duration_pb.js +0 -0
  182. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/empty_pb.d.ts +0 -0
  183. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/empty_pb.js +0 -0
  184. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/field_mask_pb.js +0 -0
  185. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/java_features_pb.d.ts +0 -0
  186. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/java_features_pb.js +0 -0
  187. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/source_context_pb.d.ts +0 -0
  188. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/source_context_pb.js +0 -0
  189. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/struct_pb.d.ts +0 -0
  190. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/struct_pb.js +0 -0
  191. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/timestamp_pb.js +0 -0
  192. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/type_pb.js +0 -0
  193. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/wrappers_pb.d.ts +0 -0
  194. /package/dist/{cjs → commonjs}/wkt/gen/google/protobuf/wrappers_pb.js +0 -0
  195. /package/dist/{cjs → commonjs}/wkt/index.d.ts +0 -0
  196. /package/dist/{cjs → commonjs}/wkt/index.js +0 -0
  197. /package/dist/{cjs → commonjs}/wkt/timestamp.d.ts +0 -0
  198. /package/dist/{cjs → commonjs}/wkt/timestamp.js +0 -0
@@ -19,40 +19,44 @@ import { FieldError, isFieldError } from "./reflect/error.js";
19
19
  import { formatVal } from "./reflect/reflect-check.js";
20
20
  import { protoSnakeCase } from "./reflect/names.js";
21
21
  import { base64Decode } from "./wire/base64-encoding.js";
22
- import { isWrapperDesc, anyPack, ListValueSchema, NullValue, StructSchema, ValueSchema, } from "./wkt/index.js";
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,21 +594,21 @@ 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
  }
485
601
  const msg = reflect(desc);
486
- if (typeName.startsWith("google.protobuf.") &&
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
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "type": "module",
3
+ "sideEffects": false
4
+ }
@@ -25,6 +25,7 @@ function makeInt64Support() {
25
25
  typeof dv.setBigInt64 === "function" &&
26
26
  typeof dv.setBigUint64 === "function" &&
27
27
  (!!globalThis.Deno ||
28
+ !!globalThis.Bun ||
28
29
  typeof process != "object" ||
29
30
  typeof process.env != "object" ||
30
31
  process.env.BUF_BIGINT_DISABLE !== "1");
@@ -89,7 +89,7 @@ export declare function buildPath(schema: DescMessage): PathBuilder;
89
89
  * paths that refer to an extension.
90
90
  */
91
91
  export declare function parsePath(schema: DescMessage, path: string, options?: {
92
- registry?: Registry;
92
+ registry?: Registry | undefined;
93
93
  }): Path;
94
94
  /**
95
95
  * Stringify a path.
@@ -74,7 +74,9 @@ function checkSingular(field, value) {
74
74
  }
75
75
  if (field.enum !== undefined) {
76
76
  if (field.enum.open) {
77
- return Number.isInteger(value);
77
+ // Open enums accept unrecognized values, but enum values are always
78
+ // int32 (see https://protobuf.dev/programming-guides/proto3/#enum).
79
+ return checkScalarValue(value, ScalarType.INT32);
78
80
  }
79
81
  return field.enum.values.some((v) => v.number === value);
80
82
  }
@@ -5,17 +5,39 @@ import { ScalarType } from "../descriptors.js";
5
5
  export type ScalarValue<T = ScalarType, LongAsString extends boolean = false> = T extends ScalarType.STRING ? string : T extends ScalarType.INT32 ? number : T extends ScalarType.UINT32 ? number : T extends ScalarType.SINT32 ? number : T extends ScalarType.FIXED32 ? number : T extends ScalarType.SFIXED32 ? number : T extends ScalarType.FLOAT ? number : T extends ScalarType.DOUBLE ? number : T extends ScalarType.INT64 ? LongAsString extends true ? string : bigint : T extends ScalarType.SINT64 ? LongAsString extends true ? string : bigint : T extends ScalarType.SFIXED64 ? LongAsString extends true ? string : bigint : T extends ScalarType.UINT64 ? LongAsString extends true ? string : bigint : T extends ScalarType.FIXED64 ? LongAsString extends true ? string : bigint : T extends ScalarType.BOOL ? boolean : T extends ScalarType.BYTES ? Uint8Array : never;
6
6
  /**
7
7
  * Returns true if both scalar values are equal.
8
+ *
9
+ * For float and double, values are compared following IEEE semantics: -0
10
+ * equals 0, and NaN does not equal NaN. This is value equality, not identity.
11
+ *
12
+ * It deliberately differs from isScalarZeroValue, which treats -0 as distinct
13
+ * from 0. A value can equal the zero value without being a zero value
14
+ * (scalarEquals(DOUBLE, -0, 0) is true while isScalarZeroValue(DOUBLE, -0) is
15
+ * false) so this function must not be used to derive implicit presence.
8
16
  */
9
17
  export declare function scalarEquals(type: ScalarType, a: ScalarValue | undefined, b: ScalarValue | undefined): boolean;
10
18
  /**
11
- * Returns the zero value for the given scalar type.
19
+ * Returns the zero value for the given scalar type, the value a field of this
20
+ * type has when unset: 0 for numeric types, "" for strings, false for
21
+ * booleans, and an empty Uint8Array for bytes. For 64-bit integer types, the
22
+ * result is "0" when longAsString is true, otherwise 0n.
23
+ *
24
+ * This is the type's zero value, not a proto2 custom field default. For float
25
+ * and double it is +0; isScalarZeroValue treats only +0, not -0, as this value.
12
26
  */
13
27
  export declare function scalarZeroValue<T extends ScalarType, LongAsString extends boolean>(type: T, longAsString: LongAsString): ScalarValue<T, LongAsString>;
14
28
  /**
15
- * Returns true for a zero-value. For example, an integer has the zero-value `0`,
16
- * a boolean is `false`, a string is `""`, and bytes is an empty Uint8Array.
29
+ * Returns true if the value is the zero value for the given scalar type: `0`
30
+ * for numeric types, `false` for booleans, `""` for strings, and an empty
31
+ * Uint8Array for bytes.
32
+ *
33
+ * This is the implicit-presence default check. A singular field with implicit
34
+ * presence is treated as unset, and omitted from the wire, when its value is
35
+ * the zero value. With explicit presence, or in repeated and map fields,
36
+ * presence is structural and this function does not apply.
17
37
  *
18
- * In proto3, zero-values are not written to the wire, unless the field is
19
- * optional or repeated.
38
+ * Note that -0 is NOT a zero value for float and double: under implicit
39
+ * presence, +0 is omitted from the wire but -0 is written, following the
40
+ * proto3 specification. As a result this can disagree with scalarEquals, which
41
+ * compares by value and treats -0 as equal to 0.
20
42
  */
21
43
  export declare function isScalarZeroValue(type: ScalarType, value: unknown): boolean;
@@ -15,6 +15,14 @@ import { protoInt64 } from "../proto-int64.js";
15
15
  import { ScalarType } from "../descriptors.js";
16
16
  /**
17
17
  * Returns true if both scalar values are equal.
18
+ *
19
+ * For float and double, values are compared following IEEE semantics: -0
20
+ * equals 0, and NaN does not equal NaN. This is value equality, not identity.
21
+ *
22
+ * It deliberately differs from isScalarZeroValue, which treats -0 as distinct
23
+ * from 0. A value can equal the zero value without being a zero value
24
+ * (scalarEquals(DOUBLE, -0, 0) is true while isScalarZeroValue(DOUBLE, -0) is
25
+ * false) so this function must not be used to derive implicit presence.
18
26
  */
19
27
  export function scalarEquals(type, a, b) {
20
28
  if (a === b) {
@@ -51,7 +59,13 @@ export function scalarEquals(type, a, b) {
51
59
  return false;
52
60
  }
53
61
  /**
54
- * Returns the zero value for the given scalar type.
62
+ * Returns the zero value for the given scalar type, the value a field of this
63
+ * type has when unset: 0 for numeric types, "" for strings, false for
64
+ * booleans, and an empty Uint8Array for bytes. For 64-bit integer types, the
65
+ * result is "0" when longAsString is true, otherwise 0n.
66
+ *
67
+ * This is the type's zero value, not a proto2 custom field default. For float
68
+ * and double it is +0; isScalarZeroValue treats only +0, not -0, as this value.
55
69
  */
56
70
  export function scalarZeroValue(type, longAsString) {
57
71
  switch (type) {
@@ -77,11 +91,19 @@ export function scalarZeroValue(type, longAsString) {
77
91
  }
78
92
  }
79
93
  /**
80
- * Returns true for a zero-value. For example, an integer has the zero-value `0`,
81
- * a boolean is `false`, a string is `""`, and bytes is an empty Uint8Array.
94
+ * Returns true if the value is the zero value for the given scalar type: `0`
95
+ * for numeric types, `false` for booleans, `""` for strings, and an empty
96
+ * Uint8Array for bytes.
82
97
  *
83
- * In proto3, zero-values are not written to the wire, unless the field is
84
- * optional or repeated.
98
+ * This is the implicit-presence default check. A singular field with implicit
99
+ * presence is treated as unset, and omitted from the wire, when its value is
100
+ * the zero value. With explicit presence, or in repeated and map fields,
101
+ * presence is structural and this function does not apply.
102
+ *
103
+ * Note that -0 is NOT a zero value for float and double: under implicit
104
+ * presence, +0 is omitted from the wire but -0 is written, following the
105
+ * proto3 specification. As a result this can disagree with scalarEquals, which
106
+ * compares by value and treats -0 as equal to 0.
85
107
  */
86
108
  export function isScalarZeroValue(type, value) {
87
109
  switch (type) {
@@ -91,7 +113,12 @@ export function isScalarZeroValue(type, value) {
91
113
  return value === "";
92
114
  case ScalarType.BYTES:
93
115
  return value instanceof Uint8Array && !value.byteLength;
116
+ case ScalarType.DOUBLE:
117
+ case ScalarType.FLOAT:
118
+ // Object.is distinguishes -0 from 0.
119
+ return Object.is(value, 0);
94
120
  default:
95
- return value == 0; // Loose comparison matches 0n, 0 and "0"
121
+ // Loose comparison matches 0n, 0 and "0".
122
+ return value == 0;
96
123
  }
97
124
  }