@bufbuild/protobuf 1.6.0 → 1.7.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 (153) hide show
  1. package/dist/cjs/binary-encoding.js +2 -3
  2. package/dist/cjs/binary-format.d.ts +17 -0
  3. package/dist/cjs/binary-format.js +1 -1
  4. package/dist/cjs/codegen-info.d.ts +4 -4
  5. package/dist/cjs/codegen-info.js +2 -1
  6. package/dist/cjs/create-descriptor-set.d.ts +7 -1
  7. package/dist/cjs/create-descriptor-set.js +56 -50
  8. package/dist/cjs/create-registry-from-desc.d.ts +2 -2
  9. package/dist/cjs/create-registry-from-desc.js +53 -9
  10. package/dist/cjs/create-registry.d.ts +3 -2
  11. package/dist/cjs/create-registry.js +56 -37
  12. package/dist/cjs/descriptor-set.js +1 -1
  13. package/dist/cjs/enum.js +1 -1
  14. package/dist/cjs/extension-accessor.d.ts +35 -0
  15. package/dist/cjs/extension-accessor.js +108 -0
  16. package/dist/cjs/extension.d.ts +24 -0
  17. package/dist/cjs/extension.js +15 -0
  18. package/dist/cjs/field-list.js +1 -1
  19. package/dist/cjs/field.js +1 -1
  20. package/dist/cjs/google/protobuf/any_pb.js +1 -1
  21. package/dist/cjs/google/protobuf/api_pb.js +1 -1
  22. package/dist/cjs/google/protobuf/compiler/plugin_pb.js +1 -1
  23. package/dist/cjs/google/protobuf/descriptor_pb.js +2 -2
  24. package/dist/cjs/google/protobuf/duration_pb.js +1 -1
  25. package/dist/cjs/google/protobuf/empty_pb.js +1 -1
  26. package/dist/cjs/google/protobuf/field_mask_pb.js +1 -1
  27. package/dist/cjs/google/protobuf/source_context_pb.js +1 -1
  28. package/dist/cjs/google/protobuf/struct_pb.js +2 -2
  29. package/dist/cjs/google/protobuf/timestamp_pb.js +1 -1
  30. package/dist/cjs/google/protobuf/type_pb.js +2 -2
  31. package/dist/cjs/google/protobuf/wrappers_pb.js +1 -1
  32. package/dist/cjs/index.d.ts +3 -1
  33. package/dist/cjs/index.js +7 -2
  34. package/dist/cjs/json-format.d.ts +5 -5
  35. package/dist/cjs/json-format.js +1 -1
  36. package/dist/cjs/message-type.js +1 -1
  37. package/dist/cjs/message.js +1 -1
  38. package/dist/cjs/private/assert.js +1 -1
  39. package/dist/cjs/private/binary-format-common.d.ts +2 -3
  40. package/dist/cjs/private/binary-format-common.js +80 -78
  41. package/dist/cjs/private/binary-format-proto2.js +59 -64
  42. package/dist/cjs/private/binary-format-proto3.js +44 -40
  43. package/dist/cjs/private/enum.js +1 -1
  44. package/dist/cjs/private/extensions.d.ts +34 -0
  45. package/dist/cjs/private/extensions.js +86 -0
  46. package/dist/cjs/private/feature-set.d.ts +6 -8
  47. package/dist/cjs/private/feature-set.js +41 -42
  48. package/dist/cjs/private/field-list.js +1 -1
  49. package/dist/cjs/private/field-wrapper.d.ts +2 -2
  50. package/dist/cjs/private/field-wrapper.js +1 -1
  51. package/dist/cjs/private/field.js +1 -1
  52. package/dist/cjs/private/json-format-common.d.ts +3 -3
  53. package/dist/cjs/private/json-format-common.js +228 -173
  54. package/dist/cjs/private/json-format-proto2.js +3 -2
  55. package/dist/cjs/private/json-format-proto3.js +8 -6
  56. package/dist/cjs/private/message-type.js +1 -1
  57. package/dist/cjs/private/names.d.ts +2 -2
  58. package/dist/cjs/private/names.js +3 -2
  59. package/dist/cjs/private/options-map.js +1 -1
  60. package/dist/cjs/private/proto-runtime.d.ts +6 -0
  61. package/dist/cjs/private/proto-runtime.js +5 -1
  62. package/dist/cjs/private/reify-wkt.js +1 -1
  63. package/dist/cjs/private/scalars.js +1 -1
  64. package/dist/cjs/private/text-format.js +1 -1
  65. package/dist/cjs/private/util-common.js +1 -1
  66. package/dist/cjs/private/util.js +1 -1
  67. package/dist/cjs/proto-base64.js +1 -1
  68. package/dist/cjs/proto-delimited.js +1 -1
  69. package/dist/cjs/proto-double.js +1 -1
  70. package/dist/cjs/proto-int64.js +1 -1
  71. package/dist/cjs/proto2.js +1 -1
  72. package/dist/cjs/proto3.js +1 -1
  73. package/dist/cjs/service-type.js +1 -1
  74. package/dist/cjs/to-plain-message.js +1 -1
  75. package/dist/cjs/type-registry.d.ts +14 -0
  76. package/dist/cjs/type-registry.js +1 -1
  77. package/dist/esm/binary-encoding.js +2 -3
  78. package/dist/esm/binary-format.d.ts +17 -0
  79. package/dist/esm/binary-format.js +1 -1
  80. package/dist/esm/codegen-info.d.ts +4 -4
  81. package/dist/esm/codegen-info.js +2 -1
  82. package/dist/esm/create-descriptor-set.d.ts +7 -1
  83. package/dist/esm/create-descriptor-set.js +57 -51
  84. package/dist/esm/create-registry-from-desc.d.ts +2 -2
  85. package/dist/esm/create-registry-from-desc.js +53 -9
  86. package/dist/esm/create-registry.d.ts +3 -2
  87. package/dist/esm/create-registry.js +56 -37
  88. package/dist/esm/descriptor-set.js +1 -1
  89. package/dist/esm/enum.js +1 -1
  90. package/dist/esm/extension-accessor.d.ts +35 -0
  91. package/dist/esm/extension-accessor.js +101 -0
  92. package/dist/esm/extension.d.ts +24 -0
  93. package/dist/esm/extension.js +14 -0
  94. package/dist/esm/field-list.js +1 -1
  95. package/dist/esm/field.js +1 -1
  96. package/dist/esm/google/protobuf/any_pb.js +1 -1
  97. package/dist/esm/google/protobuf/api_pb.js +1 -1
  98. package/dist/esm/google/protobuf/compiler/plugin_pb.js +1 -1
  99. package/dist/esm/google/protobuf/descriptor_pb.js +2 -2
  100. package/dist/esm/google/protobuf/duration_pb.js +1 -1
  101. package/dist/esm/google/protobuf/empty_pb.js +1 -1
  102. package/dist/esm/google/protobuf/field_mask_pb.js +1 -1
  103. package/dist/esm/google/protobuf/source_context_pb.js +1 -1
  104. package/dist/esm/google/protobuf/struct_pb.js +2 -2
  105. package/dist/esm/google/protobuf/timestamp_pb.js +1 -1
  106. package/dist/esm/google/protobuf/type_pb.js +2 -2
  107. package/dist/esm/google/protobuf/wrappers_pb.js +1 -1
  108. package/dist/esm/index.d.ts +3 -1
  109. package/dist/esm/index.js +2 -1
  110. package/dist/esm/json-format.d.ts +5 -5
  111. package/dist/esm/json-format.js +1 -1
  112. package/dist/esm/message-type.js +1 -1
  113. package/dist/esm/message.js +1 -1
  114. package/dist/esm/private/assert.js +1 -1
  115. package/dist/esm/private/binary-format-common.d.ts +2 -3
  116. package/dist/esm/private/binary-format-common.js +80 -77
  117. package/dist/esm/private/binary-format-proto2.js +59 -64
  118. package/dist/esm/private/binary-format-proto3.js +44 -40
  119. package/dist/esm/private/enum.js +1 -1
  120. package/dist/esm/private/extensions.d.ts +34 -0
  121. package/dist/esm/private/extensions.js +81 -0
  122. package/dist/esm/private/feature-set.d.ts +6 -8
  123. package/dist/esm/private/feature-set.js +40 -41
  124. package/dist/esm/private/field-list.js +1 -1
  125. package/dist/esm/private/field-wrapper.d.ts +2 -2
  126. package/dist/esm/private/field-wrapper.js +1 -1
  127. package/dist/esm/private/field.js +1 -1
  128. package/dist/esm/private/json-format-common.d.ts +3 -3
  129. package/dist/esm/private/json-format-common.js +228 -173
  130. package/dist/esm/private/json-format-proto2.js +3 -2
  131. package/dist/esm/private/json-format-proto3.js +8 -6
  132. package/dist/esm/private/message-type.js +1 -1
  133. package/dist/esm/private/names.d.ts +2 -2
  134. package/dist/esm/private/names.js +3 -2
  135. package/dist/esm/private/options-map.js +1 -1
  136. package/dist/esm/private/proto-runtime.d.ts +6 -0
  137. package/dist/esm/private/proto-runtime.js +5 -1
  138. package/dist/esm/private/reify-wkt.js +1 -1
  139. package/dist/esm/private/scalars.js +1 -1
  140. package/dist/esm/private/text-format.js +1 -1
  141. package/dist/esm/private/util-common.js +1 -1
  142. package/dist/esm/private/util.js +1 -1
  143. package/dist/esm/proto-base64.js +1 -1
  144. package/dist/esm/proto-delimited.js +1 -1
  145. package/dist/esm/proto-double.js +1 -1
  146. package/dist/esm/proto-int64.js +1 -1
  147. package/dist/esm/proto2.js +1 -1
  148. package/dist/esm/proto3.js +1 -1
  149. package/dist/esm/service-type.js +1 -1
  150. package/dist/esm/to-plain-message.js +1 -1
  151. package/dist/esm/type-registry.d.ts +14 -0
  152. package/dist/esm/type-registry.js +1 -1
  153. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2023 Buf Technologies, Inc.
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@ import { LongType, ScalarType } from "../field.js";
16
16
  import { assert, assertFloat32, assertInt32, assertUInt32 } from "./assert.js";
