ts-proto 2.1.0 → 2.2.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.
package/README.markdown CHANGED
@@ -118,7 +118,7 @@ plugins:
118
118
  path: ../node_modules/ts-proto/protoc-gen-ts_proto
119
119
  ```
120
120
 
121
- To prevent `buf push` from reading irrelevent `.proto` files, configure `buf.yaml` like so:
121
+ To prevent `buf push` from reading irrelevant `.proto` files, configure `buf.yaml` like so:
122
122
 
123
123
  ```yaml
124
124
  build:
@@ -150,7 +150,7 @@ In terms of the code that `ts-proto` generates, the general goals are:
150
150
 
151
151
  - Idiomatic TypeScript/ES6 types
152
152
  - `ts-proto` is a clean break from either the built-in Google/Java-esque JS code of `protoc` or the "make `.d.ts` files the `*.js` comments" approach of `protobufjs`
153
- - (Techically the `protobufjs/minimal` package is used for actually reading/writing bytes.)
153
+ - (Technically the `protobufjs/minimal` package is used for actually reading/writing bytes.)
154
154
  - TypeScript-first output
155
155
  - Interfaces over classes
156
156
  - As much as possible, types are just interfaces, so you can work with messages just like regular hashes/data structures.
@@ -248,7 +248,7 @@ creating a class and calling the right getters/setters.
248
248
 
249
249
  (Note: this is currently only supported by the Twirp clients.)
250
250
 
251
- If you're using ts-proto's clients to call backend micro-services, similar to the N+1 problem in SQL applications, it is easy for micro-service clients to (when serving an individual request) inadvertantly trigger multiple separate RPC calls for "get book 1", "get book 2", "get book 3", that should really be batched into a single "get books [1, 2, 3]" (assuming the backend supports a batch-oriented RPC method).
251
+ If you're using ts-proto's clients to call backend micro-services, similar to the N+1 problem in SQL applications, it is easy for micro-service clients to (when serving an individual request) inadvertently trigger multiple separate RPC calls for "get book 1", "get book 2", "get book 3", that should really be batched into a single "get books [1, 2, 3]" (assuming the backend supports a batch-oriented RPC method).
252
252
 
253
253
  ts-proto can help with this, and essentially auto-batch your individual "get book" calls into batched "get books" calls.
254
254
 
@@ -262,7 +262,7 @@ For ts-proto to do this, you need to implement your service's RPC methods with t
262
262
 
263
263
  When ts-proto recognizes methods of this pattern, it will automatically create a "non-batch" version of `<OperationName>` for the client, i.e. `client.Get<OperationName>`, that takes a single id and returns a single result.
264
264
 
