ts-proto 1.144.1 → 1.146.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.
package/README.markdown CHANGED
@@ -461,7 +461,7 @@ Generated code will be placed in the Gradle build directory.
461
461
 
462
462
  - With `--ts_proto_opt=useJsonWireFormat=true`, the generated code will reflect the JSON representation of Protobuf messages.
463
463
 
464
- Requires `onlyTypes=true`. Implies `useDate=string` and `stringEnums=true`. This option is to generate types that can be directly used with marshalling/unmarshalling Protobuf messages serialized as JSON.
464
+ Requires `onlyTypes=true`. Implies `useDate=string` and `stringEnums=true`. This option is to generate types that can be directly used with marshalling/unmarshalling Protobuf messages serialized as JSON.
465
465
  You may also want to set `useOptionals=all`, as gRPC gateways are not required to send default value for scalar values.
466
466
 
467
467
  - With `--ts_proto_opt=useNumericEnumForJson=true`, the JSON converter (`toJSON`) will encode enum values as int, rather than a string literal.
@@ -502,6 +502,11 @@ Generated code will be placed in the Gradle build directory.
502
502
 
503
503
  by default this is enabled which would generate a type of `Box_Element_Image_Alignment`. By disabling this option the type that is generated would be `BoxElementImageAlignment`.
504
504
 
505
+ - With `--ts_proto_opt=outputExtensions=true`, the generated code will include proto2 extensions
506
+
507
+ Extension encode/decode methods are compliant with the `outputEncodeMethods` option, and if `unknownFields=true`,
508
+ the `setExtension` and `getExtension` methods will be created for extendable messages, also compliant with `outputEncodeMethods` (setExtension = encode, getExtension = decode).
509
+
505
510
  ### NestJS Support
506
511
 
507
512
  We have a great way of working together with [nestjs](https://docs.nestjs.com/microservices/grpc). `ts-proto` generates `interfaces` and `decorators` for you controller, client. For more information see the [nestjs readme](NESTJS.markdown).
package/build/main.js CHANGED
@@ -92,14 +92,26 @@ function generateFile(ctx, fileDesc) {
92
92
  if (options.outputTypeRegistry) {
93
93
  staticMembers.push((0, ts_poet_1.code) `$type: '${fullTypeName}' as const`);
94
94
  }
95
+ if (options.outputExtensions) {
96
+ for (const extension of message.extension) {
97
+ const { name, type, extensionInfo } = generateExtension(ctx, message, extension);
98
+ staticMembers.push((0, ts_poet_1.code) `${name}: <${ctx.utils.Extension}<${type}>> ${extensionInfo}`);
99
+ }
100
+ }
95
101
  if (options.outputEncodeMethods) {
96
102
  if (options.outputEncodeMethods === true ||
97
103
  options.outputEncodeMethods === "encode-only" ||
98
104
  options.outputEncodeMethods === "encode-no-creation") {
99
105
  staticMembers.push(generateEncode(ctx, fullName, message));
106
+ if (options.outputExtensions && options.unknownFields && message.extensionRange.length) {
107
+ staticMembers.push(generateSetExtension(ctx, fullName));
108
+ }
100
109
  }
101
110
  if (options.outputEncodeMethods === true || options.outputEncodeMethods === "decode-only") {
102
111
  staticMembers.push(generateDecode(ctx, fullName, message));
112
+ if (options.outputExtensions && options.unknownFields && message.extensionRange.length) {
113
+ staticMembers.push(generateGetExtension(ctx, fullName));
114
+ }
103
115
  }
104
116
  }
105
117
  if (options.useAsyncIterable) {
@@ -144,6 +156,12 @@ function generateFile(ctx, fileDesc) {
144
156
  }
145
157
  }, options);
146
158
  }
