ts-proto 1.138.0 → 1.140.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 +5 -1
- package/build/enums.js +23 -7
- package/build/generate-grpc-web.js +59 -6
- package/build/generate-services.js +3 -3
- package/build/generate-struct-wrappers.js +318 -0
- package/build/main.js +104 -217
- package/build/options.js +1 -0
- package/package.json +3 -3
package/README.markdown
CHANGED
|
@@ -352,6 +352,10 @@ Generated code will be placed in the Gradle build directory.
|
|
|
352
352
|
|
|
353
353
|
- With `--ts_proto_opt=unrecognizedEnum=false` enums will not contain an `UNRECOGNIZED` key with value of -1.
|
|
354
354
|
|
|
355
|
+
- With `--ts_proto_opt=removeEnumPrefix=true` generated enums will have the enum name removed from members.
|
|
356
|
+
|
|
357
|
+
`FooBar.FOO_BAR_BAZ = "FOO_BAR_BAZ"` will generate `FooBar.BAZ = "FOO_BAR_BAZ"`
|
|
358
|
+
|
|
355
359
|
- With `--ts_proto_opt=lowerCaseServiceMethods=true`, the method names of service methods will be lowered/camel-case, i.e. `service.findFoo` instead of `service.FindFoo`.
|
|
356
360
|
|
|
357
361
|
- With `--ts_proto_opt=snakeToCamel=false`, fields will be kept snake case in both the message keys and the `toJSON` / `fromJSON` methods.
|
|
@@ -400,7 +404,7 @@ Generated code will be placed in the Gradle build directory.
|
|
|
400
404
|
|
|
401
405
|
- With `--ts_proto_opt=nestJs=true`, the defaults will change to generate [NestJS protobuf](https://docs.nestjs.com/microservices/grpc) friendly types & service interfaces that can be used in both the client-side and server-side of NestJS protobuf implementations. See the [nestjs readme](NESTJS.markdown) for more information and implementation examples.
|
|
402
406
|
|
|
403
|
-
Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, and `
|
|
407
|
+
Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, `lowerCaseServiceMethods` will be true and `outputServices` will be ignored.
|
|
404
408
|
|
|
405
409
|
Note that `addGrpcMetadata`, `addNestjsRestParameter` and `returnObservable` will still be false.
|
|
406
410
|
|
package/build/enums.js
CHANGED
|
@@ -23,8 +23,10 @@ function generateEnum(ctx, fullName, enumDesc, sourceInfo) {
|
|
|
23
23
|
enumDesc.value.forEach((valueDesc, index) => {
|
|
24
24
|
var _a;
|
|
25
25
|
const info = sourceInfo.lookup(sourceInfo_1.Fields.enum.value, index);
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
const valueName = getValueName(ctx, fullName, valueDesc);
|
|
27
|
+
const memberName = getMemberName(ctx, fullName, valueDesc);
|
|
28
|
+
(0, utils_1.maybeAddComment)(info, chunks, (_a = valueDesc.options) === null || _a === void 0 ? void 0 : _a.deprecated, `${memberName} - `);
|
|
29
|
+
chunks.push((0, ts_poet_1.code) `${memberName} ${delimiter} ${options.stringEnums ? `"${valueName}"` : valueDesc.number.toString()},`);
|
|
28
30
|
});
|
|
29
31
|
if (options.unrecognizedEnum)
|
|
30
32
|
chunks.push((0, ts_poet_1.code) `
|
|
@@ -60,10 +62,12 @@ function generateEnumFromJson(ctx, fullName, enumDesc) {
|
|
|
60
62
|
chunks.push((0, ts_poet_1.code) `export function ${(0, ts_poet_1.def)(functionName)}(object: any): ${fullName} {`);
|
|
61
63
|
chunks.push((0, ts_poet_1.code) `switch (object) {`);
|
|
62
64
|
for (const valueDesc of enumDesc.value) {
|
|
65
|
+
const memberName = getMemberName(ctx, fullName, valueDesc);
|
|
66
|
+
const valueName = getValueName(ctx, fullName, valueDesc);
|
|
63
67
|
chunks.push((0, ts_poet_1.code) `
|
|
64
68
|
case ${valueDesc.number}:
|
|
65
|
-
case "${
|
|
66
|
-
return ${fullName}.${
|
|
69
|
+
case "${valueName}":
|
|
70
|
+
return ${fullName}.${memberName};
|
|
67
71
|
`);
|
|
68
72
|
}
|
|
69
73
|
if (options.unrecognizedEnum) {
|
|
@@ -95,10 +99,13 @@ function generateEnumToJson(ctx, fullName, enumDesc) {
|
|
|
95
99
|
chunks.push((0, ts_poet_1.code) `switch (object) {`);
|
|
96
100
|
for (const valueDesc of enumDesc.value) {
|
|
97
101
|
if (ctx.options.useNumericEnumForJson) {
|
|
98
|
-
|
|
102
|
+
const memberName = getMemberName(ctx, fullName, valueDesc);
|
|
103
|
+
chunks.push((0, ts_poet_1.code) `case ${fullName}.${memberName}: return ${valueDesc.number};`);
|
|
99
104
|
}
|
|
100
105
|
else {
|
|
101
|
-
|
|
106
|
+
const memberName = getMemberName(ctx, fullName, valueDesc);
|
|
107
|
+
const valueName = getValueName(ctx, fullName, valueDesc);
|
|
108
|
+
chunks.push((0, ts_poet_1.code) `case ${fullName}.${memberName}: return "${valueName}";`);
|
|
102
109
|
}
|
|
103
110
|
}
|
|
104
111
|
if (options.unrecognizedEnum) {
|
|
@@ -137,7 +144,7 @@ function generateEnumToNumber(ctx, fullName, enumDesc) {
|
|
|
137
144
|
chunks.push((0, ts_poet_1.code) `export function ${(0, ts_poet_1.def)(functionName)}(object: ${fullName}): number {`);
|
|
138
145
|
chunks.push((0, ts_poet_1.code) `switch (object) {`);
|
|
139
146
|
for (const valueDesc of enumDesc.value) {
|
|
140
|
-
chunks.push((0, ts_poet_1.code) `case ${fullName}.${valueDesc
|
|
147
|
+
chunks.push((0, ts_poet_1.code) `case ${fullName}.${getMemberName(ctx, fullName, valueDesc)}: return ${valueDesc.number};`);
|
|
141
148
|
}
|
|
142
149
|
if (options.unrecognizedEnum) {
|
|
143
150
|
chunks.push((0, ts_poet_1.code) `
|
|
@@ -158,3 +165,12 @@ function generateEnumToNumber(ctx, fullName, enumDesc) {
|
|
|
158
165
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
|
159
166
|
}
|
|
160
167
|
exports.generateEnumToNumber = generateEnumToNumber;
|
|
168
|
+
function getMemberName(ctx, fullName, valueDesc) {
|
|
169
|
+
if (ctx.options.removeEnumPrefix) {
|
|
170
|
+
return valueDesc.name.replace(`${(0, case_1.camelToSnake)(fullName)}_`, "");
|
|
171
|
+
}
|
|
172
|
+
return valueDesc.name;
|
|
173
|
+
}
|
|
174
|
+
function getValueName(ctx, fullName, valueDesc) {
|
|
175
|
+
return valueDesc.name;
|
|
176
|
+
}
|
|
@@ -39,6 +39,8 @@ exports.generateGrpcClientImpl = generateGrpcClientImpl;
|
|
|
39
39
|
/** Creates the RPC methods that client code actually calls. */
|
|
40
40
|
function generateRpcMethod(ctx, serviceDesc, methodDesc) {
|
|
41
41
|
(0, utils_1.assertInstanceOf)(methodDesc, utils_1.FormattedMethodDescriptor);
|
|
42
|
+
const { options } = ctx;
|
|
43
|
+
const { useAbortSignal } = options;
|
|
42
44
|
const requestMessage = (0, types_1.rawRequestType)(ctx, methodDesc);
|
|
43
45
|
const inputType = (0, types_1.requestType)(ctx, methodDesc, true);
|
|
44
46
|
const returns = (0, types_1.responsePromiseOrObservable)(ctx, methodDesc);
|
|
@@ -47,6 +49,7 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
|
|
|
47
49
|
${methodDesc.formattedName}(
|
|
48
50
|
request: ${inputType},
|
|
49
51
|
metadata?: grpc.Metadata,
|
|
52
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
50
53
|
): ${returns} {
|
|
51
54
|
throw new Error('ts-proto does not yet support client streaming!');
|
|
52
55
|
}
|
|
@@ -57,10 +60,12 @@ function generateRpcMethod(ctx, serviceDesc, methodDesc) {
|
|
|
57
60
|
${methodDesc.formattedName}(
|
|
58
61
|
request: ${inputType},
|
|
59
62
|
metadata?: grpc.Metadata,
|
|
63
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
60
64
|
): ${returns} {
|
|
61
65
|
return this.rpc.${method}(
|
|
62
66
|
${methodDescName(serviceDesc, methodDesc)},
|
|
63
67
|
${requestMessage}.fromPartial(request),
|
|
68
|
+
${useAbortSignal ? "abortSignal," : ""}
|
|
64
69
|
metadata,
|
|
65
70
|
);
|
|
66
71
|
}
|
|
@@ -145,6 +150,8 @@ exports.addGrpcWebMisc = addGrpcWebMisc;
|
|
|
145
150
|
/** Makes an `Rpc` interface to decouple from the low-level grpc-web `grpc.invoke and grpc.unary`/etc. methods. */
|
|
146
151
|
function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
|
|
147
152
|
const chunks = [];
|
|
153
|
+
const { options } = ctx;
|
|
154
|
+
const { useAbortSignal } = options;
|
|
148
155
|
chunks.push((0, ts_poet_1.code) `interface Rpc {`);
|
|
149
156
|
const wrapper = returnObservable ? (0, types_1.observableType)(ctx) : "Promise";
|
|
150
157
|
chunks.push((0, ts_poet_1.code) `
|
|
@@ -152,6 +159,7 @@ function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
|
|
|
152
159
|
methodDesc: T,
|
|
153
160
|
request: any,
|
|
154
161
|
metadata: grpc.Metadata | undefined,
|
|
162
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
155
163
|
): ${wrapper}<any>;
|
|
156
164
|
`);
|
|
157
165
|
if (hasStreamingMethods) {
|
|
@@ -160,6 +168,7 @@ function generateGrpcWebRpcType(ctx, returnObservable, hasStreamingMethods) {
|
|
|
160
168
|
methodDesc: T,
|
|
161
169
|
request: any,
|
|
162
170
|
metadata: grpc.Metadata | undefined,
|
|
171
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
163
172
|
): ${(0, types_1.observableType)(ctx)}<any>;
|
|
164
173
|
`);
|
|
165
174
|
}
|
|
@@ -201,11 +210,23 @@ function generateGrpcWebImpl(ctx, returnObservable, hasStreamingMethods) {
|
|
|
201
210
|
return (0, ts_poet_1.joinCode)(chunks, { trim: false });
|
|
202
211
|
}
|
|
203
212
|
function createPromiseUnaryMethod(ctx) {
|
|
213
|
+
const { options } = ctx;
|
|
214
|
+
const { useAbortSignal } = options;
|
|
215
|
+
const maybeAbortSignal = useAbortSignal
|
|
216
|
+
? `
|
|
217
|
+
const abortHandler = () => {
|
|
218
|
+
client.close();
|
|
219
|
+
reject(new Error("Aborted"));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
|
|
223
|
+
: "";
|
|
204
224
|
return (0, ts_poet_1.code) `
|
|
205
225
|
unary<T extends UnaryMethodDefinitionish>(
|
|
206
226
|
methodDesc: T,
|
|
207
227
|
_request: any,
|
|
208
|
-
metadata: grpc.Metadata | undefined
|
|
228
|
+
metadata: grpc.Metadata | undefined,
|
|
229
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
209
230
|
): Promise<any> {
|
|
210
231
|
const request = { ..._request, ...methodDesc.requestType };
|
|
211
232
|
const maybeCombinedMetadata =
|
|
@@ -213,7 +234,7 @@ function createPromiseUnaryMethod(ctx) {
|
|
|
213
234
|
? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
|
|
214
235
|
: metadata || this.options.metadata;
|
|
215
236
|
return new Promise((resolve, reject) => {
|
|
216
|
-
|
|
237
|
+
${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
|
|
217
238
|
request,
|
|
218
239
|
host: this.host,
|
|
219
240
|
metadata: maybeCombinedMetadata,
|
|
@@ -228,16 +249,29 @@ function createPromiseUnaryMethod(ctx) {
|
|
|
228
249
|
}
|
|
229
250
|
},
|
|
230
251
|
});
|
|
252
|
+
|
|
253
|
+
${maybeAbortSignal}
|
|
231
254
|
});
|
|
232
255
|
}
|
|
233
256
|
`;
|
|
234
257
|
}
|
|
235
258
|
function createObservableUnaryMethod(ctx) {
|
|
259
|
+
const { options } = ctx;
|
|
260
|
+
const { useAbortSignal } = options;
|
|
261
|
+
const maybeAbortSignal = useAbortSignal
|
|
262
|
+
? `
|
|
263
|
+
const abortHandler = () => {
|
|
264
|
+
observer.error("Aborted");
|
|
265
|
+
client.close();
|
|
266
|
+
};
|
|
267
|
+
if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
|
|
268
|
+
: "";
|
|
236
269
|
return (0, ts_poet_1.code) `
|
|
237
270
|
unary<T extends UnaryMethodDefinitionish>(
|
|
238
271
|
methodDesc: T,
|
|
239
272
|
_request: any,
|
|
240
|
-
metadata: grpc.Metadata | undefined
|
|
273
|
+
metadata: grpc.Metadata | undefined,
|
|
274
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
241
275
|
): ${(0, types_1.observableType)(ctx)}<any> {
|
|
242
276
|
const request = { ..._request, ...methodDesc.requestType };
|
|
243
277
|
const maybeCombinedMetadata =
|
|
@@ -245,7 +279,7 @@ function createObservableUnaryMethod(ctx) {
|
|
|
245
279
|
? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
|
|
246
280
|
: metadata || this.options.metadata;
|
|
247
281
|
return new Observable(observer => {
|
|
248
|
-
${grpc}.unary(methodDesc, {
|
|
282
|
+
${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
|
|
249
283
|
request,
|
|
250
284
|
host: this.host,
|
|
251
285
|
metadata: maybeCombinedMetadata,
|
|
@@ -261,16 +295,31 @@ function createObservableUnaryMethod(ctx) {
|
|
|
261
295
|
}
|
|
262
296
|
},
|
|
263
297
|
});
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
${maybeAbortSignal}
|
|
301
|
+
|
|
264
302
|
}).pipe(${take}(1));
|
|
265
303
|
}
|
|
266
304
|
`;
|
|
267
305
|
}
|
|
268
306
|
function createInvokeMethod(ctx) {
|
|
307
|
+
const { options } = ctx;
|
|
308
|
+
const { useAbortSignal } = options;
|
|
309
|
+
const maybeAbortSignal = useAbortSignal
|
|
310
|
+
? `
|
|
311
|
+
const abortHandler = () => {
|
|
312
|
+
observer.error("Aborted");
|
|
313
|
+
client.close();
|
|
314
|
+
};
|
|
315
|
+
if (abortSignal) abortSignal.addEventListener("abort", abortHandler);`
|
|
316
|
+
: "";
|
|
269
317
|
return (0, ts_poet_1.code) `
|
|
270
318
|
invoke<T extends UnaryMethodDefinitionish>(
|
|
271
319
|
methodDesc: T,
|
|
272
320
|
_request: any,
|
|
273
|
-
metadata: grpc.Metadata | undefined
|
|
321
|
+
metadata: grpc.Metadata | undefined,
|
|
322
|
+
${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
|
|
274
323
|
): ${(0, types_1.observableType)(ctx)}<any> {
|
|
275
324
|
const upStreamCodes = this.options.upStreamRetryCodes || [];
|
|
276
325
|
const DEFAULT_TIMEOUT_TIME: number = 3_000;
|
|
@@ -301,7 +350,11 @@ function createInvokeMethod(ctx) {
|
|
|
301
350
|
}
|
|
302
351
|
},
|
|
303
352
|
});
|
|
304
|
-
observer.add(() =>
|
|
353
|
+
observer.add(() => {
|
|
354
|
+
if (!observer.closed) return client.close()
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
${maybeAbortSignal}
|
|
305
358
|
});
|
|
306
359
|
upStream();
|
|
307
360
|
}).pipe(${share}());
|
|
@@ -39,9 +39,6 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
|
|
|
39
39
|
const partialInput = options.outputClientImpl === "grpc-web";
|
|
40
40
|
const inputType = (0, types_1.requestType)(ctx, methodDesc, partialInput);
|
|
41
41
|
params.push((0, ts_poet_1.code) `request: ${inputType}`);
|
|
42
|
-
if (options.useAbortSignal) {
|
|
43
|
-
params.push((0, ts_poet_1.code) `abortSignal?: AbortSignal`);
|
|
44
|
-
}
|
|
45
42
|
// Use metadata as last argument for interface only configuration
|
|
46
43
|
if (options.outputClientImpl === "grpc-web") {
|
|
47
44
|
// We have to use grpc.Metadata where grpc will come from @improbable-eng
|
|
@@ -56,6 +53,9 @@ function generateService(ctx, fileDesc, sourceInfo, serviceDesc) {
|
|
|
56
53
|
const Metadata = (0, ts_poet_1.imp)(options.metadataType);
|
|
57
54
|
params.push((0, ts_poet_1.code) `metadata?: ${Metadata}`);
|
|
58
55
|
}
|
|
56
|
+
if (options.useAbortSignal) {
|
|
57
|
+
params.push((0, ts_poet_1.code) `abortSignal?: AbortSignal`);
|
|
58
|
+
}
|
|
59
59
|
if (options.addNestjsRestParameter) {
|
|
60
60
|
params.push((0, ts_poet_1.code) `...rest: any`);
|
|
61
61
|
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateUnwrapShallow = exports.generateWrapShallow = exports.generateUnwrapDeep = exports.generateWrapDeep = exports.isWrapperType = void 0;
|
|
4
|
+
const ts_poet_1 = require("ts-poet");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const options_1 = require("./options");
|
|
7
|
+
/** Whether we need to generate `.wrap` and `.unwrap` methods for the given type. */
|
|
8
|
+
function isWrapperType(fullProtoTypeName) {
|
|
9
|
+
return ((0, types_1.isStructTypeName)(fullProtoTypeName) ||
|
|
10
|
+
(0, types_1.isAnyValueTypeName)(fullProtoTypeName) ||
|
|
11
|
+
(0, types_1.isListValueTypeName)(fullProtoTypeName) ||
|
|
12
|
+
(0, types_1.isFieldMaskTypeName)(fullProtoTypeName));
|
|
13
|
+
}
|
|
14
|
+
exports.isWrapperType = isWrapperType;
|
|
15
|
+
/**
|
|
16
|
+
* Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
|
|
17
|
+
*
|
|
18
|
+
* We do this deeply b/c NestJS does not invoke wrappers recursively.
|
|
19
|
+
*/
|
|
20
|
+
function generateWrapDeep(ctx, fullProtoTypeName, fieldNames) {
|
|
21
|
+
const chunks = [];
|
|
22
|
+
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
23
|
+
let setStatement = "struct.fields[key] = Value.wrap(object[key]);";
|
|
24
|
+
if (ctx.options.useMapType) {
|
|
25
|
+
setStatement = "struct.fields.set(key, Value.wrap(object[key]));";
|
|
26
|
+
}
|
|
27
|
+
chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
|
|
28
|
+
const struct = createBaseStruct();
|
|
29
|
+
if (object !== undefined) {
|
|
30
|
+
Object.keys(object).forEach(key => {
|
|
31
|
+
${setStatement}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return struct;
|
|
35
|
+
}`);
|
|
36
|
+
}
|
|
37
|
+
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
38
|
+
// Turn ts-proto representation --> proto representation
|
|
39
|
+
chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
|
|
40
|
+
const result = {} as any;
|
|
41
|
+
if (value === null) {
|
|
42
|
+
result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
|
|
43
|
+
} else if (typeof value === 'boolean') {
|
|
44
|
+
result.${fieldNames.boolValue} = value;
|
|
45
|
+
} else if (typeof value === 'number') {
|
|
46
|
+
result.${fieldNames.numberValue} = value;
|
|
47
|
+
} else if (typeof value === 'string') {
|
|
48
|
+
result.${fieldNames.stringValue} = value;
|
|
49
|
+
} else if (Array.isArray(value)) {
|
|
50
|
+
result.${fieldNames.listValue} = ListValue.wrap(value);
|
|
51
|
+
} else if (typeof value === 'object') {
|
|
52
|
+
result.${fieldNames.structValue} = Struct.wrap(value);
|
|
53
|
+
} else if (typeof value !== 'undefined') {
|
|
54
|
+
throw new Error('Unsupported any value type: ' + typeof value);
|
|
55
|
+
}
|
|
56
|
+
return result;
|
|
57
|
+
}`);
|
|
58
|
+
}
|
|
59
|
+
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
60
|
+
const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : "";
|
|
61
|
+
chunks.push((0, ts_poet_1.code) `wrap(array: ${maybeReadyOnly}Array<any> | undefined): ListValue {
|
|
62
|
+
const result = createBaseListValue()${maybeAsAny(ctx.options)};
|
|
63
|
+
result.values = (array ?? []).map(Value.wrap);
|
|
64
|
+
return result;
|
|
65
|
+
}`);
|
|
66
|
+
}
|
|
67
|
+
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
68
|
+
chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
|
|
69
|
+
const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
|
|
70
|
+
result.paths = paths;
|
|
71
|
+
return result;
|
|
72
|
+
}`);
|
|
73
|
+
}
|
|
74
|
+
return chunks;
|
|
75
|
+
}
|
|
76
|
+
exports.generateWrapDeep = generateWrapDeep;
|
|
77
|
+
/**
|
|
78
|
+
* Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
|
|
79
|
+
*
|
|
80
|
+
* We do this deeply b/c NestJS does not invoke wrappers recursively.
|
|
81
|
+
*/
|
|
82
|
+
function generateUnwrapDeep(ctx, fullProtoTypeName, fieldNames) {
|
|
83
|
+
const chunks = [];
|
|
84
|
+
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
85
|
+
if (ctx.options.useMapType) {
|
|
86
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
87
|
+
const object: { [key: string]: any } = {};
|
|
88
|
+
[...message.fields.keys()].forEach((key) => {
|
|
89
|
+
object[key] = Value.unwrap(message.fields.get(key));
|
|
90
|
+
});
|
|
91
|
+
return object;
|
|
92
|
+
}`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
96
|
+
const object: { [key: string]: any } = {};
|
|
97
|
+
if (message.fields) {
|
|
98
|
+
Object.keys(message.fields).forEach(key => {
|
|
99
|
+
object[key] = Value.unwrap(message.fields[key]);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return object;
|
|
103
|
+
}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
107
|
+
// We check hasOwnProperty because the incoming `message` has been serde-ing
|
|
108
|
+
// by the NestJS/protobufjs runtime, and so has a base class with default values
|
|
109
|
+
// that throw off the simpler checks we do in generateUnwrapShallow
|
|
110
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined {
|
|
111
|
+
if (message?.hasOwnProperty('${fieldNames.stringValue}') && message.${fieldNames.stringValue} !== undefined) {
|
|
112
|
+
return message.${fieldNames.stringValue};
|
|
113
|
+
} else if (message?.hasOwnProperty('${fieldNames.numberValue}') && message?.${fieldNames.numberValue} !== undefined) {
|
|
114
|
+
return message.${fieldNames.numberValue};
|
|
115
|
+
} else if (message?.hasOwnProperty('${fieldNames.boolValue}') && message?.${fieldNames.boolValue} !== undefined) {
|
|
116
|
+
return message.${fieldNames.boolValue};
|
|
117
|
+
} else if (message?.hasOwnProperty('${fieldNames.structValue}') && message?.${fieldNames.structValue} !== undefined) {
|
|
118
|
+
return Struct.unwrap(message.${fieldNames.structValue} as any);
|
|
119
|
+
} else if (message?.hasOwnProperty('${fieldNames.listValue}') && message?.${fieldNames.listValue} !== undefined) {
|
|
120
|
+
return ListValue.unwrap(message.${fieldNames.listValue});
|
|
121
|
+
} else if (message?.hasOwnProperty('${fieldNames.nullValue}') && message?.${fieldNames.nullValue} !== undefined) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return undefined;
|
|
125
|
+
}`);
|
|
126
|
+
}
|
|
127
|
+
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
128
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
|
|
129
|
+
if (message?.hasOwnProperty('values') && Array.isArray(message.values)) {
|
|
130
|
+
return message.values.map(Value.unwrap);
|
|
131
|
+
} else {
|
|
132
|
+
return message as any;
|
|
133
|
+
}
|
|
134
|
+
}`);
|
|
135
|
+
}
|
|
136
|
+
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
137
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
|
|
138
|
+
return message.paths;
|
|
139
|
+
}`);
|
|
140
|
+
}
|
|
141
|
+
return chunks;
|
|
142
|
+
}
|
|
143
|
+
exports.generateUnwrapDeep = generateUnwrapDeep;
|
|
144
|
+
/**
|
|
145
|
+
* Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages.
|
|
146
|
+
*
|
|
147
|
+
* We do this shallow's b/c ts-proto's encode methods handle the recursion.
|
|
148
|
+
*/
|
|
149
|
+
function generateWrapShallow(ctx, fullProtoTypeName, fieldNames) {
|
|
150
|
+
const chunks = [];
|
|
151
|
+
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
152
|
+
let setStatement = "struct.fields[key] = object[key];";
|
|
153
|
+
if (ctx.options.useMapType) {
|
|
154
|
+
setStatement = "struct.fields.set(key, object[key]);";
|
|
155
|
+
}
|
|
156
|
+
chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
|
|
157
|
+
const struct = createBaseStruct();
|
|
158
|
+
if (object !== undefined) {
|
|
159
|
+
Object.keys(object).forEach(key => {
|
|
160
|
+
${setStatement}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return struct;
|
|
164
|
+
}`);
|
|
165
|
+
}
|
|
166
|
+
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
167
|
+
if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
|
|
168
|
+
chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
|
|
169
|
+
const result = createBaseValue()${maybeAsAny(ctx.options)};
|
|
170
|
+
if (value === null) {
|
|
171
|
+
result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE};
|
|
172
|
+
} else if (typeof value === 'boolean') {
|
|
173
|
+
result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value};
|
|
174
|
+
} else if (typeof value === 'number') {
|
|
175
|
+
result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value};
|
|
176
|
+
} else if (typeof value === 'string') {
|
|
177
|
+
result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value};
|
|
178
|
+
} else if (Array.isArray(value)) {
|
|
179
|
+
result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value};
|
|
180
|
+
} else if (typeof value === 'object') {
|
|
181
|
+
result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value};
|
|
182
|
+
} else if (typeof value !== 'undefined') {
|
|
183
|
+
throw new Error('Unsupported any value type: ' + typeof value);
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
|
|
190
|
+
const result = createBaseValue()${maybeAsAny(ctx.options)};
|
|
191
|
+
if (value === null) {
|
|
192
|
+
result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
|
|
193
|
+
} else if (typeof value === 'boolean') {
|
|
194
|
+
result.${fieldNames.boolValue} = value;
|
|
195
|
+
} else if (typeof value === 'number') {
|
|
196
|
+
result.${fieldNames.numberValue} = value;
|
|
197
|
+
} else if (typeof value === 'string') {
|
|
198
|
+
result.${fieldNames.stringValue} = value;
|
|
199
|
+
} else if (Array.isArray(value)) {
|
|
200
|
+
result.${fieldNames.listValue} = value;
|
|
201
|
+
} else if (typeof value === 'object') {
|
|
202
|
+
result.${fieldNames.structValue} = value;
|
|
203
|
+
} else if (typeof value !== 'undefined') {
|
|
204
|
+
throw new Error('Unsupported any value type: ' + typeof value);
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
211
|
+
const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : "";
|
|
212
|
+
chunks.push((0, ts_poet_1.code) `wrap(array: ${maybeReadyOnly}Array<any> | undefined): ListValue {
|
|
213
|
+
const result = createBaseListValue()${maybeAsAny(ctx.options)};
|
|
214
|
+
result.values = array ?? [];
|
|
215
|
+
return result;
|
|
216
|
+
}`);
|
|
217
|
+
}
|
|
218
|
+
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
219
|
+
chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
|
|
220
|
+
const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
|
|
221
|
+
result.paths = paths;
|
|
222
|
+
return result;
|
|
223
|
+
}`);
|
|
224
|
+
}
|
|
225
|
+
return chunks;
|
|
226
|
+
}
|
|
227
|
+
exports.generateWrapShallow = generateWrapShallow;
|
|
228
|
+
/**
|
|
229
|
+
* Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation.
|
|
230
|
+
*
|
|
231
|
+
* We do this shallowly b/c ts-proto's decode methods handle recursion.
|
|
232
|
+
*/
|
|
233
|
+
function generateUnwrapShallow(ctx, fullProtoTypeName, fieldNames) {
|
|
234
|
+
const chunks = [];
|
|
235
|
+
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
236
|
+
if (ctx.options.useMapType) {
|
|
237
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
238
|
+
const object: { [key: string]: any } = {};
|
|
239
|
+
[...message.fields.keys()].forEach((key) => {
|
|
240
|
+
object[key] = message.fields.get(key);
|
|
241
|
+
});
|
|
242
|
+
return object;
|
|
243
|
+
}`);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
247
|
+
const object: { [key: string]: any } = {};
|
|
248
|
+
if (message.fields) {
|
|
249
|
+
Object.keys(message.fields).forEach(key => {
|
|
250
|
+
object[key] = message.fields[key];
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return object;
|
|
254
|
+
}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
258
|
+
if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
|
|
259
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
|
|
260
|
+
if (message.kind?.$case === '${fieldNames.nullValue}') {
|
|
261
|
+
return null;
|
|
262
|
+
} else if (message.kind?.$case === '${fieldNames.numberValue}') {
|
|
263
|
+
return message.kind?.${fieldNames.numberValue};
|
|
264
|
+
} else if (message.kind?.$case === '${fieldNames.stringValue}') {
|
|
265
|
+
return message.kind?.${fieldNames.stringValue};
|
|
266
|
+
} else if (message.kind?.$case === '${fieldNames.boolValue}') {
|
|
267
|
+
return message.kind?.${fieldNames.boolValue};
|
|
268
|
+
} else if (message.kind?.$case === '${fieldNames.structValue}') {
|
|
269
|
+
return message.kind?.${fieldNames.structValue};
|
|
270
|
+
} else if (message.kind?.$case === '${fieldNames.listValue}') {
|
|
271
|
+
return message.kind?.${fieldNames.listValue};
|
|
272
|
+
} else {
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
}`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: any): string | number | boolean | Object | null | Array<any> | undefined {
|
|
279
|
+
if (message.${fieldNames.stringValue} !== undefined) {
|
|
280
|
+
return message.${fieldNames.stringValue};
|
|
281
|
+
} else if (message?.${fieldNames.numberValue} !== undefined) {
|
|
282
|
+
return message.${fieldNames.numberValue};
|
|
283
|
+
} else if (message?.${fieldNames.boolValue} !== undefined) {
|
|
284
|
+
return message.${fieldNames.boolValue};
|
|
285
|
+
} else if (message?.${fieldNames.structValue} !== undefined) {
|
|
286
|
+
return message.${fieldNames.structValue} as any;
|
|
287
|
+
} else if (message?.${fieldNames.listValue} !== undefined) {
|
|
288
|
+
return message.${fieldNames.listValue};
|
|
289
|
+
} else if (message?.${fieldNames.nullValue} !== undefined) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
297
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
|
|
298
|
+
if (message?.hasOwnProperty('values') && Array.isArray(message.values)) {
|
|
299
|
+
return message.values;
|
|
300
|
+
} else {
|
|
301
|
+
return message as any;
|
|
302
|
+
}
|
|
303
|
+
}`);
|
|
304
|
+
}
|
|
305
|
+
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
306
|
+
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
|
|
307
|
+
return message.paths;
|
|
308
|
+
}`);
|
|
309
|
+
}
|
|
310
|
+
return chunks;
|
|
311
|
+
}
|
|
312
|
+
exports.generateUnwrapShallow = generateUnwrapShallow;
|
|
313
|
+
function maybeReadonly(options) {
|
|
314
|
+
return options.useReadonlyTypes ? "readonly " : "";
|
|
315
|
+
}
|
|
316
|
+
function maybeAsAny(options) {
|
|
317
|
+
return options.useReadonlyTypes ? " as any" : "";
|
|
318
|
+
}
|
package/build/main.js
CHANGED
|
@@ -18,6 +18,7 @@ const ConditionalOutput_1 = require("ts-poet/build/ConditionalOutput");
|
|
|
18
18
|
const generate_grpc_js_1 = require("./generate-grpc-js");
|
|
19
19
|
const generate_generic_service_definition_1 = require("./generate-generic-service-definition");
|
|
20
20
|
const generate_nice_grpc_1 = require("./generate-nice-grpc");
|
|
21
|
+
const generate_struct_wrappers_1 = require("./generate-struct-wrappers");
|
|
21
22
|
function generateFile(ctx, fileDesc) {
|
|
22
23
|
var _a;
|
|
23
24
|
const { options, utils } = ctx;
|
|
@@ -72,11 +73,18 @@ function generateFile(ctx, fileDesc) {
|
|
|
72
73
|
chunks.push(makeProtobufTimestampWrapper());
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
// We add `nestJs` here because enough though it doesn't use our encode/decode methods
|
|
77
|
+
// for most/vanilla messages, we do generate static wrap/unwrap methods for the special
|
|
78
|
+
// Struct/Value/wrapper types and use the `wrappers[...]` to have NestJS know about them.
|
|
79
|
+
if (options.outputEncodeMethods || options.outputJsonMethods || options.outputTypeRegistry || options.nestJs) {
|
|
76
80
|
// then add the encoder/decoder/base instance
|
|
77
81
|
(0, visit_1.visit)(fileDesc, sourceInfo, (fullName, message, _sInfo, fullProtoTypeName) => {
|
|
78
82
|
const fullTypeName = (0, utils_1.maybePrefixPackage)(fileDesc, fullProtoTypeName);
|
|
79
|
-
|
|
83
|
+
const outputWrapAndUnwrap = (0, generate_struct_wrappers_1.isWrapperType)(fullTypeName);
|
|
84
|
+
// Only decode, fromPartial, and wrap use the createBase method
|
|
85
|
+
if (options.outputEncodeMethods || options.outputPartialMethods || outputWrapAndUnwrap) {
|
|
86
|
+
chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName));
|
|
87
|
+
}
|
|
80
88
|
const staticMembers = [];
|
|
81
89
|
if (options.outputTypeRegistry) {
|
|
82
90
|
staticMembers.push((0, ts_poet_1.code) `$type: '${fullTypeName}' as const`);
|
|
@@ -104,13 +112,21 @@ function generateFile(ctx, fileDesc) {
|
|
|
104
112
|
structValue: (0, case_1.maybeSnakeToCamel)("struct_value", ctx.options),
|
|
105
113
|
listValue: (0, case_1.maybeSnakeToCamel)("list_value", ctx.options),
|
|
106
114
|
};
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
if (options.nestJs) {
|
|
116
|
+
staticMembers.push(...(0, generate_struct_wrappers_1.generateWrapDeep)(ctx, fullTypeName, structFieldNames));
|
|
117
|
+
staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapDeep)(ctx, fullTypeName, structFieldNames));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
staticMembers.push(...(0, generate_struct_wrappers_1.generateWrapShallow)(ctx, fullTypeName, structFieldNames));
|
|
121
|
+
staticMembers.push(...(0, generate_struct_wrappers_1.generateUnwrapShallow)(ctx, fullTypeName, structFieldNames));
|
|
122
|
+
}
|
|
123
|
+
if (staticMembers.length > 0) {
|
|
124
|
+
chunks.push((0, ts_poet_1.code) `
|
|
125
|
+
export const ${(0, ts_poet_1.def)(fullName)} = {
|
|
126
|
+
${(0, ts_poet_1.joinCode)(staticMembers, { on: ",\n\n" })}
|
|
127
|
+
};
|
|
128
|
+
`);
|
|
129
|
+
}
|
|
114
130
|
if (options.outputTypeRegistry) {
|
|
115
131
|
const messageTypeRegistry = (0, utils_1.impFile)(options, "messageTypeRegistry@./typeRegistry");
|
|
116
132
|
chunks.push((0, ts_poet_1.code) `
|
|
@@ -119,6 +135,11 @@ function generateFile(ctx, fileDesc) {
|
|
|
119
135
|
}
|
|
120
136
|
}, options);
|
|
121
137
|
}
|
|
138
|
+
if (options.nestJs) {
|
|
139
|
+
if (fileDesc.messageType.find((message) => message.field.find(types_1.isStructType))) {
|
|
140
|
+
chunks.push(makeProtobufStructWrapper(options));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
122
143
|
let hasServerStreamingMethods = false;
|
|
123
144
|
let hasStreamingMethods = false;
|
|
124
145
|
(0, visit_1.visitServices)(fileDesc, sourceInfo, (serviceDesc, sInfo) => {
|
|
@@ -244,6 +265,15 @@ function makeProtobufTimestampWrapper() {
|
|
|
244
265
|
},
|
|
245
266
|
} as any;`;
|
|
246
267
|
}
|
|
268
|
+
function makeProtobufStructWrapper(options) {
|
|
269
|
+
const wrappers = (0, ts_poet_1.imp)("wrappers@protobufjs");
|
|
270
|
+
const Struct = (0, utils_1.impProto)(options, "google/protobuf/struct", "Struct");
|
|
271
|
+
return (0, ts_poet_1.code) `
|
|
272
|
+
${wrappers}['.google.protobuf.Struct'] = {
|
|
273
|
+
fromObject: ${Struct}.wrap,
|
|
274
|
+
toObject: ${Struct}.unwrap,
|
|
275
|
+
} as any;`;
|
|
276
|
+
}
|
|
247
277
|
function makeLongUtils(options, bytes) {
|
|
248
278
|
// Regardless of which `forceLong` config option we're using, we always use
|
|
249
279
|
// the `long` library to either represent or at least sanity-check 64-bit values
|
|
@@ -771,6 +801,53 @@ function generateDecode(ctx, fullName, messageDesc) {
|
|
|
771
801
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
772
802
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
|
773
803
|
}
|
|
804
|
+
/** Returns a generic writer.doSomething based on the basic type */
|
|
805
|
+
function getEncodeWriteSnippet(ctx, field) {
|
|
806
|
+
const { options, utils } = ctx;
|
|
807
|
+
if ((0, types_1.isEnum)(field) && options.stringEnums) {
|
|
808
|
+
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
809
|
+
const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
|
|
810
|
+
return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${toNumber}(${place}))`;
|
|
811
|
+
}
|
|
812
|
+
else if ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT) {
|
|
813
|
+
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
814
|
+
return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place}.toString())`;
|
|
815
|
+
}
|
|
816
|
+
else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
|
|
817
|
+
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
818
|
+
return (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place})`;
|
|
819
|
+
}
|
|
820
|
+
else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
|
|
821
|
+
const tag = ((field.number << 3) | 2) >>> 0;
|
|
822
|
+
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
823
|
+
return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toProtoObjectId}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
|
|
824
|
+
}
|
|
825
|
+
else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
|
|
826
|
+
const tag = ((field.number << 3) | 2) >>> 0;
|
|
827
|
+
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
828
|
+
return (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
|
|
829
|
+
}
|
|
830
|
+
else if ((0, types_1.isValueType)(ctx, field)) {
|
|
831
|
+
const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : "";
|
|
832
|
+
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
833
|
+
const wrappedValue = (place) => {
|
|
834
|
+
if ((0, types_1.isAnyValueType)(field) || (0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isFieldMaskType)(field)) {
|
|
835
|
+
return (0, ts_poet_1.code) `${type}.wrap(${place})`;
|
|
836
|
+
}
|
|
837
|
+
return (0, ts_poet_1.code) `{${maybeTypeField} value: ${place}!}`;
|
|
838
|
+
};
|
|
839
|
+
const tag = ((field.number << 3) | 2) >>> 0;
|
|
840
|
+
return (place) => (0, ts_poet_1.code) `${type}.encode(${wrappedValue(place)}, writer.uint32(${tag}).fork()).ldelim()`;
|
|
841
|
+
}
|
|
842
|
+
else if ((0, types_1.isMessage)(field)) {
|
|
843
|
+
const tag = ((field.number << 3) | 2) >>> 0;
|
|
844
|
+
const type = (0, types_1.basicTypeName)(ctx, field);
|
|
845
|
+
return (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`;
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
throw new Error(`Unhandled field ${field}`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
774
851
|
/** Creates a function to encode a message by loop overing the tags. */
|
|
775
852
|
function generateEncode(ctx, fullName, messageDesc) {
|
|
776
853
|
const { options, utils, typeMap } = ctx;
|
|
@@ -783,54 +860,15 @@ function generateEncode(ctx, fullName, messageDesc) {
|
|
|
783
860
|
writer: ${Writer} = ${Writer}.create(),
|
|
784
861
|
): ${Writer} {
|
|
785
862
|
`);
|
|
863
|
+
const processedOneofs = new Set();
|
|
864
|
+
const oneOfFieldsDict = messageDesc.field
|
|
865
|
+
.filter((field) => (0, types_1.isWithinOneOfThatShouldBeUnion)(options, field))
|
|
866
|
+
.reduce((result, field) => ((result[field.oneofIndex] || (result[field.oneofIndex] = [])).push(field), result), {});
|
|
786
867
|
// then add a case for each field
|
|
787
868
|
messageDesc.field.forEach((field) => {
|
|
788
869
|
const fieldName = (0, case_1.maybeSnakeToCamel)(field.name, options);
|
|
789
870
|
// get a generic writer.doSomething based on the basic type
|
|
790
|
-
|
|
791
|
-
if ((0, types_1.isEnum)(field) && options.stringEnums) {
|
|
792
|
-
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
793
|
-
const toNumber = (0, types_1.getEnumMethod)(ctx, field.typeName, "ToNumber");
|
|
794
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${toNumber}(${place}))`;
|
|
795
|
-
}
|
|
796
|
-
else if ((0, types_1.isLong)(field) && options.forceLong === options_1.LongOption.BIGINT) {
|
|
797
|
-
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
798
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place}.toString())`;
|
|
799
|
-
}
|
|
800
|
-
else if ((0, types_1.isScalar)(field) || (0, types_1.isEnum)(field)) {
|
|
801
|
-
const tag = ((field.number << 3) | (0, types_1.basicWireType)(field.type)) >>> 0;
|
|
802
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `writer.uint32(${tag}).${(0, types_1.toReaderCall)(field)}(${place})`;
|
|
803
|
-
}
|
|
804
|
-
else if ((0, types_1.isObjectId)(field) && options.useMongoObjectId) {
|
|
805
|
-
const tag = ((field.number << 3) | 2) >>> 0;
|
|
806
|
-
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
807
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toProtoObjectId}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
|
|
808
|
-
}
|
|
809
|
-
else if ((0, types_1.isTimestamp)(field) && (options.useDate === options_1.DateOption.DATE || options.useDate === options_1.DateOption.STRING)) {
|
|
810
|
-
const tag = ((field.number << 3) | 2) >>> 0;
|
|
811
|
-
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
812
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${utils.toTimestamp}(${place}), writer.uint32(${tag}).fork()).ldelim()`;
|
|
813
|
-
}
|
|
814
|
-
else if ((0, types_1.isValueType)(ctx, field)) {
|
|
815
|
-
const maybeTypeField = options.outputTypeRegistry ? `$type: '${field.typeName.slice(1)}',` : "";
|
|
816
|
-
const type = (0, types_1.basicTypeName)(ctx, field, { keepValueType: true });
|
|
817
|
-
const wrappedValue = (place) => {
|
|
818
|
-
if ((0, types_1.isAnyValueType)(field) || (0, types_1.isListValueType)(field) || (0, types_1.isStructType)(field) || (0, types_1.isFieldMaskType)(field)) {
|
|
819
|
-
return (0, ts_poet_1.code) `${type}.wrap(${place})`;
|
|
820
|
-
}
|
|
821
|
-
return (0, ts_poet_1.code) `{${maybeTypeField} value: ${place}!}`;
|
|
822
|
-
};
|
|
823
|
-
const tag = ((field.number << 3) | 2) >>> 0;
|
|
824
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${wrappedValue(place)}, writer.uint32(${tag}).fork()).ldelim()`;
|
|
825
|
-
}
|
|
826
|
-
else if ((0, types_1.isMessage)(field)) {
|
|
827
|
-
const tag = ((field.number << 3) | 2) >>> 0;
|
|
828
|
-
const type = (0, types_1.basicTypeName)(ctx, field);
|
|
829
|
-
writeSnippet = (place) => (0, ts_poet_1.code) `${type}.encode(${place}, writer.uint32(${tag}).fork()).ldelim()`;
|
|
830
|
-
}
|
|
831
|
-
else {
|
|
832
|
-
throw new Error(`Unhandled field ${field}`);
|
|
833
|
-
}
|
|
871
|
+
const writeSnippet = getEncodeWriteSnippet(ctx, field);
|
|
834
872
|
const isOptional = (0, types_1.isOptionalProperty)(field, messageDesc.options, options);
|
|
835
873
|
if ((0, types_1.isRepeated)(field)) {
|
|
836
874
|
if ((0, types_1.isMapType)(ctx, messageDesc, field)) {
|
|
@@ -925,12 +963,19 @@ function generateEncode(ctx, fullName, messageDesc) {
|
|
|
925
963
|
}
|
|
926
964
|
}
|
|
927
965
|
else if ((0, types_1.isWithinOneOfThatShouldBeUnion)(options, field)) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
966
|
+
if (!processedOneofs.has(field.oneofIndex)) {
|
|
967
|
+
processedOneofs.add(field.oneofIndex);
|
|
968
|
+
const oneofName = (0, case_1.maybeSnakeToCamel)(messageDesc.oneofDecl[field.oneofIndex].name, options);
|
|
969
|
+
chunks.push((0, ts_poet_1.code) `switch (message.${oneofName}?.$case) {`);
|
|
970
|
+
for (const oneOfField of oneOfFieldsDict[field.oneofIndex]) {
|
|
971
|
+
const writeSnippet = getEncodeWriteSnippet(ctx, oneOfField);
|
|
972
|
+
const oneOfFieldName = (0, case_1.maybeSnakeToCamel)(oneOfField.name, ctx.options);
|
|
973
|
+
chunks.push((0, ts_poet_1.code) `case "${oneOfFieldName}":
|
|
974
|
+
${writeSnippet(`message.${oneofName}.${oneOfFieldName}`)};
|
|
975
|
+
break;`);
|
|
976
|
+
}
|
|
977
|
+
chunks.push((0, ts_poet_1.code) `}`);
|
|
978
|
+
}
|
|
934
979
|
}
|
|
935
980
|
else if ((0, types_1.isWithinOneOf)(field)) {
|
|
936
981
|
// Oneofs don't have a default value check b/c they need to denote which-oneof presence
|
|
@@ -1553,164 +1598,6 @@ function generateFromPartial(ctx, fullName, messageDesc) {
|
|
|
1553
1598
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
1554
1599
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
|
1555
1600
|
}
|
|
1556
|
-
function generateWrap(ctx, fullProtoTypeName, fieldNames) {
|
|
1557
|
-
const chunks = [];
|
|
1558
|
-
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
1559
|
-
let setStatement = "struct.fields[key] = object[key];";
|
|
1560
|
-
if (ctx.options.useMapType) {
|
|
1561
|
-
setStatement = "struct.fields.set(key, object[key]);";
|
|
1562
|
-
}
|
|
1563
|
-
chunks.push((0, ts_poet_1.code) `wrap(object: {[key: string]: any} | undefined): Struct {
|
|
1564
|
-
const struct = createBaseStruct();
|
|
1565
|
-
if (object !== undefined) {
|
|
1566
|
-
Object.keys(object).forEach(key => {
|
|
1567
|
-
${setStatement}
|
|
1568
|
-
});
|
|
1569
|
-
}
|
|
1570
|
-
return struct;
|
|
1571
|
-
}`);
|
|
1572
|
-
}
|
|
1573
|
-
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
1574
|
-
if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
|
|
1575
|
-
chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
|
|
1576
|
-
const result = createBaseValue()${maybeAsAny(ctx.options)};
|
|
1577
|
-
|
|
1578
|
-
if (value === null) {
|
|
1579
|
-
result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE};
|
|
1580
|
-
} else if (typeof value === 'boolean') {
|
|
1581
|
-
result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value};
|
|
1582
|
-
} else if (typeof value === 'number') {
|
|
1583
|
-
result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value};
|
|
1584
|
-
} else if (typeof value === 'string') {
|
|
1585
|
-
result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value};
|
|
1586
|
-
} else if (Array.isArray(value)) {
|
|
1587
|
-
result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value};
|
|
1588
|
-
} else if (typeof value === 'object') {
|
|
1589
|
-
result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value};
|
|
1590
|
-
} else if (typeof value !== 'undefined') {
|
|
1591
|
-
throw new Error('Unsupported any value type: ' + typeof value);
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
return result;
|
|
1595
|
-
}`);
|
|
1596
|
-
}
|
|
1597
|
-
else {
|
|
1598
|
-
chunks.push((0, ts_poet_1.code) `wrap(value: any): Value {
|
|
1599
|
-
const result = createBaseValue()${maybeAsAny(ctx.options)};
|
|
1600
|
-
|
|
1601
|
-
if (value === null) {
|
|
1602
|
-
result.${fieldNames.nullValue} = NullValue.NULL_VALUE;
|
|
1603
|
-
} else if (typeof value === 'boolean') {
|
|
1604
|
-
result.${fieldNames.boolValue} = value;
|
|
1605
|
-
} else if (typeof value === 'number') {
|
|
1606
|
-
result.${fieldNames.numberValue} = value;
|
|
1607
|
-
} else if (typeof value === 'string') {
|
|
1608
|
-
result.${fieldNames.stringValue} = value;
|
|
1609
|
-
} else if (Array.isArray(value)) {
|
|
1610
|
-
result.${fieldNames.listValue} = value;
|
|
1611
|
-
} else if (typeof value === 'object') {
|
|
1612
|
-
result.${fieldNames.structValue} = value;
|
|
1613
|
-
} else if (typeof value !== 'undefined') {
|
|
1614
|
-
throw new Error('Unsupported any value type: ' + typeof value);
|
|
1615
|
-
}
|
|
1616
|
-
|
|
1617
|
-
return result;
|
|
1618
|
-
}`);
|
|
1619
|
-
}
|
|
1620
|
-
}
|
|
1621
|
-
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
1622
|
-
chunks.push((0, ts_poet_1.code) `wrap(value: ${ctx.options.useReadonlyTypes ? "ReadonlyArray<any>" : "Array<any>"} | undefined): ListValue {
|
|
1623
|
-
const result = createBaseListValue()${maybeAsAny(ctx.options)};
|
|
1624
|
-
|
|
1625
|
-
result.values = value ?? [];
|
|
1626
|
-
|
|
1627
|
-
return result;
|
|
1628
|
-
}`);
|
|
1629
|
-
}
|
|
1630
|
-
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
1631
|
-
chunks.push((0, ts_poet_1.code) `wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask {
|
|
1632
|
-
const result = createBaseFieldMask()${maybeAsAny(ctx.options)};
|
|
1633
|
-
|
|
1634
|
-
result.paths = paths;
|
|
1635
|
-
|
|
1636
|
-
return result;
|
|
1637
|
-
}`);
|
|
1638
|
-
}
|
|
1639
|
-
return chunks;
|
|
1640
|
-
}
|
|
1641
|
-
function generateUnwrap(ctx, fullProtoTypeName, fieldNames) {
|
|
1642
|
-
const chunks = [];
|
|
1643
|
-
if ((0, types_1.isStructTypeName)(fullProtoTypeName)) {
|
|
1644
|
-
if (ctx.options.useMapType) {
|
|
1645
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
1646
|
-
const object: { [key: string]: any } = {};
|
|
1647
|
-
[...message.fields.keys()].forEach((key) => {
|
|
1648
|
-
object[key] = message.fields.get(key);
|
|
1649
|
-
});
|
|
1650
|
-
return object;
|
|
1651
|
-
}`);
|
|
1652
|
-
}
|
|
1653
|
-
else {
|
|
1654
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: Struct): {[key: string]: any} {
|
|
1655
|
-
const object: { [key: string]: any } = {};
|
|
1656
|
-
Object.keys(message.fields).forEach(key => {
|
|
1657
|
-
object[key] = message.fields[key];
|
|
1658
|
-
});
|
|
1659
|
-
return object;
|
|
1660
|
-
}`);
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
if ((0, types_1.isAnyValueTypeName)(fullProtoTypeName)) {
|
|
1664
|
-
if (ctx.options.oneof === options_1.OneofOption.UNIONS) {
|
|
1665
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
|
|
1666
|
-
if (message.kind?.$case === '${fieldNames.nullValue}') {
|
|
1667
|
-
return null;
|
|
1668
|
-
} else if (message.kind?.$case === '${fieldNames.numberValue}') {
|
|
1669
|
-
return message.kind?.${fieldNames.numberValue};
|
|
1670
|
-
} else if (message.kind?.$case === '${fieldNames.stringValue}') {
|
|
1671
|
-
return message.kind?.${fieldNames.stringValue};
|
|
1672
|
-
} else if (message.kind?.$case === '${fieldNames.boolValue}') {
|
|
1673
|
-
return message.kind?.${fieldNames.boolValue};
|
|
1674
|
-
} else if (message.kind?.$case === '${fieldNames.structValue}') {
|
|
1675
|
-
return message.kind?.${fieldNames.structValue};
|
|
1676
|
-
} else if (message.kind?.$case === '${fieldNames.listValue}') {
|
|
1677
|
-
return message.kind?.${fieldNames.listValue};
|
|
1678
|
-
} else {
|
|
1679
|
-
return undefined;
|
|
1680
|
-
}
|
|
1681
|
-
}`);
|
|
1682
|
-
}
|
|
1683
|
-
else {
|
|
1684
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: Value): string | number | boolean | Object | null | Array<any> | undefined {
|
|
1685
|
-
if (message?.${fieldNames.stringValue} !== undefined) {
|
|
1686
|
-
return message.${fieldNames.stringValue};
|
|
1687
|
-
} else if (message?.${fieldNames.numberValue} !== undefined) {
|
|
1688
|
-
return message.${fieldNames.numberValue};
|
|
1689
|
-
} else if (message?.${fieldNames.boolValue} !== undefined) {
|
|
1690
|
-
return message.${fieldNames.boolValue};
|
|
1691
|
-
} else if (message?.${fieldNames.structValue} !== undefined) {
|
|
1692
|
-
return message.${fieldNames.structValue};
|
|
1693
|
-
} else if (message?.${fieldNames.listValue} !== undefined) {
|
|
1694
|
-
return message.${fieldNames.listValue};
|
|
1695
|
-
} else if (message?.${fieldNames.nullValue} !== undefined) {
|
|
1696
|
-
return null;
|
|
1697
|
-
}
|
|
1698
|
-
return undefined;
|
|
1699
|
-
}`);
|
|
1700
|
-
}
|
|
1701
|
-
}
|
|
1702
|
-
if ((0, types_1.isListValueTypeName)(fullProtoTypeName)) {
|
|
1703
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array<any> {
|
|
1704
|
-
return message.values;
|
|
1705
|
-
}`);
|
|
1706
|
-
}
|
|
1707
|
-
if ((0, types_1.isFieldMaskTypeName)(fullProtoTypeName)) {
|
|
1708
|
-
chunks.push((0, ts_poet_1.code) `unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] {
|
|
1709
|
-
return message.paths;
|
|
1710
|
-
}`);
|
|
1711
|
-
}
|
|
1712
|
-
return chunks;
|
|
1713
|
-
}
|
|
1714
1601
|
exports.contextTypeVar = "Context extends DataLoaders";
|
|
1715
1602
|
function maybeCastToNumber(ctx, messageDesc, field, variableName) {
|
|
1716
1603
|
const { keyType } = (0, types_1.detectMapType)(ctx, messageDesc, field);
|
package/build/options.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-proto",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.140.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "build/plugin.js",
|
|
6
6
|
"repository": "github:stephenh/ts-proto",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"author": "",
|
|
29
29
|
"license": "ISC",
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@grpc/grpc-js": "^1.
|
|
32
|
-
"@grpc/proto-loader": "^0.
|
|
31
|
+
"@grpc/grpc-js": "^1.3.0",
|
|
32
|
+
"@grpc/proto-loader": "^0.6.0",
|
|
33
33
|
"@improbable-eng/grpc-web": "^0.14.0",
|
|
34
34
|
"@improbable-eng/grpc-web-node-http-transport": "^0.14.0",
|
|
35
35
|
"@nestjs/common": "^8.2.2",
|