265
- This provides the client code with the illusion that it can make individual `Get<OperationName>` calls (which is generally preferrable/easier when implementing the client's business logic), but the actual implementation that ts-proto provides will end up making `Batch<OperationName>` calls to the backend service.
265
+ This provides the client code with the illusion that it can make individual `Get<OperationName>` calls (which is generally preferable/easier when implementing the client's business logic), but the actual implementation that ts-proto provides will end up making `Batch<OperationName>` calls to the backend service.
266
266
 
267
267
  You also need to enable the `useContext=true` build-time parameter, which gives all client methods a Go-style `ctx` parameter, with a `getDataLoaders` method that lets ts-proto cache/resolve request-scoped [DataLoaders](https://github.com/graphql/dataloader), which provide the fundamental auto-batch detection/flushing behavior.
268
268
 
@@ -325,7 +325,7 @@ Generated code will be placed in the Gradle build directory.
325
325
 
326
326
  The default behavior is `forceLong=number`, which will internally still use the `long` library to encode/decode values on the wire (so you will still see a `util.Long = Long` line in your output), but will convert the `long` values to `number` automatically for you. Note that a runtime error is thrown if, while doing this conversion, a 64-bit value is larger than can be correctly stored as a `number`.
327
327
 
328
- - With `--ts_proto_opt=useJsTypeOverride`, 64-bit numbers will be ouput as the [FieldOption.JSType](https://protobuf.dev/reference/java/api-docs/com/google/protobuf/DescriptorProtos.FieldOptions.JSType) specified on the field. This takes precendence over the `forceLong` option provided.
328
+ - With `--ts_proto_opt=useJsTypeOverride`, 64-bit numbers will be output as the [FieldOption.JSType](https://protobuf.dev/reference/java/api-docs/com/google/protobuf/DescriptorProtos.FieldOptions.JSType) specified on the field. This takes precedence over the `forceLong` option provided.
329
329
 
330
330
  - With `--ts_proto_opt=esModuleInterop=true` changes output to be `esModuleInterop` compliant.
331
331
 
@@ -438,7 +438,7 @@ Generated code will be placed in the Gradle build directory.
438
438
 
439
439
  (Requires `nestJs=true`.)
440
440
 
441
- - With`--ts_proto_opt=addNestjsRestParameter=true`, the last argument of service methods will be an rest parameter with type any. This way you can use custom decorators you could normally use in nestjs.
441
+ - With`--ts_proto_opt=addNestjsRestParameter=true`, the last argument of service methods will be a rest parameter with type any. This way you can use custom decorators you could normally use in nestjs.
442
442
 
443
443
  (Requires `nestJs=true`.)
444
444
 
@@ -476,11 +476,11 @@ Generated code will be placed in the Gradle build directory.
476
476
 
477
477
  - With `--ts_proto_opt=outputServices=false`, or `=none`, ts-proto will output NO service definitions.
478
478
 
479
- - With `--ts_proto_opt=rpcBeforeRequest=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `beforeRequest(service: string, message: string, request: <RequestType>)`. It will will also automatically set `outputServices=default`. Each of the Service's methods will call `beforeRequest` before performing it's request.
479
+ - With `--ts_proto_opt=rpcBeforeRequest=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `beforeRequest(service: string, message: string, request: <RequestType>)`. It will also automatically set `outputServices=default`. Each of the Service's methods will call `beforeRequest` before performing its request.
480
480
 
481
- - With `--ts_proto_opt=rpcAfterResponse=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `afterResponse(service: string, message: string, response: <ResponseType>)`. It will will also automatically set `outputServices=default`. Each of the Service's methods will call `afterResponse` before returning the response.
481
+ - With `--ts_proto_opt=rpcAfterResponse=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `afterResponse(service: string, message: string, response: <ResponseType>)`. It will also automatically set `outputServices=default`. Each of the Service's methods will call `afterResponse` before returning the response.
482
482
 
483
- - With `--ts_proto_opt=rpcErrorHandler=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `handleError(service: string, message: string, error: Error)`. It will will also automatically set `outputServices=default`.
483
+ - With `--ts_proto_opt=rpcErrorHandler=true`, ts-proto will add a function definition to the Rpc interface definition with the signature: `handleError(service: string, message: string, error: Error)`. It will also automatically set `outputServices=default`.
484
484
 
485
485
  - With `--ts_proto_opt=useAbortSignal=true`, the generated services will accept an `AbortSignal` to cancel RPC calls.
486
486
 
@@ -522,7 +522,7 @@ Generated code will be placed in the Gradle build directory.
522
522
 
523
523
  - With `--ts_proto_opt=useNumericEnumForJson=true`, the JSON converter (`toJSON`) will encode enum values as int, rather than a string literal.
524
524
 
525
- - With `--ts_proto_opt=initializeFieldsAsUndefined=false`, all optional field initializers will be omited from the generated base instances.
525
+ - With `--ts_proto_opt=initializeFieldsAsUndefined=false`, all optional field initializers will be omitted from the generated base instances.
526
526
 
527
527
  - With `--ts_proto_opt=disableProto2Optionals=true`, all optional fields on proto2 files will not be set to be optional. Please note that this flag is primarily for preserving ts-proto's legacy handling of proto2 files, to avoid breaking changes, and as a result, it is not intended to be used moving forward.
528
528
 
@@ -533,14 +533,14 @@ Generated code will be placed in the Gradle build directory.
533
533
  - `Mfoo/bar.proto=@myorg/some-lib` will map `foo/bar.proto` imports into `import ... from '@myorg/some-lib'`.
534
534
  - `Mfoo/bar.proto=./some/local/lib` will map `foo/bar.proto` imports into `import ... from './some/local/lib'`.
535
535
  - `Mfoo/bar.proto=some-modules/some-lib` will map `foo/bar.proto` imports into `import ... from 'some-module/some-lib'`.
536
- - **Note**: Uses are accummulated, so multiple values are expected in the form of `--ts_proto_opt=M... --ts_proto_opt=M...` (one `ts_proto_opt` per mapping).
536
+ - **Note**: Uses are accumulated, so multiple values are expected in the form of `--ts_proto_opt=M... --ts_proto_opt=M...` (one `ts_proto_opt` per mapping).
537
537
  - **Note**: Proto files that match mapped imports **will not be generated**.
538
538
 
539
539
  - With `--ts_proto_opt=useMapType=true`, the generated code for protobuf `map<key_type, value_type>` will become `Map<key_type, value_type>` that uses JavaScript Map type.
540
540
 
541
541
  The default behavior is `useMapType=false`, which makes it generate the code for protobuf `map<key_type, value_type` with the key-value pair like `{[key: key_type]: value_type}`.
542
542
 
543
- - With `--ts_proto_opt=useReadonlyTypes=true`, the generated types will be declared as immutable using typescript's `readonly` modifer.
543
+ - With `--ts_proto_opt=useReadonlyTypes=true`, the generated types will be declared as immutable using typescript's `readonly` modifier.
544
544
 
545
545
  - With `--ts_proto_opt=useSnakeTypeName=false` will remove snake casing from types.
546
546
 
@@ -633,7 +633,7 @@ export interface User {
633
633
  }
634
634
  ```
635
635
 
636
- - With `--ts_proto_opt=noDefaultsForOptionals=true`, `undefined` primitive values will not be defaulted as per the protobuf spec. Additionally unlike the standard behavior, when a field is set to it's standard default value, it *will* be encoded allowing it to be sent over the wire and distinguished from undefined values. For example if a message does not set a boolean value, ordinarily this would be defaulted to `false` which is different to it being undefined.
636
+ - With `--ts_proto_opt=noDefaultsForOptionals=true`, `undefined` primitive values will not be defaulted as per the protobuf spec. Additionally unlike the standard behavior, when a field is set to its standard default value, it *will* be encoded allowing it to be sent over the wire and distinguished from undefined values. For example if a message does not set a boolean value, ordinarily this would be defaulted to `false` which is different to it being undefined.
637
637
 
638
638
  This option allows the library to act in a compatible way with the [Wire implementation](https://square.github.io/wire/) maintained and used by Square/Block. Note: this option should only be used in combination with other client/server code generated using Wire or ts-proto with this option enabled.
639
639
 
@@ -225,7 +225,7 @@ function generateServiceClientImpl(ctx, fileDesc, serviceDesc) {
225
225
  chunks.push((0, ts_poet_1.code) `}`);
226
226
  // Create a method for each FooService method
227
227
  for (const methodDesc of serviceDesc.method) {
228
- // See if this this fuzzy matches to a batchable method
228
+ // See if this fuzzy matches to a batchable method
229
229
  if (options.context) {
230
230
  const batchMethod = (0, types_1.detectBatchMethod)(ctx, fileDesc, serviceDesc, methodDesc);
231
231
  if (batchMethod) {
@@ -4,7 +4,7 @@ import { FileDescriptorProto } from "ts-proto-descriptors";
4
4
  import { Context } from "./context";
5
5
  import { Options } from "./options";
6
6
  export declare function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [string, Code];
7
- export type Utils = ReturnType<typeof makeDeepPartial> & ReturnType<typeof makeObjectIdMethods> & ReturnType<typeof makeTimestampMethods> & ReturnType<typeof makeByteUtils> & ReturnType<typeof makeLongUtils> & ReturnType<typeof makeComparisonUtils> & ReturnType<typeof makeNiceGrpcServerStreamingMethodResult> & ReturnType<typeof makeGrpcWebErrorClass> & ReturnType<typeof makeExtensionClass> & ReturnType<typeof makeAssertionUtils>;
7
+ export type Utils = ReturnType<typeof makeDeepPartial> & ReturnType<typeof makeObjectIdMethods> & ReturnType<typeof makeTimestampMethods> & ReturnType<typeof makeByteUtils> & ReturnType<typeof makeLongUtils> & ReturnType<typeof makeComparisonUtils> & ReturnType<typeof makeNiceGrpcServerStreamingMethodResult> & ReturnType<typeof makeGrpcWebErrorClass> & ReturnType<typeof makeExtensionClass> & ReturnType<typeof makeAssertionUtils> & ReturnType<typeof makeMessageFns>;
8
8
  /** These are runtime utility methods used by the generated code. */
9
9
  export declare function makeUtils(options: Options): Utils;
10
10
  declare function makeLongUtils(options: Options, bytes: ReturnType<typeof makeByteUtils>): {
@@ -22,6 +22,15 @@ declare function makeDeepPartial(options: Options, longs: ReturnType<typeof make
22
22
  DeepPartial: ConditionalOutput;
23
23
  Exact: ConditionalOutput;
24
24
  };
25
+ declare function makeMessageFns(options: Options, deepPartial: ReturnType<typeof makeDeepPartial>, extension: ReturnType<typeof makeExtensionClass>): {
26
+ MessageFns: ConditionalOutput;
27
+ ExtensionFns: ConditionalOutput;
28
+ ExtensionHolder: ConditionalOutput;
29
+ StructWrapperFns: ConditionalOutput;
30
+ AnyValueWrapperFns: ConditionalOutput;
31
+ ListValueWrapperFns: ConditionalOutput;
32
+ FieldMaskWrapperFns: ConditionalOutput;
33
+ };
25
34
  declare function makeObjectIdMethods(): {
26
35
  fromJsonObjectId: ConditionalOutput;
27
36
  fromProtoObjectId: ConditionalOutput;
package/build/src/main.js CHANGED
@@ -96,7 +96,8 @@ function generateFile(ctx, fileDesc) {
96
96
  chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
97
97
  }
98
98
  const staticMembers = [];
99
- if (options.outputTypeAnnotations || options.outputTypeRegistry) {
99
+ const hasTypeMember = options.outputTypeAnnotations || options.outputTypeRegistry;
100
+ if (hasTypeMember) {
100
101
  staticMembers.push((0, ts_poet_1.code) `$type: '${fullTypeName}' as const`);
101
102
  }
102
103
  if (options.outputExtensions) {
@@ -153,8 +154,37 @@ function generateFile(ctx, fileDesc) {
153
154
  staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapShallow)(ctx, fullTypeName, structFieldNames));
154
155
  }
155
156
  if (staticMembers.length > 0) {
157
+ const messageFnsTypeParameters = [fullName, hasTypeMember && `'${fullTypeName}'`]
158
+ .filter((p) => !!p)
159
+ .join(", ");
160
+ const interfaces = [(0, ts_poet_1.code) `${utils.MessageFns}<${messageFnsTypeParameters}>`];
161
+ if (options.outputEncodeMethods &&
162
+ options.outputExtensions &&
163
+ options.unknownFields &&
164
+ message.extensionRange.length) {
165
+ interfaces.push((0, ts_poet_1.code) `${utils.ExtensionFns}<${fullName}>`);
166
+ }
167
+ if ((0, types_1.isStructTypeName)(fullTypeName)) {
168
+ interfaces.push((0, ts_poet_1.code) `${utils.StructWrapperFns}`);
169
+ }
170
+ else if ((0, types_1.isAnyValueTypeName)(fullTypeName)) {
171
+ interfaces.push((0, ts_poet_1.code) `${utils.AnyValueWrapperFns}`);
172
+ }
173
+ else if ((0, types_1.isListValueTypeName)(fullTypeName)) {
174
+ interfaces.push((0, ts_poet_1.code) `${utils.ListValueWrapperFns}`);
175
+ }
176
+ else if ((0, types_1.isFieldMaskTypeName)(fullTypeName)) {
177
+ interfaces.push((0, ts_poet_1.code) `${utils.FieldMaskWrapperFns}`);
178
+ }
179
+ if (options.outputExtensions) {
180
+ for (const extension of message.extension) {
181
+ const name = (0, case_1.maybeSnakeToCamel)(extension.name, ctx.options);
182
+ const type = (0, types_1.toTypeName)(ctx, message, extension);
183
+ interfaces.push((0, ts_poet_1.code) `${utils.ExtensionHolder}<"${name}", ${type}>`);
184
+ }
185
+ }
156
186
  chunks.push((0, ts_poet_1.code) `
157
- export const ${(0, ts_poet_1.def)(fullName)} = {
187
+ export const ${(0, ts_poet_1.def)(fullName)}: ${(0, ts_poet_1.joinCode)(interfaces, { on: " & " })} = {
158
188
  ${(0, ts_poet_1.joinCode)(staticMembers, { on: ",\n\n" })}
159
189
  };
160
190
  `);
@@ -281,17 +311,20 @@ function generateFile(ctx, fileDesc) {
281
311
  function makeUtils(options) {
282
312
  const bytes = makeByteUtils(options);
283
313
  const longs = makeLongUtils(options, bytes);
314
+ const deepPartial = makeDeepPartial(options, longs);
315
+ const extension = makeExtensionClass(options);
284
316
  return {
285
317
  ...bytes,
286
- ...makeDeepPartial(options, longs),
318
+ ...deepPartial,
287
319
  ...makeObjectIdMethods(),
288
320
  ...makeTimestampMethods(options, longs, bytes),
289
321
  ...longs,
290
322
  ...makeComparisonUtils(),
291
323
  ...makeNiceGrpcServerStreamingMethodResult(options),
292
324
  ...makeGrpcWebErrorClass(bytes),
293
- ...makeExtensionClass(options),
325
+ ...extension,
294
326
  ...makeAssertionUtils(bytes),
327
+ ...makeMessageFns(options, deepPartial, extension),
295
328
  };
296
329
  }
297
330
  function makeProtobufTimestampWrapper() {
@@ -461,6 +494,121 @@ function makeDeepPartial(options, longs) {
461
494
  `);
462
495
  return { Builtin, DeepPartial, Exact };
463
496
  }
497
+ function makeMessageFns(options, deepPartial, extension) {
498
+ const BinaryWriter = (0, ts_poet_1.imp)("BinaryWriter@@bufbuild/protobuf/wire");
499
+ const BinaryReader = (0, ts_poet_1.imp)("BinaryReader@@bufbuild/protobuf/wire");
500
+ const { Exact, DeepPartial } = deepPartial;
501
+ const { Extension } = extension;
502
+ const commonStaticMembers = [];
503
+ const extensionStaticMembers = [];
504
+ const hasTypeMember = options.outputTypeAnnotations || options.outputTypeRegistry;
505
+ if (hasTypeMember) {
506
+ commonStaticMembers.push((0, ts_poet_1.code) `readonly $type: V;`);
507
+ }
508
+ if (options.outputEncodeMethods) {
509
+ if (options.outputEncodeMethods === true ||
510
+ options.outputEncodeMethods === "encode-only" ||
511
+ options.outputEncodeMethods === "encode-no-creation") {
512
+ commonStaticMembers.push((0, ts_poet_1.code) `
513
+ encode(message: T, writer?: ${BinaryWriter}): ${BinaryWriter};
514
+ `);
515
+ if (options.outputExtensions && options.unknownFields) {
516
+ extensionStaticMembers.push((0, ts_poet_1.code) `
517
+ setExtension<E>(message: T, extension: ${Extension}<E>, value: E): void;
518
+ `);
519
+ }
520
+ }
521
+ if (options.outputEncodeMethods === true || options.outputEncodeMethods === "decode-only") {
522
+ commonStaticMembers.push((0, ts_poet_1.code) `
523
+ decode(input: ${BinaryReader} | Uint8Array, length?: number): T;
524
+ `);
525
+ if (options.outputExtensions && options.unknownFields) {
526
+ extensionStaticMembers.push((0, ts_poet_1.code) `
527
+ getExtension<E>(message: T, extension: ${Extension}<E>): E | undefined;
528
+ `);
529
+ }
530
+ }
531
+ }
532
+ if (options.useAsyncIterable) {
533
+ commonStaticMembers.push((0, ts_poet_1.code) `
534
+ encodeTransform(
535
+ source: AsyncIterable<T | T[]> | Iterable<T | T[]>
536
+ ): AsyncIterable<Uint8Array>;
537
+ `);
538
+ commonStaticMembers.push((0, ts_poet_1.code) `
539
+ decodeTransform(
540
+ source: AsyncIterable<Uint8Array | Uint8Array[]> | Iterable<Uint8Array | Uint8Array[]>
541
+ ): AsyncIterable<T>;
542
+ `);
543
+ }
544
+ if (options.outputJsonMethods) {
545
+ if (options.outputJsonMethods === true || options.outputJsonMethods === "from-only") {
546
+ commonStaticMembers.push((0, ts_poet_1.code) `fromJSON(object: any): T;`);
547
+ }
548
+ if (options.outputJsonMethods === true || options.outputJsonMethods === "to-only") {
549
+ commonStaticMembers.push((0, ts_poet_1.code) `toJSON(message: T): unknown;`);
550
+ }
551
+ }
552
+ if (options.outputPartialMethods) {
553
+ if (options.useExactTypes) {
554
+ commonStaticMembers.push((0, ts_poet_1.code) `create<I extends ${Exact}<${DeepPartial}<T>, I>>(base?: I): T;`);
555
+ commonStaticMembers.push((0, ts_poet_1.code) `fromPartial<I extends ${Exact}<${DeepPartial}<T>, I>>(object: I): T;`);
556
+ }
557
+ else {
558
+ commonStaticMembers.push((0, ts_poet_1.code) `create(base?: DeepPartial<T>): T;`);
559
+ commonStaticMembers.push((0, ts_poet_1.code) `fromPartial(object: DeepPartial<T>): T;`);
560
+ }
561
+ }
562
+ const maybeExport = options.exportCommonSymbols ? "export" : "";
563
+ const MessageFns = (0, ts_poet_1.conditionalOutput)("MessageFns", (0, ts_poet_1.code) `
564
+ ${maybeExport} interface MessageFns<T${hasTypeMember ? ", V extends string" : ""}> {
565
+ ${(0, ts_poet_1.joinCode)(commonStaticMembers, { on: "\n" })}
566
+ }
567
+ `);
568
+ const ExtensionFns = (0, ts_poet_1.conditionalOutput)("ExtensionFns", (0, ts_poet_1.code) `
569
+ ${maybeExport} interface ExtensionFns<T> {
570
+ ${(0, ts_poet_1.joinCode)(extensionStaticMembers, { on: "\n" })}
571
+ }
572
+ `);
573
+ const ExtensionHolder = (0, ts_poet_1.conditionalOutput)("ExtensionHolder", (0, ts_poet_1.code) `
574
+ ${maybeExport} type ExtensionHolder<T extends string, V> = {
575
+ [key in T]: Extension<V>;
576
+ }
577
+ `);
578
+ const StructWrapperFns = (0, ts_poet_1.conditionalOutput)("StructWrapperFns", (0, ts_poet_1.code) `
579
+ ${maybeExport} interface StructWrapperFns {
580
+ wrap(object: {[key: string]: any} | undefined): Struct;
581
+ unwrap(message: Struct): {[key: string]: any};
582
+ }
583
+ `);
584
+ const AnyValueWrapperFns = (0, ts_poet_1.conditionalOutput)("AnyValueWrapperFns", (0, ts_poet_1.code) `
585
+ ${maybeExport} interface AnyValueWrapperFns {
586
+ wrap(value: any): Value;
587
+ unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined;
588
+ }
589
+ `);
590
+ const ListValueWrapperFns = (0, ts_poet_1.conditionalOutput)("ListValueWrapperFns", (0, ts_poet_1.code) `
591
+ ${maybeExport} interface ListValueWrapperFns {
592
+ wrap(array: ${options.useReadonlyTypes ? "Readonly" : ""}Array<any> | undefined): ListValue;
593
+ unwrap(message: ${options.useReadonlyTypes ? "any" : "ListValue"}): Array<any>;
594
+ }
595
+ `);
596
+ const FieldMaskWrapperFns = (0, ts_poet_1.conditionalOutput)("FieldMaskWrapperFns", (0, ts_poet_1.code) `
597
+ ${maybeExport} interface FieldMaskWrapperFns {
598
+ wrap(paths: ${options.useReadonlyTypes ? "readonly" : ""} string[]): FieldMask;
599
+ unwrap(message: ${options.useReadonlyTypes ? "any" : "FieldMask"}): string[] ${options.useOptionals === "all" ? "| undefined" : ""};
600
+ }
601
+ `);
602
+ return {
603
+ MessageFns,
604
+ ExtensionFns,
605
+ ExtensionHolder,
606
+ StructWrapperFns,
607
+ AnyValueWrapperFns,
608
+ ListValueWrapperFns,
609
+ FieldMaskWrapperFns,
610
+ };
611
+ }
464
612
  function makeObjectIdMethods() {
465
613
  const mongodb = (0, ts_poet_1.imp)("mongodb*mongodb");
466
614
  const fromProtoObjectId = (0, ts_poet_1.conditionalOutput)("fromProtoObjectId", (0, ts_poet_1.code) `
@@ -882,7 +1030,7 @@ function generateDecode(ctx, fullName, messageDesc) {
882
1030
  messageDesc.field.forEach((field) => {
883
1031
  const fieldName = (0, utils_1.getFieldName)(field, options);
884
1032
  const messageProperty = (0, utils_1.getPropertyAccessor)("message", fieldName);
885
- chunks.push((0, ts_poet_1.code) `case ${field.number}:`);
1033
+ chunks.push((0, ts_poet_1.code) `case ${field.number}: {`);
886
1034
  const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
887
1035
  const tagCheck = (0, ts_poet_1.code) `
888
1036
  if (tag !== ${tag}) {
@@ -935,11 +1083,23 @@ function generateDecode(ctx, fullName, messageDesc) {
935
1083
  }`
936
1084
  : "";
937
1085
  if ((0, types_1.packedType)(field.type) === undefined) {
938
- chunks.push((0, ts_poet_1.code) `
939
- ${tagCheck}
940
- ${initializerSnippet}
941
- ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
942
- `);
1086
+ if (options.useOptionals === "all") {
1087
+ chunks.push((0, ts_poet_1.code) `
1088
+ ${tagCheck}
1089
+ ${initializerSnippet}
1090
+ const el = ${readSnippet};
1091
+ if (el !== undefined) {
1092
+ ${messageProperty}${maybeNonNullAssertion}.push(el);
1093
+ }
1094
+ `);
1095
+ }
1096
+ else {
1097
+ chunks.push((0, ts_poet_1.code) `
1098
+ ${tagCheck}
1099
+ ${initializerSnippet}
1100
+ ${messageProperty}${maybeNonNullAssertion}.push(${readSnippet});
1101
+ `);
1102
+ }
943
1103
  }
944
1104
  else {
945
1105
  const packedTag = ((field.number << 3) | 2) >>> 0;
@@ -962,7 +1122,7 @@ function generateDecode(ctx, fullName, messageDesc) {
962
1122
  }
963
1123
 
964
1124
  break;
965
- `);
1125
+ }`);
966
1126
  }
967
1127
  }
968
1128
  }
@@ -983,7 +1143,7 @@ function generateDecode(ctx, fullName, messageDesc) {
983
1143
  `);
984
1144
  }
985
1145
  if (!(0, types_1.isRepeated)(field) || (0, types_1.packedType)(field.type) === undefined) {
986
- chunks.push((0, ts_poet_1.code) `continue;`);
1146
+ chunks.push((0, ts_poet_1.code) `continue; }`);
987
1147
  }
988
1148
  });
989
1149
  chunks.push((0, ts_poet_1.code) `}`);
@@ -197,9 +197,9 @@ function encodedOptionsToOptions(ctx, extendee, encodedOptions) {
197
197
  // getExtensionValue is to decode the encoded value into a message, but this
198
198
  // Message.decode(...) method is not always included in the generated code.
199
199
  // We should fix this so that we can always create an option definition by
200
- // somehow premptively decoding the encoded value and then inserting it into
201
- // the option defintion.
202
- // please refer to inteegration/options-types-only to see this in action
200
+ // somehow preemptively decoding the encoded value and then inserting it into
201
+ // the option definition.
202
+ // please refer to integration/options-types-only to see this in action
203
203
  resultOptions.push(getExtensionValue(ctx, extension, value));
204
204
  }
205
205
  }
@@ -69,10 +69,10 @@ export default class SourceInfo implements SourceDescription {
69
69
  get leadingComments(): string;
70
70
  /** Documentation is unclear about what exactly this is */
71
71
  get trailingComments(): string;
72
- /** Detached comments are those preceeding but separated by a blank non-comment line */
72
+ /** Detached comments are those preceding but separated by a blank non-comment line */
73
73
  get leadingDetachedComments(): string[];
74
- /** Return the source info for the field id and index specficied */
74
+ /** Return the source info for the field id and index specified */
75
75
  lookup(type: FieldID, index?: number): SourceDescription;
76
- /** Returns a new SourceInfo class representing the field id and index specficied */
76
+ /** Returns a new SourceInfo class representing the field id and index specified */
77
77
  open(type: FieldID, index: number): SourceInfo;
78
78
  }
@@ -80,18 +80,18 @@ class SourceInfo {
80
80
  get trailingComments() {
81
81
  return this.selfDescription.trailingComments;
82
82
  }
83
- /** Detached comments are those preceeding but separated by a blank non-comment line */
83
+ /** Detached comments are those preceding but separated by a blank non-comment line */
84
84
  get leadingDetachedComments() {
85
85
  return this.selfDescription.leadingDetachedComments;
86
86
  }
87
- /** Return the source info for the field id and index specficied */
87
+ /** Return the source info for the field id and index specified */
88
88
  lookup(type, index) {
89
89
  if (index === undefined) {
90
90
  return this.sourceCode[`${type}`] || new EmptyDescription();
91
91
  }
92
92
  return this.sourceCode[`${type}.${index}`] || new EmptyDescription();
93
93
  }
94
- /** Returns a new SourceInfo class representing the field id and index specficied */
94
+ /** Returns a new SourceInfo class representing the field id and index specified */
95
95
  open(type, index) {
96
96
  const prefix = `${type}.${index}.`;
97
97
  const map = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-proto",
3
- "version": "2.1.0",
3
+ "version": "2.2.1",
4
4
  "description": "",
5
5
  "main": "build/src/plugin.js",
6
6
  "repository": "github:stephenh/ts-proto",