159
+ if (options.outputExtensions) {
160
+ for (const extension of fileDesc.extension) {
161
+ const { name, type, extensionInfo } = generateExtension(ctx, undefined, extension);
162
+ chunks.push((0, ts_poet_1.code) `export const ${name}: ${ctx.utils.Extension}<${type}> = ${extensionInfo};`);
163
+ }
164
+ }
147
165
  if (options.nestJs) {
148
166
  if (fileDesc.messageType.find((message) => message.field.find(types_1.isStructType))) {
149
167
  chunks.push(makeProtobufStructWrapper(options));
@@ -256,6 +274,7 @@ function makeUtils(options) {
256
274
  ...makeComparisonUtils(),
257
275
  ...makeNiceGrpcServerStreamingMethodResult(),
258
276
  ...makeGrpcWebErrorClass(bytes),
277
+ ...makeExtensionClass(options),
259
278
  };
260
279
  }
261
280
  exports.makeUtils = makeUtils;
@@ -550,6 +569,22 @@ function makeGrpcWebErrorClass(bytes) {
550
569
  `);
551
570
  return { GrpcWebError };
552
571
  }
572
+ function makeExtensionClass(options) {
573
+ const Reader = (0, utils_1.impFile)(options, "Reader@protobufjs/minimal");
574
+ const Writer = (0, utils_1.impFile)(options, "Writer@protobufjs/minimal");
575
+ const Extension = (0, ts_poet_1.conditionalOutput)("Extension", (0, ts_poet_1.code) `
576
+ export interface Extension <T> {
577
+ number: number;
578
+ tag: number;
579
+ singularTag?: number;
580
+ encode?: (message: T) => Uint8Array[];
581
+ decode?: (tag: number, input: Uint8Array[]) => T;
582
+ repeated: boolean;
583
+ packed: boolean;
584
+ }
585
+ `);
586
+ return { Extension };
587
+ }
553
588
  // Create the interface with properties
554
589
  function generateInterfaceDeclaration(ctx, fullName, messageDesc, sourceInfo, fullTypeName) {
555
590
  var _a;
@@ -661,6 +696,71 @@ function generateBaseInstanceFactory(ctx, fullName, messageDesc, fullTypeName) {
661
696
  }
662
697
  `;
663
698
  }
699
+ function getDecodeReadSnippet(ctx, field) {
700
+ const { options, utils } = ctx;
701
+ let readSnippet;
702
+ if ((0, types_1.isPrimitive)(field)) {
703
+ readSnippet = (0, ts_poet_1.code) `reader.${(0, types_1.toReaderCall)(field)}()`;
704
+ if ((0, types_1.isBytes)(field)) {
705
+ if (options.env === options_1.EnvOption.NODE) {
706
+ readSnippet = (0, ts_poet_1.code) `${readSnippet} as Buffer`;
707
+ }
708
+ }
709
+ else if ((0, types_1.basicLongWireType)(field.type) !== undefined) {
710
+ if (options.forceLong === options_1.LongOption.LONG) {
711
+ readSnippet = (0, ts_poet_1.code) `${readSnippet} as Long`;
712
+ }
713
+ else if (options.forceLong === options_1.LongOption.STRING) {
714
+ readSnippet = (0, ts_poet_1.code) `${utils.longToString}(${readSnippet} as Long)`;
715
+ }
716
+ else if (options.forceLong === options_1.LongOption.BIGINT) {
717
+ readSnippet = (0, ts_poet_1.code) `${utils.longToBigint}(${readSnippet} as Long)`;
718
+ }
719
+ else {
720
+ readSnippet = (0, ts_poet_1.code) `${utils.longToNumber}(${readSnippet} as Long)`;
721
+ }
722
+ }
723
+ else if ((0, types_1.isEnum)(field)) {
724
+ if (options.stringEnums) {
725
+ const fromJson = (0, types_1.getEnumMethod)(ctx, field.typeName, "FromJSON");
726
+ readSnippet = (0, ts_poet_1.code) `${fromJson}(${readSnippet})`;
727
+ }
728
+ else {
729
+ readSnippet = (0, ts_poet_1.code) `${readSnippet} as any`;
730
+ }
731
+ }
732
+ }
733
+ else if ((0, types_1.isValueType)(ctx, field)) {
734
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
735
+ const unwrap = (decodedValue) => {
736
+ if ((0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isAnyValueType)(field) || (0, types_1.isFieldMaskType)(field)) {
737
+ return (0, ts_poet_1.code) `${type}.unwrap(${decodedValue})`;
738
+ }
739
+ return (0, ts_poet_1.code) `${decodedValue}.value`;
740
+ };
741
+ const decoder = (0, ts_poet_1.code) `${type}.decode(reader, reader.uint32())`;
742
+ readSnippet = (0, ts_poet_1.code) `${unwrap(decoder)}`;
743
+ }
744
+ else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
745
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
746
+ readSnippet = (0, ts_poet_1.code) `${utils.fromTimestamp}(${type}.decode(reader, reader.uint32()))`;
747
+ }
748
+ else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
749
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
750
+ readSnippet = (0, ts_poet_1.code) `${utils.fromProtoObjectId}(${type}.decode(reader, reader.uint32()))`;
751
+ }
752
+ else if ((0, types_1.isMessage)(field)) {
753
+ const type = (0, types_1.basicTypeName)(ctx, field);
754
+ if (field.type == ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_GROUP)
755
+ readSnippet = (0, ts_poet_1.code) `${type}.decode(reader)`;
756
+ else
757
+ readSnippet = (0, ts_poet_1.code) `${type}.decode(reader, reader.uint32())`;
758
+ }
759
+ else {
760
+ throw new Error(`Unhandled field ${field}`);
761
+ }
762
+ return readSnippet;
763
+ }
664
764
  /** Creates a function to decode a message by loop overing the tags. */