17
17
  import { protoInt64 } from "../proto-int64.js";
18
18
  import { protoBase64 } from "../proto-base64.js";
19
+ import { createExtensionContainer } from "./extensions.js";
20
+ import { getExtension, hasExtension, setExtension, } from "../extension-accessor.js";
19
21
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
20
22
  // Default options for parsing JSON.
21
23
  const jsonReadDefaults = {
@@ -34,171 +36,52 @@ function makeReadOptions(options) {
34
36
  function makeWriteOptions(options) {
35
37
  return options ? Object.assign(Object.assign({}, jsonWriteDefaults), options) : jsonWriteDefaults;
36
38
  }
37
- export function makeJsonFormatCommon(makeWriteField) {
39
+ export function makeJsonFormatCommon(nullAsZeroValue, makeWriteField) {
38
40
  const writeField = makeWriteField(writeEnum, writeScalar);
39
41
  return {
40
42
  makeReadOptions,
41
43
  makeWriteOptions,
42
44
  readMessage(type, json, options, message) {
43
45
  if (json == null || Array.isArray(json) || typeof json != "object") {
44
- throw new Error(`cannot decode message ${type.typeName} from JSON: ${this.debug(json)}`);
46
+ throw new Error(`cannot decode message ${type.typeName} from JSON: ${debugJsonValue(json)}`);
45
47
  }
46
48
  message = message !== null && message !== void 0 ? message : new type();
47
- const oneofSeen = {};
49
+ const oneofSeen = new Map();
50
+ const registry = options.typeRegistry;
48
51
  for (const [jsonKey, jsonValue] of Object.entries(json)) {
49
52
  const field = type.fields.findJsonName(jsonKey);
50
- if (!field) {
51
- if (!options.ignoreUnknownFields) {
52
- throw new Error(`cannot decode message ${type.typeName} from JSON: key "${jsonKey}" is unknown`);
53
- }
54
- continue;
55
- }
56
- let localName = field.localName;
57
- let target = message;
58
- if (field.oneof) {
59
- if (jsonValue === null && field.kind == "scalar") {
60
- // see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
61
- continue;
62
- }
63
- const seen = oneofSeen[field.oneof.localName];
64
- if (seen) {
65
- throw new Error(`cannot decode message ${type.typeName} from JSON: multiple keys for oneof "${field.oneof.name}" present: "${seen}", "${jsonKey}"`);
66
- }
67
- oneofSeen[field.oneof.localName] = jsonKey;
68
- target = target[field.oneof.localName] = { case: localName };
69
- localName = "value";
70
- }
71
- if (field.repeated) {
72
- if (jsonValue === null) {
73
- continue;
74
- }
75
- if (!Array.isArray(jsonValue)) {
76
- throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonValue)}`);
77
- }
78
- const targetArray = target[localName];
79
- for (const jsonItem of jsonValue) {
80
- if (jsonItem === null) {
81
- throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonItem)}`);
53
+ if (field) {
54
+ if (field.oneof) {
55
+ if (jsonValue === null && field.kind == "scalar") {
56
+ // see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
57
+ continue;
82
58
  }
83
- let val;
84
- // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- "map" is invalid for repeated fields
85
- switch (field.kind) {
86
- case "message":
87
- val = field.T.fromJson(jsonItem, options);
88
- break;
89
- case "enum":
90
- val = readEnum(field.T, jsonItem, options.ignoreUnknownFields);
91
- if (val === undefined)
92
- continue;
93
- break;
94
- case "scalar":
95
- try {
96
- val = readScalar(field.T, jsonItem, field.L);
97
- }
98
- catch (e) {
99
- let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonItem)}`;
100
- if (e instanceof Error && e.message.length > 0) {
101
- m += `: ${e.message}`;
102
- }
103
- throw new Error(m);
104
- }
105
- break;
59
+ const seen = oneofSeen.get(field.oneof);
60
+ if (seen !== undefined) {
61
+ throw new Error(`cannot decode message ${type.typeName} from JSON: multiple keys for oneof "${field.oneof.name}" present: "${seen}", "${jsonKey}"`);
106
62
  }
107
- targetArray.push(val);
63
+ oneofSeen.set(field.oneof, jsonKey);
108
64
  }
65
+ readField(message, jsonValue, field, options, type, nullAsZeroValue);
109
66
  }
110
- else if (field.kind == "map") {
111
- if (jsonValue === null) {
112
- continue;
113
- }
114
- if (Array.isArray(jsonValue) || typeof jsonValue != "object") {
115
- throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonValue)}`);
116
- }
117
- const targetMap = target[localName];
118
- for (const [jsonMapKey, jsonMapValue] of Object.entries(jsonValue)) {
119
- if (jsonMapValue === null) {
120
- throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: map value null`);
121
- }
122
- let val;
123
- switch (field.V.kind) {
124
- case "message":
125
- val = field.V.T.fromJson(jsonMapValue, options);
126
- break;
127
- case "enum":
128
- val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields);
129
- if (val === undefined)
130
- continue;
131
- break;
132
- case "scalar":
133
- try {
134
- val = readScalar(field.V.T, jsonMapValue, LongType.BIGINT);
135
- }
136
- catch (e) {
137
- let m = `cannot decode map value for field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonValue)}`;
138
- if (e instanceof Error && e.message.length > 0) {
139
- m += `: ${e.message}`;
140
- }
141
- throw new Error(m);
142
- }
143
- break;
144
- }
145
- try {
146
- targetMap[readScalar(field.K, field.K == ScalarType.BOOL
147
- ? jsonMapKey == "true"
148
- ? true
149
- : jsonMapKey == "false"
150
- ? false
151
- : jsonMapKey
152
- : jsonMapKey, LongType.BIGINT).toString()] = val;
153
- }
154
- catch (e) {
155
- let m = `cannot decode map key for field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonValue)}`;
156
- if (e instanceof Error && e.message.length > 0) {
157
- m += `: ${e.message}`;
158
- }
159
- throw new Error(m);
67
+ else {
68
+ let found = false;
69
+ if ((registry === null || registry === void 0 ? void 0 : registry.findExtension) &&
70
+ jsonKey.startsWith("[") &&
71
+ jsonKey.endsWith("]")) {
72
+ const ext = registry.findExtension(jsonKey.substring(1, jsonKey.length - 1));
73
+ if (ext && ext.extendee.typeName == type.typeName) {
74
+ found = true;
75
+ const [container, get] = createExtensionContainer(ext);
76
+ readField(container, jsonValue, ext.field, options, ext, true);
77
+ // We pass on the options as BinaryReadOptions/BinaryWriteOptions,
78
+ // so that users can bring their own binary reader and writer factories
79
+ // if necessary.
80
+ setExtension(message, ext, get(), options);
160
81
  }
161
82
  }
162
- }
163
- else {
164
- switch (field.kind) {
165
- case "message":
166
- const messageType = field.T;
167
- if (jsonValue === null &&
168
- messageType.typeName != "google.protobuf.Value") {
169
- if (field.oneof) {
170
- throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: null is invalid for oneof field "${jsonKey}"`);
171
- }
172
- continue;
173
- }
174
- if (target[localName] instanceof Message) {
175
- target[localName].fromJson(jsonValue, options);
176
- }
177
- else {
178
- target[localName] = messageType.fromJson(jsonValue, options);
179
- if (messageType.fieldWrapper && !field.oneof) {
180
- target[localName] = messageType.fieldWrapper.unwrapField(target[localName]);
181
- }
182
- }
183
- break;
184
- case "enum":
185
- const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields);
186
- if (enumValue !== undefined) {
187
- target[localName] = enumValue;
188
- }
189
- break;
190
- case "scalar":
191
- try {
192
- target[localName] = readScalar(field.T, jsonValue, field.L);
193
- }
194
- catch (e) {
195
- let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${this.debug(jsonValue)}`;
196
- if (e instanceof Error && e.message.length > 0) {
197
- m += `: ${e.message}`;
198
- }
199
- throw new Error(m);
200
- }
201
- break;
83
+ if (!found && !options.ignoreUnknownFields) {
84
+ throw new Error(`cannot decode message ${type.typeName} from JSON: key "${jsonKey}" is unknown`);
202
85
  }
203
86
  }
204
87
  }
@@ -231,6 +114,21 @@ export function makeJsonFormatCommon(makeWriteField) {
231
114
  jsonValue;
232
115
  }
233
116
  }
117
+ const registry = options.typeRegistry;
118
+ if (registry === null || registry === void 0 ? void 0 : registry.findExtensionFor) {
119
+ for (const uf of type.runtime.bin.listUnknownFields(message)) {
120
+ const ext = registry.findExtensionFor(type.typeName, uf.no);
121
+ if (ext && hasExtension(message, ext)) {
122
+ // We pass on the options as BinaryReadOptions, so that users can bring their own
123
+ // binary reader factory if necessary.
124
+ const value = getExtension(message, ext, options);
125
+ const jsonValue = writeField(ext.field, value, options);
126
+ if (jsonValue !== undefined) {
127
+ json[ext.field.jsonName] = jsonValue;
128
+ }
129
+ }
130
+ }
131
+ }
234
132
  }
235
133
  catch (e) {
236
134
  const m = field
@@ -241,7 +139,7 @@ export function makeJsonFormatCommon(makeWriteField) {
241
139
  }
242
140
  return json;
243
141
  },
244
- readScalar,
142
+ readScalar: (type, json, longType) => readScalar(type, json, longType, nullAsZeroValue), // eslint-disable-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return
245
143
  writeScalar,
246
144
  debug: debugJsonValue,
247
145
  };
@@ -259,9 +157,162 @@ function debugJsonValue(json) {
259
157
  return String(json);
260
158
  }
261
159
  }
160
+ // Read a JSON value for a field.
161
+ // The "type" argument is only used to provide context in errors.
162
+ // The argument "nullAsZeroValue" applies to singular fields (not repeated, map,
163
+ // or oneof). If the argument is `true`, JSON null is parsed as the fields
164
+ // zero-value. If the argument is false, JSON null sets the field to `undefined`.
165
+ function readField(target, // eslint-disable-line @typescript-eslint/no-explicit-any -- `any` is the best choice for dynamic access
166
+ jsonValue, field, options, type, nullAsZeroValue) {
167
+ let localName = field.localName;
168
+ if (field.oneof) {
169
+ if (jsonValue === null && field.kind == "scalar") {
170
+ // see conformance test Required.Proto3.JsonInput.OneofFieldNull{First,Second}
171
+ return;
172
+ }
173
+ target = target[field.oneof.localName] = { case: localName };
174
+ localName = "value";
175
+ }
176
+ if (field.repeated) {
177
+ if (jsonValue === null) {
178
+ return;
179
+ }
180
+ if (!Array.isArray(jsonValue)) {
181
+ throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`);
182
+ }
183
+ const targetArray = target[localName];
184
+ for (const jsonItem of jsonValue) {
185
+ if (jsonItem === null) {
186
+ throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonItem)}`);
187
+ }
188
+ let val;
189
+ // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check -- "map" is invalid for repeated fields
190
+ switch (field.kind) {
191
+ case "message":
192
+ val = field.T.fromJson(jsonItem, options);
193
+ break;
194
+ case "enum":
195
+ val = readEnum(field.T, jsonItem, options.ignoreUnknownFields, true);
196
+ if (val === undefined)
197
+ continue;
198
+ break;
199
+ case "scalar":
200
+ try {
201
+ val = readScalar(field.T, jsonItem, field.L, true);
202
+ }
203
+ catch (e) {
204
+ let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonItem)}`;
205
+ if (e instanceof Error && e.message.length > 0) {
206
+ m += `: ${e.message}`;
207
+ }
208
+ throw new Error(m);
209
+ }
210
+ break;
211
+ }
212
+ targetArray.push(val);
213
+ }
214
+ }
215
+ else if (field.kind == "map") {
216
+ if (jsonValue === null) {
217
+ return;
218
+ }
219
+ if (typeof jsonValue != "object" || Array.isArray(jsonValue)) {
220
+ throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`);
221
+ }
222
+ const targetMap = target[localName];
223
+ for (const [jsonMapKey, jsonMapValue] of Object.entries(jsonValue)) {
224
+ if (jsonMapValue === null) {
225
+ throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: map value null`);
226
+ }
227
+ let val;
228
+ switch (field.V.kind) {
229
+ case "message":
230
+ val = field.V.T.fromJson(jsonMapValue, options);
231
+ break;
232
+ case "enum":
233
+ val = readEnum(field.V.T, jsonMapValue, options.ignoreUnknownFields, true);
234
+ if (val === undefined)
235
+ continue;
236
+ break;
237
+ case "scalar":
238
+ try {
239
+ val = readScalar(field.V.T, jsonMapValue, LongType.BIGINT, true);
240
+ }
241
+ catch (e) {
242
+ let m = `cannot decode map value for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
243
+ if (e instanceof Error && e.message.length > 0) {
244
+ m += `: ${e.message}`;
245
+ }
246
+ throw new Error(m);
247
+ }
248
+ break;
249
+ }
250
+ try {
251
+ targetMap[readScalar(field.K, field.K == ScalarType.BOOL
252
+ ? jsonMapKey == "true"
253
+ ? true
254
+ : jsonMapKey == "false"
255
+ ? false
256
+ : jsonMapKey
257
+ : jsonMapKey, LongType.BIGINT, true).toString()] = val;
258
+ }
259
+ catch (e) {
260
+ let m = `cannot decode map key for field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
261
+ if (e instanceof Error && e.message.length > 0) {
262
+ m += `: ${e.message}`;
263
+ }
264
+ throw new Error(m);
265
+ }
266
+ }
267
+ }
268
+ else {
269
+ switch (field.kind) {
270
+ case "message":
271
+ const messageType = field.T;
272
+ if (jsonValue === null &&
273
+ messageType.typeName != "google.protobuf.Value") {
274
+ if (field.oneof) {
275
+ throw new Error(`cannot decode field ${type.typeName}.${field.name} from JSON: null is invalid for oneof field`);
276
+ }
277
+ return;
278
+ }
279
+ if (target[localName] instanceof Message) {
280
+ target[localName].fromJson(jsonValue, options);
281
+ }
282
+ else {
283
+ target[localName] = messageType.fromJson(jsonValue, options);
284
+ if (messageType.fieldWrapper && !field.oneof) {
285
+ target[localName] = messageType.fieldWrapper.unwrapField(target[localName]);
286
+ }
287
+ }
288
+ break;
289
+ case "enum":
290
+ const enumValue = readEnum(field.T, jsonValue, options.ignoreUnknownFields, nullAsZeroValue);
291
+ if (enumValue !== undefined) {
292
+ target[localName] = enumValue;
293
+ }
294
+ break;
295
+ case "scalar":
296
+ try {
297
+ target[localName] = readScalar(field.T, jsonValue, field.L, nullAsZeroValue);
298
+ }
299
+ catch (e) {
300
+ let m = `cannot decode field ${type.typeName}.${field.name} from JSON: ${debugJsonValue(jsonValue)}`;
301
+ if (e instanceof Error && e.message.length > 0) {
302
+ m += `: ${e.message}`;
303
+ }
304
+ throw new Error(m);
305
+ }
306
+ break;
307
+ }
308
+ }
309
+ }
262
310
  // May throw an error. If the error message is non-blank, it should be shown.
