ts-proto 2.14.0 → 2.15.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
|
@@ -465,11 +465,17 @@ With `--ts_proto_opt=protoJsonFormat=false`, strict proto3 compliance is disable
|
|
|
465
465
|
|
|
466
466
|
(Requires `nestJs=true`.)
|
|
467
467
|
|
|
468
|
+
- With `--ts_proto_opt=useNestjsFqnServiceName=true`, the generated `@FooServiceControllerMethods()` decorator will pass the fully qualified service name (i.e. `package.FooService` instead of `FooService`) to the `@GrpcMethod`/`@GrpcStreamMethod` decorators of `@nestjs/microservices`.
|
|
469
|
+
|
|
470
|
+
NestJS falls back to resolving a handler by the `package.Service` segment of the gRPC method path, so the handlers are found regardless of which `package` the gRPC server was configured with, and services with the same name in different packages no longer collide. The `FOO_SERVICE_NAME` const is not affected, since `client.getService()` expects the package-relative name.
|
|
471
|
+
|
|
472
|
+
(Requires `nestJs=true`.)
|
|
473
|
+
|
|
468
474
|
- 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.
|
|
469
475
|
|
|
470
476
|
Specifically `outputEncodeMethods`, `outputJsonMethods`, and `outputClientImpl` will all be false, `lowerCaseServiceMethods` will be true and `outputServices` will be ignored.
|
|
471
477
|
|
|
472
|
-
Note that `addGrpcMetadata`, `addNestjsRestParameter` and `
|
|
478
|
+
Note that `addGrpcMetadata`, `addNestjsRestParameter`, `returnObservable` and `useNestjsFqnServiceName` will still be false.
|
|
473
479
|
|
|
474
480
|
- With `--ts_proto_opt=useDate=false`, fields of type `google.protobuf.Timestamp` will not be mapped to type `Date` in the generated types. See [Timestamp](#timestamp) for more details.
|
|
475
481
|
|
|
@@ -4,4 +4,4 @@ import SourceInfo from "./sourceInfo";
|
|
|
4
4
|
import { Context } from "./context";
|
|
5
5
|
export declare function generateNestjsServiceController(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
|
|
6
6
|
export declare function generateNestjsServiceClient(ctx: Context, fileDesc: FileDescriptorProto, sourceInfo: SourceInfo, serviceDesc: ServiceDescriptorProto): Code;
|
|
7
|
-
export declare function generateNestjsGrpcServiceMethodsDecorator(ctx: Context, serviceDesc: ServiceDescriptorProto): Code;
|
|
7
|
+
export declare function generateNestjsGrpcServiceMethodsDecorator(ctx: Context, fileDesc: FileDescriptorProto, serviceDesc: ServiceDescriptorProto): Code;
|
|
@@ -118,10 +118,13 @@ function generateNestjsServiceClient(ctx, fileDesc, sourceInfo, serviceDesc) {
|
|
|
118
118
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
119
119
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n\n" });
|
|
120
120
|
}
|
|
121
|
-
function generateNestjsGrpcServiceMethodsDecorator(ctx, serviceDesc) {
|
|
121
|
+
function generateNestjsGrpcServiceMethodsDecorator(ctx, fileDesc, serviceDesc) {
|
|
122
122
|
const { options } = ctx;
|
|
123
123
|
const GrpcMethod = (0, ts_poet_1.imp)("GrpcMethod@@nestjs/microservices");
|
|
124
124
|
const GrpcStreamMethod = (0, ts_poet_1.imp)("GrpcStreamMethod@@nestjs/microservices");
|
|
125
|
+
const serviceName = options.useNestjsFqnServiceName
|
|
126
|
+
? (0, utils_1.maybePrefixPackage)(fileDesc, serviceDesc.name)
|
|
127
|
+
: serviceDesc.name;
|
|
125
128
|
const grpcMethods = serviceDesc.method
|
|
126
129
|
.filter((m) => !m.clientStreaming)
|
|
127
130
|
.map((m) => {
|
|
@@ -142,13 +145,13 @@ function generateNestjsGrpcServiceMethodsDecorator(ctx, serviceDesc) {
|
|
|
142
145
|
const grpcMethods: string[] = [${grpcMethods.join(", ")}];
|
|
143
146
|
for (const method of grpcMethods) {
|
|
144
147
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
|
145
|
-
${GrpcMethod}('${
|
|
148
|
+
${GrpcMethod}('${serviceName}', method)(constructor.prototype[method], method, descriptor);
|
|
146
149
|
Object.defineProperty(constructor.prototype, method, descriptor);
|
|
147
150
|
}
|
|
148
151
|
const grpcStreamMethods: string[] = [${grpcStreamMethods.join(", ")}];
|
|
149
152
|
for (const method of grpcStreamMethods) {
|
|
150
153
|
const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
|
|
151
|
-
${GrpcStreamMethod}('${
|
|
154
|
+
${GrpcStreamMethod}('${serviceName}', method)(constructor.prototype[method], method, descriptor);
|
|
152
155
|
Object.defineProperty(constructor.prototype, method, descriptor);
|
|
153
156
|
}
|
|
154
157
|
};
|
package/build/src/main.d.ts
CHANGED
|
@@ -42,6 +42,9 @@ declare function makeTimestampMethods(options: Options, longs: ReturnType<typeof
|
|
|
42
42
|
fromTimestamp: ConditionalOutput;
|
|
43
43
|
timestampFractionRegex: ConditionalOutput;
|
|
44
44
|
fromJsonTimestamp: ConditionalOutput;
|
|
45
|
+
timestampJsonRegex: ConditionalOutput;
|
|
46
|
+
timestampFromJson: ConditionalOutput;
|
|
47
|
+
timestampToJson: ConditionalOutput;
|
|
45
48
|
};
|
|
46
49
|
declare function makeAnyJsonUtils(): {
|
|
47
50
|
anyJsonValueTypes: ConditionalOutput;
|
package/build/src/main.js
CHANGED
|
@@ -233,7 +233,7 @@ function generateFile(ctx, fileDesc) {
|
|
|
233
233
|
// and the service controller interface
|
|
234
234
|
chunks.push((0, generate_nestjs_1.generateNestjsServiceController)(ctx, fileDesc, sInfo, serviceDesc));
|
|
235
235
|
// generate nestjs grpc service controller decorator
|
|
236
|
-
chunks.push((0, generate_nestjs_1.generateNestjsGrpcServiceMethodsDecorator)(ctx, serviceDesc));
|
|
236
|
+
chunks.push((0, generate_nestjs_1.generateNestjsGrpcServiceMethodsDecorator)(ctx, fileDesc, serviceDesc));
|
|
237
237
|
let serviceConstName = `${(0, case_1.camelToSnake)(serviceDesc.name)}_NAME`;
|
|
238
238
|
if (!serviceDesc.name.toLowerCase().endsWith("service")) {
|
|
239
239
|
serviceConstName = `${(0, case_1.camelToSnake)(serviceDesc.name)}_SERVICE_NAME`;
|
|
@@ -829,7 +829,43 @@ function makeTimestampMethods(options, longs, bytes) {
|
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
831
|
`);
|
|
832
|
-
|
|
832
|
+
// Declared once per file, so the regex isn't compiled on each timestampFromJson call
|
|
833
|
+
const timestampJsonRegex = (0, ts_poet_1.conditionalOutput)("timestampJsonRegex", (0, ts_poet_1.code) `const timestampJsonRegex = /^.+T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/;`);
|
|
834
|
+
// Parses the canonical JSON of a Timestamp, an RFC 3339 string
|
|
835
|
+
const timestampFromJson = (0, ts_poet_1.conditionalOutput)("timestampFromJson", (0, ts_poet_1.code) `
|
|
836
|
+
function timestampFromJson(value: string): { seconds: number; nanos: number } {
|
|
837
|
+
const match = ${timestampJsonRegex}.exec(value);
|
|
838
|
+
// Date only keeps milliseconds, so it parses the string without the fractional seconds
|
|
839
|
+
const fraction = match?.[1];
|
|
840
|
+
const millis = match
|
|
841
|
+
? new ${bytes.globalThis}.Date(fraction === undefined ? value : value.replace(fraction, "")).getTime()
|
|
842
|
+
: NaN;
|
|
843
|
+
if (!match || ${bytes.globalThis}.Number.isNaN(millis)) {
|
|
844
|
+
throw new ${bytes.globalThis}.Error(\`Invalid timestamp: \${value}\`);
|
|
845
|
+
}
|
|
846
|
+
const nanos = fraction === undefined ? 0 : ${bytes.globalThis}.Number(fraction.slice(1, 10).padEnd(9, "0"));
|
|
847
|
+
return { seconds: millis / 1_000, nanos };
|
|
848
|
+
}
|
|
849
|
+
`);
|
|
850
|
+
// Writes the canonical JSON of a Timestamp, an RFC 3339 string
|
|
851
|
+
const timestampToJson = (0, ts_poet_1.conditionalOutput)("timestampToJson", (0, ts_poet_1.code) `
|
|
852
|
+
function timestampToJson(seconds: number, nanos: number): string {
|
|
853
|
+
const date = new ${bytes.globalThis}.Date(seconds * 1_000).toISOString().slice(0, 19);
|
|
854
|
+
if (nanos === 0) return \`\${date}Z\`;
|
|
855
|
+
const padded = nanos.toString().padStart(9, "0");
|
|
856
|
+
const len = nanos % 1_000_000 === 0 ? 3 : nanos % 1_000 === 0 ? 6 : 9;
|
|
857
|
+
return \`\${date}.\${padded.slice(0, len)}Z\`;
|
|
858
|
+
}
|
|
859
|
+
`);
|
|
860
|
+
return {
|
|
861
|
+
toTimestamp,
|
|
862
|
+
fromTimestamp,
|
|
863
|
+
timestampFractionRegex,
|
|
864
|
+
fromJsonTimestamp,
|
|
865
|
+
timestampJsonRegex,
|
|
866
|
+
timestampFromJson,
|
|
867
|
+
timestampToJson,
|
|
868
|
+
};
|
|
833
869
|
}
|
|
834
870
|
function makeAnyJsonUtils() {
|
|
835
871
|
// Well-known types whose JSON is not an object of their fields. Packed in an Any, that
|
|
@@ -1905,10 +1941,22 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
|
|
|
1905
1941
|
`;
|
|
1906
1942
|
}
|
|
1907
1943
|
// create the basic function declaration
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1944
|
+
if (fullTypeName === "google.protobuf.Timestamp") {
|
|
1945
|
+
// The canonical JSON of a Timestamp is an RFC 3339 string; the object form is still accepted.
|
|
1946
|
+
chunks.push((0, ts_poet_1.code) `
|
|
1947
|
+
fromJSON(object: any): ${fullName} {
|
|
1948
|
+
if (typeof object === "string") {
|
|
1949
|
+
return ${fullName}.fromJSON(${utils.timestampFromJson}(object));
|
|
1950
|
+
}
|
|
1951
|
+
return {
|
|
1952
|
+
`);
|
|
1953
|
+
}
|
|
1954
|
+
else {
|
|
1955
|
+
chunks.push((0, ts_poet_1.code) `
|
|
1956
|
+
fromJSON(${messageDesc.field.length > 0 ? "object" : "_"}: any): ${fullName} {
|
|
1957
|
+
return {
|
|
1958
|
+
`);
|
|
1959
|
+
}
|
|
1912
1960
|
if ((0, options_1.addTypeToMessages)(options)) {
|
|
1913
1961
|
chunks.push((0, ts_poet_1.code) `$type: ${fullName}.$type,`);
|
|
1914
1962
|
}
|
|
@@ -2223,7 +2271,7 @@ function generateFromJson(ctx, fullName, fullTypeName, messageDesc) {
|
|
|
2223
2271
|
chunks.push((0, ts_poet_1.code) `}`);
|
|
2224
2272
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
|
2225
2273
|
}
|
|
2226
|
-
function generateCanonicalToJson(fullName, fullProtobufTypeName, { useOptionals, useNullAsOptional, useDuration, forceLong }) {
|
|
2274
|
+
function generateCanonicalToJson(fullName, fullProtobufTypeName, { useOptionals, useNullAsOptional, useDuration, useJsonTimestamp, forceLong }, utils) {
|
|
2227
2275
|
if ((0, types_1.isFieldMaskTypeName)(fullProtobufTypeName)) {
|
|
2228
2276
|
const returnType = useOptionals === "all" ? `string | ${(0, utils_1.nullOrUndefined)({ useNullAsOptional })}` : "string";
|
|
2229
2277
|
const pathModifier = useOptionals === "all" ? "?" : "";
|
|
@@ -2269,6 +2317,16 @@ function generateCanonicalToJson(fullName, fullProtobufTypeName, { useOptionals,
|
|
|
2269
2317
|
}
|
|
2270
2318
|
`;
|
|
2271
2319
|
}
|
|
2320
|
+
if (fullProtobufTypeName === "google.protobuf.Timestamp" && useJsonTimestamp === options_1.JsonTimestampOption.RFC3339) {
|
|
2321
|
+
const seconds = forceLong === options_1.LongOption.LONG
|
|
2322
|
+
? (0, ts_poet_1.code) `message.seconds?.toNumber() ?? 0`
|
|
2323
|
+
: (0, ts_poet_1.code) `${utils.globalThis}.Number(message.seconds ?? 0)`;
|
|
2324
|
+
return (0, ts_poet_1.code) `
|
|
2325
|
+
toJSON(message: ${fullName}): string {
|
|
2326
|
+
return ${utils.timestampToJson}(${seconds}, message.nanos ?? 0);
|
|
2327
|
+
}
|
|
2328
|
+
`;
|
|
2329
|
+
}
|
|
2272
2330
|
return undefined;
|
|
2273
2331
|
}
|
|
2274
2332
|
/** Whether `google.protobuf.Any` gets its canonical `@type` JSON form, see the `anyType` option. */
|
|
@@ -2348,7 +2406,7 @@ function generateToJson(ctx, fullName, fullProtobufTypeName, messageDesc) {
|
|
|
2348
2406
|
chunks.push(generateCanonicalAnyToJson(ctx, fullName, messageDesc));
|
|
2349
2407
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
|
2350
2408
|
}
|
|
2351
|
-
const canonicalToJson = generateCanonicalToJson(fullName, fullProtobufTypeName, options);
|
|
2409
|
+
const canonicalToJson = generateCanonicalToJson(fullName, fullProtobufTypeName, options, utils);
|
|
2352
2410
|
if (canonicalToJson) {
|
|
2353
2411
|
chunks.push(canonicalToJson);
|
|
2354
2412
|
return (0, ts_poet_1.joinCode)(chunks, { on: "\n" });
|
package/build/src/options.d.ts
CHANGED
package/build/src/options.js
CHANGED
|
@@ -98,6 +98,7 @@ function defaultOptions() {
|
|
|
98
98
|
addGrpcMetadata: false,
|
|
99
99
|
metadataType: undefined,
|
|
100
100
|
addNestjsRestParameter: false,
|
|
101
|
+
useNestjsFqnServiceName: false,
|
|
101
102
|
nestJs: false,
|
|
102
103
|
env: EnvOption.BOTH,
|
|
103
104
|
unrecognizedEnum: true,
|
|
@@ -238,6 +239,10 @@ function optionsFromParameter(parameter) {
|
|
|
238
239
|
if (options.nestJs) {
|
|
239
240
|
options.initializeFieldsAsUndefined = false;
|
|
240
241
|
}
|
|
242
|
+
else {
|
|
243
|
+
// useNestjsFqnServiceName requires nestJs=true
|
|
244
|
+
options.useNestjsFqnServiceName = false;
|
|
245
|
+
}
|
|
241
246
|
if (options.outputIndex) {
|
|
242
247
|
options.exportCommonSymbols = false;
|
|
243
248
|
}
|