665
765
  function generateDecode(ctx, fullName, messageDesc) {
666
766
  const { options, utils, typeMap } = ctx;
@@ -696,67 +796,7 @@ function generateDecode(ctx, fullName, messageDesc) {
696
796
  break;
697
797
  `;
698
798
  // get a generic 'reader.doSomething' bit that is specific to the basic type
699
- let readSnippet;
700
- if ((0, types_1.isPrimitive)(field)) {
701
- readSnippet = (0, ts_poet_1.code) `reader.${(0, types_1.toReaderCall)(field)}()`;
702
- if ((0, types_1.isBytes)(field)) {
703
- if (options.env === options_1.EnvOption.NODE) {
704
- readSnippet = (0, ts_poet_1.code) `${readSnippet} as Buffer`;
705
- }
706
- }
707
- else if ((0, types_1.basicLongWireType)(field.type) !== undefined) {
708
- if (options.forceLong === options_1.LongOption.LONG) {
709
- readSnippet = (0, ts_poet_1.code) `${readSnippet} as Long`;
710
- }
711
- else if (options.forceLong === options_1.LongOption.STRING) {
712
- readSnippet = (0, ts_poet_1.code) `${utils.longToString}(${readSnippet} as Long)`;
713
- }
714
- else if (options.forceLong === options_1.LongOption.BIGINT) {
715
- readSnippet = (0, ts_poet_1.code) `${utils.longToBigint}(${readSnippet} as Long)`;
716
- }
717
- else {
718
- readSnippet = (0, ts_poet_1.code) `${utils.longToNumber}(${readSnippet} as Long)`;
719
- }
720
- }
721
- else if ((0, types_1.isEnum)(field)) {
722
- if (options.stringEnums) {
723
- const fromJson = (0, types_1.getEnumMethod)(ctx, field.typeName, "FromJSON");
724
- readSnippet = (0, ts_poet_1.code) `${fromJson}(${readSnippet})`;
725
- }
726
- else {
727
- readSnippet = (0, ts_poet_1.code) `${readSnippet} as any`;
728
- }
729
- }
730
- }
731
- else if ((0, types_1.isValueType)(ctx, field)) {
732
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
733
- const unwrap = (decodedValue) => {
734
- if ((0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isAnyValueType)(field) || (0, types_1.isFieldMaskType)(field)) {
735
- return (0, ts_poet_1.code) `${type}.unwrap(${decodedValue})`;
736
- }
737
- return (0, ts_poet_1.code) `${decodedValue}.value`;
738
- };
739
- const decoder = (0, ts_poet_1.code) `${type}.decode(reader, reader.uint32())`;
740
- readSnippet = (0, ts_poet_1.code) `${unwrap(decoder)}`;
741
- }
742
- else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
743
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
744
- readSnippet = (0, ts_poet_1.code) `${utils.fromTimestamp}(${type}.decode(reader, reader.uint32()))`;
745
- }
746
- else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
747
- const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
748
- readSnippet = (0, ts_poet_1.code) `${utils.fromProtoObjectId}(${type}.decode(reader, reader.uint32()))`;
749
- }
750
- else if ((0, types_1.isMessage)(field)) {
751
- const type = (0, types_1.basicTypeName)(ctx, field);
752
- if (field.type == ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_GROUP)
753
- readSnippet = (0, ts_poet_1.code) `${type}.decode(reader)`;
754
- else
755
- readSnippet = (0, ts_poet_1.code) `${type}.decode(reader, reader.uint32())`;
756
- }
757
- else {
758
- throw new Error(`Unhandled field ${field}`);
759
- }
799
+ const readSnippet = getDecodeReadSnippet(ctx, field);
760
800
  // and then use the snippet to handle repeated fields if necessary
761
801
  const initializerNecessary = !options.initializeFieldsAsUndefined && (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
762
802
  if ((0, types_1.isRepeated)(field)) {
@@ -957,10 +997,10 @@ function generateEncode(ctx, fullName, messageDesc) {
957
997
  }
958
998
  `
959
999
  : writeSnippet(`{ ${maybeTypeField} key: key as any, value }`);
960
- const optionalAlternative = isOptional ? " || {}" : "";
1000
+ const optionalAlternative = isOptional ? (ctx.options.useMapType ? " || new Map()" : " || {}") : "";
961
1001
  if (ctx.options.useMapType) {
962
1002
  chunks.push((0, ts_poet_1.code) `
963
- message.${fieldName}${optionalAlternative}.forEach((value, key) => {
1003
+ (message.${fieldName}${optionalAlternative}).forEach((value, key) => {
964
1004
  ${entryWriteSnippet}
965
1005
  });
966
1006
  `);
@@ -1099,6 +1139,226 @@ function generateEncode(ctx, fullName, messageDesc) {
1099
1139
  chunks.push((0, ts_poet_1.code) `}`);
1100
1140
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
1101
1141
  }
1142
+ function generateSetExtension(ctx, fullName) {
1143
+ return (0, ts_poet_1.code) `
1144
+ setExtension <T> (message: ${fullName}, extension: ${ctx.utils.Extension}<T>, value: T): void {
1145
+ const encoded = extension.encode!(value);
1146
+
1147
+ if (message._unknownFields !== undefined){
1148
+ delete message._unknownFields[extension.tag];
1149
+
1150
+ if (extension.singularTag !== undefined) {
1151
+ delete message._unknownFields[extension.singularTag];
1152
+ }
1153
+ }
1154
+
1155
+ if (encoded.length !== 0) {
1156
+ if (message._unknownFields === undefined) {
1157
+ message._unknownFields = {};
1158
+ }
1159
+
1160
+ message._unknownFields[extension.tag] = encoded;
1161
+ }
1162
+ }
1163
+ `;
1164
+ }
1165
+ function generateGetExtension(ctx, fullName) {
1166
+ return (0, ts_poet_1.code) `
1167
+ getExtension <T> (message: ${fullName}, extension: ${ctx.utils.Extension}<T>): T | undefined {
1168
+ let results: T | undefined = undefined;
1169
+
1170
+ if (message._unknownFields === undefined) {
1171
+ return undefined;
1172
+ }
1173
+
1174
+ let list = message._unknownFields[extension.tag];
1175
+
1176
+ if (list !== undefined) {
1177
+ results = extension.decode!(extension.tag, list);
1178
+ }
1179
+
1180
+ if (extension.singularTag === undefined) {
1181
+ return results;
1182
+ }
1183
+
1184
+ list = message._unknownFields[extension.singularTag];
1185
+
1186
+ if (list !== undefined) {
1187
+ const results2 = extension.decode!(extension.singularTag, list);
1188
+
1189
+ if (results !== undefined && (results as any).length !== 0) {
1190
+ results = (results as any).concat(results2);
1191
+ } else {
1192
+ results = results2;
1193
+ }
1194
+ }
1195
+
1196
+ return results;
1197
+ }
1198
+ `;
1199
+ }
1200
+ function generateExtension(ctx, message, extension) {
1201
+ var _a;
1202
+ const type = (0, types_1.toTypeName)(ctx, message, extension);
1203
+ const packedTag = (0, types_1.isRepeated)(extension) && (0, types_1.packedType)(extension.type) !== undefined ? ((extension.number << 3) | 2) >>> 0 : undefined;
1204
+ const singularTag = ((extension.number << 3) | (0, types_1.basicWireType)(extension.type)) >>> 0;
1205
+ const tag = packedTag !== null && packedTag !== void 0 ? packedTag : singularTag;
1206
+ const chunks = [];
1207
+ chunks.push((0, ts_poet_1.code) `{`);
1208
+ chunks.push((0, ts_poet_1.code) `number: ${extension.number},`);
1209
+ chunks.push((0, ts_poet_1.code) `tag: ${tag},`);
1210
+ if (packedTag !== undefined)
1211
+ chunks.push((0, ts_poet_1.code) `singularTag: ${singularTag},`);
1212
+ chunks.push((0, ts_poet_1.code) `repeated: ${extension.label == ts_proto_descriptors_1.FieldDescriptorProto_Label.LABEL_REPEATED},`);
1213
+ chunks.push((0, ts_poet_1.code) `packed: ${((_a = extension.options) === null || _a === void 0 ? void 0 : _a.packed) ? true : false},`);
1214
+ const Reader = (0, utils_1.impFile)(ctx.options, "Reader@protobufjs/minimal");
1215
+ const Writer = (0, utils_1.impFile)(ctx.options, "Writer@protobufjs/minimal");
1216
+ if (ctx.options.outputEncodeMethods === true ||
1217
+ ctx.options.outputEncodeMethods === "encode-only" ||
1218
+ ctx.options.outputEncodeMethods === "encode-no-creation") {
1219
+ chunks.push((0, ts_poet_1.code) `
1220
+ encode: (value: ${type}): Uint8Array[] => {
1221
+ const encoded: Uint8Array[] = [];
1222
+ `);
1223
+ function getEncodeSnippet(ctx, field) {
1224
+ const { options, utils } = ctx;
1225
+ if ((0, types_1.isEnum)(field) && options.stringEnums) {
1226
+ const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
1227
+ return (place) => (0, ts_poet_1.code) `writer.${(0, types_1.toReaderCall)(field)}(${toNumber}(${place}))`;
1228
+ }
1229
+ else if ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT) {
1230
+ return (place) => (0, ts_poet_1.code) `writer.${(0, types_1.toReaderCall)(field)}(${place}.toString())`;
1231
+ }
1232
+ else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
1233
+ return (place) => (0, ts_poet_1.code) `writer.${(0, types_1.toReaderCall)(field)}(${place})`;
1234
+ }
1235
+ else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
1236
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
1237
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toProtoObjectId}(${place}), writer.fork()).ldelim()`;
1238
+ }
1239
+ else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
1240
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
1241
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toTimestamp}(${place}), writer.fork()).ldelim()`;
1242
+ }
1243
+ else if ((0, types_1.isValueType)(ctx, field)) {
1244
+ const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : "";
1245
+ const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
1246
+ const wrappedValue = (place) => {
1247
+ if ((0, types_1.isAnyValueType)(field) || (0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isFieldMaskType)(field)) {
1248
+ return (0, ts_poet_1.code) `${type}.wrap(${place})`;
1249
+ }
1250
+ return (0, ts_poet_1.code) `{${maybeTypeField} value: ${place}!}`;
1251
+ };
1252
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${wrappedValue(place)}, writer.fork()).ldelim()`;
1253
+ }
1254
+ else if ((0, types_1.isMessage)(field)) {
1255
+ const type = (0, types_1.basicTypeName)(ctx, field);
1256
+ if (field.type == ts_proto_descriptors_1.FieldDescriptorProto_Type.TYPE_GROUP) {
1257
+ const endTag = ((field.number << 3) | 4) >>> 0;
1258
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer).uint32(${endTag})`;
1259
+ }
1260
+ return (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer.fork()).ldelim()`;
1261
+ }
1262
+ else {
1263
+ throw new Error(`Unhandled field ${field}`);
1264
+ }
1265
+ }
1266
+ const writeSnippet = getEncodeSnippet(ctx, extension);
1267
+ if ((0, types_1.isRepeated)(extension)) {
1268
+ if (packedTag === undefined) {
1269
+ chunks.push((0, ts_poet_1.code) `
1270
+ for (const v of value) {
1271
+ const writer = ${Writer}.create();
1272
+ ${writeSnippet("v")};
1273
+ encoded.push(writer.finish());
1274
+ }
1275
+ `);
1276
+ }
1277
+ else {
1278
+ const rhs = (x) => (0, types_1.isLong)(extension) && ctx.options.forceLong === options_1.LongOption.BIGINT ? `${x}.toString()` : x;
1279
+ chunks.push((0, ts_poet_1.code) `
1280
+ const writer = ${Writer}.create();
1281
+ writer.fork();
1282
+ for (const v of value) {
1283
+ ${writeSnippet(rhs("v"))};
1284
+ }
1285
+ writer.ldelim();
1286
+ encoded.push(writer.finish());
1287
+ `);
1288
+ }
1289
+ }
1290
+ else if ((0, types_1.isScalar)(extension) || (0, types_1.isEnum)(extension)) {
1291
+ chunks.push((0, ts_poet_1.code) `
1292
+ if (${(0, types_1.notDefaultCheck)(ctx, extension, message === null || message === void 0 ? void 0 : message.options, "value")}) {
1293
+ const writer = ${Writer}.create();
1294
+ ${writeSnippet("value")};
1295
+ encoded.push(writer.finish());
1296
+ }
1297
+ `);
1298
+ }
1299
+ else {
1300
+ chunks.push((0, ts_poet_1.code) `
1301
+ const writer = ${Writer}.create();
1302
+ ${writeSnippet("value")};
1303
+ encoded.push(writer.finish());
1304
+ `);
1305
+ }
1306
+ chunks.push((0, ts_poet_1.code) `
1307
+ return encoded;
1308
+ },
1309
+ `);
1310
+ }
1311
+ if (ctx.options.outputEncodeMethods === true || ctx.options.outputEncodeMethods === "decode-only") {
1312
+ chunks.push((0, ts_poet_1.code) `decode: (tag: number, input: Uint8Array[]): ${type} => {`);
1313
+ // get a generic 'reader.doSomething' bit that is specific to the basic type
1314
+ const readSnippet = getDecodeReadSnippet(ctx, extension);
1315
+ if ((0, types_1.isRepeated)(extension)) {
1316
+ chunks.push((0, ts_poet_1.code) `const values: ${type} = [];`);
1317
+ // start loop over all buffers
1318
+ chunks.push((0, ts_poet_1.code) `
1319
+ for (var buffer of input) {
1320
+ const reader = ${Reader}.create(buffer);
1321
+ `);
1322
+ if ((0, types_1.packedType)(extension.type) === undefined) {
1323
+ chunks.push((0, ts_poet_1.code) `
1324
+ values.push(${readSnippet});
1325
+ `);
1326
+ }
1327
+ else {
1328
+ chunks.push((0, ts_poet_1.code) `
1329
+ if(tag == ${packedTag}){
1330
+ const end2 = reader.uint32() + reader.pos;
1331
+ while (reader.pos < end2) {
1332
+ values.push(${readSnippet});
1333
+ }
1334
+ }else{
1335
+ values.push(${readSnippet});
1336
+ }
1337
+ `);
1338
+ }
1339
+ chunks.push((0, ts_poet_1.code) `
1340
+ }
1341
+
1342
+ return values;
1343
+ },
1344
+ `);
1345
+ }
1346
+ else {
1347
+ // pick the last entry, since it overrides all previous entries if not repeated
1348
+ chunks.push((0, ts_poet_1.code) `
1349
+ const reader = ${Reader}.create(input[input.length -1]);
1350
+ return ${readSnippet};
1351
+ },
1352
+ `);
1353
+ }
1354
+ }
1355
+ chunks.push((0, ts_poet_1.code) `}`);
1356
+ return {
1357
+ name: (0, case_1.maybeSnakeToCamel)(extension.name, ctx.options),
1358
+ type,
1359
+ extensionInfo: (0, ts_poet_1.joinCode)(chunks, { on: "\n" }),
1360
+ };
1361
+ }
1102
1362
  /**
1103
1363
  * Creates a function to decode a message from JSON.
1104
1364
  *
@@ -1212,6 +1472,9 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1212
1472
  else if ((0, types_1.isLong)(valueField) && options.forceLong === options_1.LongOption.LONG) {
1213
1473
  return (0, ts_poet_1.code) `Long.fromValue(${from} as Long | string)`;
1214
1474
  }
1475
+ else if ((0, types_1.isLong)(valueField) && options.forceLong === options_1.LongOption.BIGINT) {
1476
+ return (0, ts_poet_1.code) `BigInt(${from} as string | number | bigint | boolean)`;
1477
+ }
1215
1478
  else if ((0, types_1.isEnum)(valueField)) {
1216
1479
  const fromJson = (0, types_1.getEnumMethod)(ctx, valueField.typeName, "FromJSON");
1217
1480
  return (0, ts_poet_1.code) `${fromJson}(${from})`;
@@ -1250,6 +1513,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1250
1513
  throw new Error(`Unhandled field ${field}`);
1251
1514
  }
1252
1515
  };
1516
+ const noDefaultValue = !options.initializeFieldsAsUndefined && (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
1253
1517
  // and then use the snippet to handle repeated fields if necessary
1254
1518
  if ((_a = canonicalFromJson[fullTypeName]) === null || _a === void 0 ? void 0 : _a[fieldName]) {
1255
1519
  chunks.push((0, ts_poet_1.code) `${fieldName}: ${canonicalFromJson[fullTypeName][fieldName]("object")},`);
@@ -1257,29 +1521,32 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1257
1521
  else if ((0, types_1.isRepeated)(field)) {
1258
1522
  if ((0, types_1.isMapType)(ctx, messageDesc, field)) {
1259
1523
  const fieldType = (0, types_1.toTypeName)(ctx, messageDesc, field);
1260
- const i = maybeCastToNumber(ctx, messageDesc, field, "key");
1524
+ const i = convertFromObjectKey(ctx, messageDesc, field, "key");
1261
1525
  if (ctx.options.useMapType) {
1526
+ const fallback = noDefaultValue ? "undefined" : "new Map()";
1262
1527
  chunks.push((0, ts_poet_1.code) `