263
311
  // It is up to the caller to provide context.
264
- function readScalar(type, json, longType) {
312
+ // If the argument "nullAsZeroValue" is `true`, this function returns the zero-value
313
+ // for JSON null. If the argument is `false`, this function returns `undefined`
314
+ // for JSON null.
315
+ function readScalar(type, json, longType, nullAsZeroValue) {
265
316
  // every valid case in the switch below returns, and every fall
266
317
  // through is regarded as a failure.
267
318
  switch (type) {
@@ -270,7 +321,7 @@ function readScalar(type, json, longType) {
270
321
  case ScalarType.DOUBLE:
271
322
  case ScalarType.FLOAT:
272
323
  if (json === null)
273
- return 0.0;
324
+ return nullAsZeroValue ? 0.0 : undefined;
274
325
  if (json === "NaN")
275
326
  return Number.NaN;
276
327
  if (json === "Infinity")
@@ -307,7 +358,7 @@ function readScalar(type, json, longType) {
307
358
  case ScalarType.SINT32:
308
359
  case ScalarType.UINT32:
309
360
  if (json === null)
310
- return 0;
361
+ return nullAsZeroValue ? 0 : undefined;
311
362
  let int32;
312
363
  if (typeof json == "number")
313
364
  int32 = json;
@@ -327,7 +378,7 @@ function readScalar(type, json, longType) {
327
378
  case ScalarType.SFIXED64:
328
379
  case ScalarType.SINT64:
329
380
  if (json === null)
330
- return protoInt64.zero;
381
+ return nullAsZeroValue ? protoInt64.zero : undefined;
331
382
  if (typeof json != "number" && typeof json != "string")
332
383
  break;
333
384
  const long = protoInt64.parse(json);
@@ -336,7 +387,7 @@ function readScalar(type, json, longType) {
336
387
  case ScalarType.FIXED64:
337
388
  case ScalarType.UINT64:
338
389
  if (json === null)
339
- return protoInt64.zero;
390
+ return nullAsZeroValue ? protoInt64.zero : undefined;
340
391
  if (typeof json != "number" && typeof json != "string")
341
392
  break;
342
393
  const uLong = protoInt64.uParse(json);
@@ -345,14 +396,14 @@ function readScalar(type, json, longType) {
345
396
  // bool:
346
397
  case ScalarType.BOOL:
347
398
  if (json === null)
348
- return false;
399
+ return nullAsZeroValue ? false : undefined;
349
400
  if (typeof json !== "boolean")
350
401
  break;
351
402
  return json;
352
403
  // string:
353
404
  case ScalarType.STRING:
354
405
  if (json === null)
355
- return "";
406
+ return nullAsZeroValue ? "" : undefined;
356
407
  if (typeof json !== "string") {
357
408
  break;
358
409
  }
@@ -368,7 +419,9 @@ function readScalar(type, json, longType) {
368
419
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
369
420
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
370
421
  case ScalarType.BYTES:
371
- if (json === null || json === "")
422
+ if (json === null)
423
+ return nullAsZeroValue ? new Uint8Array(0) : undefined;
424
+ if (json === "")
372
425
  return new Uint8Array(0);
373
426
  if (typeof json !== "string")
374
427
  break;
@@ -376,10 +429,14 @@ function readScalar(type, json, longType) {
376
429
  }
377
430
  throw new Error();
378
431
  }
379
- function readEnum(type, json, ignoreUnknownFields) {
432
+ // If the argument "nullAsZeroValue" is `true`, this function returns the first
433
+ // enum value for JSON null. If the argument is `false`, this function returns
434
+ // `undefined`.
435
+ function readEnum(type, json, ignoreUnknownFields, nullAsZeroValue) {
380
436
  if (json === null) {
381
- // proto3 requires 0 to be default value for all enums
382
- return 0;
437
+ // proto3 requires 0 to be default value for all enums (which is required to be the first value).
438
+ // proto2 uses the first value (at least with protoc-gen-go).
439
+ return nullAsZeroValue ? type.values[0].no : undefined;
383
440
  }
384
441
  // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
385
442
  switch (typeof json) {
@@ -398,12 +455,12 @@ function readEnum(type, json, ignoreUnknownFields) {
398
455
  }
399
456
  throw new Error(`cannot decode enum ${type.typeName} from JSON: ${debugJsonValue(json)}`);
400
457
  }
401
- function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
458
+ function writeEnum(type, value, emitZeroValue, enumAsInteger) {
402
459
  var _a;
403
460
  if (value === undefined) {
404
461
  return value;
405
462
  }
406
- if (value === 0 && !emitIntrinsicDefault) {
463
+ if (!emitZeroValue && type.values[0].no === value) {
407
464
  // proto3 requires 0 to be default value for all enums
408
465
  return undefined;
409
466
  }
@@ -416,7 +473,7 @@ function writeEnum(type, value, emitIntrinsicDefault, enumAsInteger) {
416
473
  const val = type.findNumber(value);
417
474
  return (_a = val === null || val === void 0 ? void 0 : val.name) !== null && _a !== void 0 ? _a : value; // if we don't know the enum value, just return the number
418
475
  }
419
- function writeScalar(type, value, emitIntrinsicDefault) {
476
+ function writeScalar(type, value, emitZeroValue) {
420
477
  if (value === undefined) {
421
478
  return undefined;
422
479
  }
@@ -428,7 +485,7 @@ function writeScalar(type, value, emitIntrinsicDefault) {
428
485
  case ScalarType.FIXED32:
429
486
  case ScalarType.UINT32:
430
487
  assert(typeof value == "number");
431
- return value != 0 || emitIntrinsicDefault ? value : undefined;
488
+ return value != 0 || emitZeroValue ? value : undefined;
432
489
  // float, double: JSON value will be a number or one of the special string values "NaN", "Infinity", and "-Infinity".
433
490
  // Either numbers or strings are accepted. Exponent notation is also accepted.
434
491
  case ScalarType.FLOAT:
@@ -441,15 +498,15 @@ function writeScalar(type, value, emitIntrinsicDefault) {
441
498
  return "Infinity";
442
499
  if (value === Number.NEGATIVE_INFINITY)
443
500
  return "-Infinity";
444
- return value !== 0 || emitIntrinsicDefault ? value : undefined;
501
+ return value !== 0 || emitZeroValue ? value : undefined;
445
502
  // string:
446
503
  case ScalarType.STRING:
447
504
  assert(typeof value == "string");
448
- return value.length > 0 || emitIntrinsicDefault ? value : undefined;
505
+ return value.length > 0 || emitZeroValue ? value : undefined;
449
506
  // bool:
450
507
  case ScalarType.BOOL:
451
508
  assert(typeof value == "boolean");
452
- return value || emitIntrinsicDefault ? value : undefined;
509
+ return value || emitZeroValue ? value : undefined;
453
510
  // JSON value will be a decimal string. Either numbers or strings are accepted.
454
511
  case ScalarType.UINT64:
455
512
  case ScalarType.FIXED64:
@@ -462,14 +519,12 @@ function writeScalar(type, value, emitIntrinsicDefault) {
462
519
  // We use implicit conversion with `value != 0` to catch both 0n and "0"
463
520
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
464
521
  // @ts-ignore
465
- return emitIntrinsicDefault || value != 0
466
- ? value.toString(10)
467
- : undefined;
522
+ return emitZeroValue || value != 0 ? value.toString(10) : undefined;
468
523
  // bytes: JSON value will be the data encoded as a string using standard base64 encoding with paddings.
469
524
  // Either standard or URL-safe base64 encoding with/without paddings are accepted.
470
525
  case ScalarType.BYTES:
471
526
  assert(value instanceof Uint8Array);
472
- return emitIntrinsicDefault || value.byteLength > 0
527
+ return emitZeroValue || value.byteLength > 0
473
528
  ? protoBase64.enc(value)
474
529
  : undefined;
475
530
  }
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2023 Buf Technologies, Inc.
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@ import { assert } from "./assert.js";
16
16
  import { makeJsonFormatCommon } from "./json-format-common.js";
17
17
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
18
18
  export function makeJsonFormatProto2() {
19
- return makeJsonFormatCommon((writeEnum, writeScalar) => {
19
+ // TODO field presence: merge this function with proto3. the reading side already switches behavior on the nullAsZeroValue argument.
20
+ return makeJsonFormatCommon(false, (writeEnum, writeScalar) => {
20
21
  return function writeField(field, value, options) {
21
22
  if (field.kind == "map") {
22
23
  const jsonObj = {};
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2023 Buf Technologies, Inc.
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@ import { assert } from "./assert.js";
16
16
  import { makeJsonFormatCommon } from "./json-format-common.js";
17
17
  /* eslint-disable no-case-declarations, @typescript-eslint/restrict-plus-operands,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-argument */
18
18
  export function makeJsonFormatProto3() {
19
- return makeJsonFormatCommon((writeEnum, writeScalar) => {
19
+ // TODO field presence: merge this function with proto2. the reading side already switches behavior on the nullAsZeroValue argument.
20
+ return makeJsonFormatCommon(true, (writeEnum, writeScalar) => {
20
21
  return function writeField(field, value, options) {
21
22
  if (field.kind == "map") {
22
23
  const jsonObj = {};
@@ -63,7 +64,7 @@ export function makeJsonFormatProto3() {
63
64
  break;
64
65
  case "message":
65
66
  for (let i = 0; i < value.length; i++) {
66
- jsonArr.push(wrapField(field.T, value[i]).toJson(options));
67
+ jsonArr.push(value[i].toJson(options));
67
68
  }
68
69
  break;
69
70
  }
@@ -72,15 +73,16 @@ export function makeJsonFormatProto3() {
72
73
  : undefined;
73
74
  }
74
75
  else {
76
+ if (value === undefined) {
77
+ return undefined;
78
+ }
75
79
  switch (field.kind) {
76
80
  case "scalar":
77
81
  return writeScalar(field.T, value, !!field.oneof || field.opt || options.emitDefaultValues);
78
82
  case "enum":
79
83
  return writeEnum(field.T, value, !!field.oneof || field.opt || options.emitDefaultValues, options.enumAsInteger);
80
84
  case "message":
81
- return value !== undefined
82
- ? wrapField(field.T, value).toJson(options)
83
- : undefined;
85
+ return wrapField(field.T, value).toJson(options);
84
86
  }
85
87
  }
86
88
  };
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2023 Buf Technologies, Inc.
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- import type { DescEnum, DescEnumValue, DescField, DescMessage, DescService } from "../descriptor-set.js";
1
+ import type { DescEnum, DescEnumValue, DescExtension, DescField, DescMessage, DescService } from "../descriptor-set.js";
2
2
  import type { DescMethod, DescOneof } from "../descriptor-set.js";
3
3
  /**
4
4
  * Returns the name of a protobuf element in generated code.
@@ -8,7 +8,7 @@ import type { DescMethod, DescOneof } from "../descriptor-set.js";
8
8
  * the type name. For nested messages and enumerations, the names are joined
9
9
  * with an underscore. For methods, the first character is made lowercase.
10
10
  */
11
- export declare function localName(desc: DescEnum | DescEnumValue | DescMessage | DescOneof | DescField | DescService | DescMethod): string;
11
+ export declare function localName(desc: DescEnum | DescEnumValue | DescMessage | DescExtension | DescOneof | DescField | DescService | DescMethod): string;
12
12
  /**
13
13
  * Returns the name of a field in generated code.
14
14
  */
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2023 Buf Technologies, Inc.
1
+ // Copyright 2021-2024 Buf Technologies, Inc.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -27,7 +27,8 @@ export function localName(desc) {
27
27
  return localOneofName(desc.name);
28
28
  case "enum":
29
29
  case "message":
30
- case "service": {
30
+ case "service":
31
+ case "extension": {
31
32
  const pkg = desc.file.proto.package;
32
33
  const offset = pkg === undefined ? 0 : pkg.length + 1;
33
34
  const name = desc.typeName.substring(offset).replace(/\./g, "_");