1263
1528
  ${fieldName}: ${ctx.utils.isObject}(${jsonProperty})
1264
1529
  ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => {
1265
1530
  acc.set(${i}, ${readSnippet("value")});
1266
1531
  return acc;
1267
1532
  }, new Map())
1268
- : new Map(),
1533
+ : ${fallback},
1269
1534
  `);
1270
1535
  }
1271
1536
  else {
1537
+ const fallback = noDefaultValue ? "undefined" : "{}";
1272
1538
  chunks.push((0, ts_poet_1.code) `
1273
1539
  ${fieldName}: ${ctx.utils.isObject}(${jsonProperty})
1274
1540
  ? Object.entries(${jsonProperty}).reduce<${fieldType}>((acc, [key, value]) => {
1275
1541
  acc[${i}] = ${readSnippet("value")};
1276
1542
  return acc;
1277
1543
  }, {})
1278
- : {},
1544
+ : ${fallback},
1279
1545
  `);
1280
1546
  }
1281
1547
  }
1282
1548
  else {
1549
+ const fallback = noDefaultValue ? "undefined" : "[]";
1283
1550
  const readValueSnippet = readSnippet("e");
1284
1551
  if (readValueSnippet.toString() === (0, ts_poet_1.code) `e`.toString()) {
1285
1552
  chunks.push((0, ts_poet_1.code) `${fieldName}: Array.isArray(${jsonPropertyOptional}) ? [...${jsonProperty}] : [],`);
@@ -1287,7 +1554,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1287
1554
  else {
1288
1555
  // Explicit `any` type required to make TS with noImplicitAny happy. `object` is also `any` here.
1289
1556
  chunks.push((0, ts_poet_1.code) `
1290
- ${fieldName}: Array.isArray(${jsonPropertyOptional}) ? ${jsonProperty}.map((e: any) => ${readValueSnippet}): [],
1557
+ ${fieldName}: Array.isArray(${jsonPropertyOptional}) ? ${jsonProperty}.map((e: any) => ${readValueSnippet}): ${fallback},
1291
1558
  `);
1292
1559
  }
1293
1560
  }
@@ -1326,7 +1593,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
1326
1593
  `);
1327
1594
  }
1328
1595
  else {
1329
- const fallback = (0, types_1.isWithinOneOf)(field) ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1596
+ const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1330
1597
  chunks.push((0, ts_poet_1.code) `
1331
1598
  ${fieldName}: ${ctx.utils.isSet}(${jsonProperty})
1332
1599
  ? ${readSnippet(`${jsonProperty}`)}
@@ -1463,12 +1730,13 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1463
1730
  };
1464
1731
  if ((0, types_1.isMapType)(ctx, messageDesc, field)) {
1465
1732
  // Maps might need their values transformed, i.e. bytes --> base64
1733
+ const i = convertToObjectKey(ctx, messageDesc, field, "k");
1466
1734
  if (ctx.options.useMapType) {
1467
1735
  chunks.push((0, ts_poet_1.code) `
1468
1736
  ${jsonProperty} = {};
1469
1737
  if (message.${fieldName}) {
1470
1738
  message.${fieldName}.forEach((v, k) => {
1471
- ${jsonProperty}[k] = ${readSnippet("v")};
1739
+ ${jsonProperty}[${i}] = ${readSnippet("v")};
1472
1740
  });
1473
1741
  }
1474
1742
  `);
@@ -1478,7 +1746,7 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
1478
1746
  ${jsonProperty} = {};
1479
1747
  if (message.${fieldName}) {
1480
1748
  Object.entries(message.${fieldName}).forEach(([k, v]) => {
1481
- ${jsonProperty}[k] = ${readSnippet("v")};
1749
+ ${jsonProperty}[${i}] = ${readSnippet("v")};
1482
1750
  });
1483
1751
  }
1484
1752
  `);
@@ -1572,6 +1840,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1572
1840
  else if ((0, types_1.isLong)(valueField) && options.forceLong === options_1.LongOption.LONG) {
1573
1841
  return (0, ts_poet_1.code) `Long.fromValue(${from})`;
1574
1842
  }
1843
+ else if ((0, types_1.isLong)(valueField) && options.forceLong === options_1.LongOption.BIGINT) {
1844
+ return (0, ts_poet_1.code) `BigInt(${from} as string | number | bigint | boolean)`;
1845
+ }
1575
1846
  else {
1576
1847
  const cstr = (0, case_1.capitalize)(valueType.toCodeString([]));
1577
1848
  return (0, ts_poet_1.code) `${cstr}(${from})`;
@@ -1607,18 +1878,22 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1607
1878
  throw new Error(`Unhandled field ${field}`);
1608
1879
  }
1609
1880
  };
1881
+ const noDefaultValue = !options.initializeFieldsAsUndefined && (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
1610
1882
  // and then use the snippet to handle repeated fields if necessary
1611
1883
  if ((0, types_1.isRepeated)(field)) {
1612
1884
  if ((0, types_1.isMapType)(ctx, messageDesc, field)) {
1613
1885
  const fieldType = (0, types_1.toTypeName)(ctx, messageDesc, field);
1614
- const i = maybeCastToNumber(ctx, messageDesc, field, "key");
1886
+ const i = convertFromObjectKey(ctx, messageDesc, field, "key");
1887
+ const noValueSnippet = noDefaultValue
1888
+ ? `(object.${fieldName} === undefined || object.${fieldName} === null) ? undefined : `
1889
+ : "";
1615
1890
  if (ctx.options.useMapType) {
1616
1891
  chunks.push((0, ts_poet_1.code) `
1617
- message.${fieldName} = (() => {
1892
+ message.${fieldName} = ${noValueSnippet} (() => {
1618
1893
  const m = new Map();
1619
1894
  (object.${fieldName} as ${fieldType} ?? new Map()).forEach((value, key) => {
1620
1895
  if (value !== undefined) {
1621
- m.set(${i}, ${readSnippet("value")});
1896
+ m.set(key, ${readSnippet("value")});
1622
1897
  }
1623
1898
  });
1624
1899
  return m;
@@ -1627,7 +1902,7 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1627
1902
  }
1628
1903
  else {
1629
1904
  chunks.push((0, ts_poet_1.code) `
1630
- message.${fieldName} = Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1905
+ message.${fieldName} = ${noValueSnippet} Object.entries(object.${fieldName} ?? {}).reduce<${fieldType}>((acc, [key, value]) => {
1631
1906
  if (value !== undefined) {
1632
1907
  acc[${i}] = ${readSnippet("value")};
1633
1908
  }
@@ -1637,8 +1912,9 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1637
1912
  }
1638
1913
  }
1639
1914
  else {
1915
+ const fallback = noDefaultValue ? "undefined" : "[]";
1640
1916
  chunks.push((0, ts_poet_1.code) `
1641
- message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || [];
1917
+ message.${fieldName} = object.${fieldName}?.map((e) => ${readSnippet("e")}) || ${fallback};
1642
1918
  `);
1643
1919
  }
1644
1920
  }
@@ -1657,11 +1933,11 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1657
1933
  }
1658
1934
  else if (readSnippet(`x`).toCodeString([]) == "x") {
1659
1935
  // An optimized case of the else below that works when `readSnippet` returns the plain input
1660
- const fallback = (0, types_1.isWithinOneOf)(field) ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1936
+ const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1661
1937
  chunks.push((0, ts_poet_1.code) `message.${fieldName} = object.${fieldName} ?? ${fallback};`);
1662
1938
  }
1663
1939
  else {
1664
- const fallback = (0, types_1.isWithinOneOf)(field) ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1940
+ const fallback = (0, types_1.isWithinOneOf)(field) || noDefaultValue ? "undefined" : (0, types_1.defaultValue)(ctx, field);
1665
1941
  chunks.push((0, ts_poet_1.code) `
1666
1942
  message.${fieldName} = (object.${fieldName} !== undefined && object.${fieldName} !== null)
1667
1943
  ? ${readSnippet(`object.${fieldName}`)}
@@ -1675,13 +1951,47 @@ function generateFromPartial(ctx, fullName, messageDesc) {
1675
1951
  return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
1676
1952
  }
1677
1953
  exports.contextTypeVar = "Context extends DataLoaders";
1678
- function maybeCastToNumber(ctx, messageDesc, field, variableName) {
1679
- const { keyType } = (0, types_1.detectMapType)(ctx, messageDesc, field);
1954
+ function convertFromObjectKey(ctx, messageDesc, field, variableName) {
1955
+ const { keyType, keyField } = (0, types_1.detectMapType)(ctx, messageDesc, field);
1956
+ if (keyType.toCodeString([]) === "string") {
1957
+ return (0, ts_poet_1.code) `${variableName}`;
1958
+ }
1959
+ else if ((0, types_1.isLong)(keyField) && ctx.options.useMapType) {
1960
+ if (ctx.options.forceLong === options_1.LongOption.LONG) {
1961
+ return (0, ts_poet_1.code) `${(0, case_1.capitalize)(keyType.toCodeString([]))}.fromValue(${variableName})`;
1962
+ }
1963
+ else if (ctx.options.forceLong === options_1.LongOption.BIGINT) {
1964
+ return (0, ts_poet_1.code) `BigInt(${variableName})`;
1965
+ }
1966
+ else if (ctx.options.forceLong === options_1.LongOption.STRING) {
1967
+ return (0, ts_poet_1.code) `String(${variableName})`;
1968
+ }
1969
+ else {
1970
+ return (0, ts_poet_1.code) `Number(${variableName})`;
1971
+ }
1972
+ }
1973
+ else {
1974
+ return (0, ts_poet_1.code) `Number(${variableName})`;
1975
+ }
1976
+ }
1977
+ function convertToObjectKey(ctx, messageDesc, field, variableName) {
1978
+ const { keyType, keyField } = (0, types_1.detectMapType)(ctx, messageDesc, field);
1680
1979
  if (keyType.toCodeString([]) === "string") {
1681
- return variableName;
1980
+ return (0, ts_poet_1.code) `${variableName}`;
1981
+ }
1982
+ else if ((0, types_1.isLong)(keyField) && ctx.options.useMapType) {
1983
+ if (ctx.options.forceLong === options_1.LongOption.LONG) {
1984
+ return (0, ts_poet_1.code) `${ctx.utils.longToNumber}(${variableName})`;
1985
+ }
1986
+ else if (ctx.options.forceLong === options_1.LongOption.BIGINT) {
1987
+ return (0, ts_poet_1.code) `${variableName}.toString()`;
1988
+ }
1989
+ else {
1990
+ return (0, ts_poet_1.code) `${variableName}`;
1991
+ }
1682
1992
  }
1683
1993
  else {
1684
- return `Number(${variableName})`;
1994
+ return (0, ts_poet_1.code) `${variableName}`;
1685
1995
  }
1686
1996
  }
1687
1997
  function maybeReadonly(options) {
package/build/options.js CHANGED
@@ -78,6 +78,7 @@ function defaultOptions() {
78
78
  useMapType: false,
79
79
  useReadonlyTypes: false,
80
80
  useSnakeTypeName: true,
81
+ outputExtensions: false,
81
82
  M: {},
82
83
  };
83
84
  }
package/build/types.js CHANGED
@@ -578,7 +578,7 @@ exports.getEnumMethod = getEnumMethod;
578
578
  function toTypeName(ctx, messageDesc, field) {
579
579
  let type = basicTypeName(ctx, field, { keepValueType: false });
580
580
  if (isRepeated(field)) {
581
- const mapType = detectMapType(ctx, messageDesc, field);
581
+ const mapType = messageDesc ? detectMapType(ctx, messageDesc, field) : false;
582
582
  if (mapType) {
583
583
  const { keyType, valueType } = mapType;
584
584
  if (ctx.options.useMapType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "1.144.1",
3
+ "version": "1.146.0",
4
4
  "description": "",
5
5
  "main": "build/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",