diff --git a/integration/fieldmask/google/protobuf/field_mask.ts b/integration/fieldmask/google/protobuf/field_mask.ts index 9a6b703f5..37ec28306 100644 --- a/integration/fieldmask/google/protobuf/field_mask.ts +++ b/integration/fieldmask/google/protobuf/field_mask.ts @@ -264,9 +264,7 @@ export const FieldMask = { wrap(paths: string[]): FieldMask { const result = createBaseFieldMask(); - result.paths = paths; - return result; }, diff --git a/integration/grpc-js/google/protobuf/struct.ts b/integration/grpc-js/google/protobuf/struct.ts index 675831508..cc3f1bebd 100644 --- a/integration/grpc-js/google/protobuf/struct.ts +++ b/integration/grpc-js/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/nestjs-simple/google/protobuf/struct.ts b/integration/nestjs-simple/google/protobuf/struct.ts new file mode 100644 index 000000000..d9ae0e11d --- /dev/null +++ b/integration/nestjs-simple/google/protobuf/struct.ts @@ -0,0 +1,172 @@ +/* eslint-disable */ +import { wrappers } from "protobufjs"; + +export const protobufPackage = "google.protobuf"; + +/** + * `NullValue` is a singleton enumeration to represent the null value for the + * `Value` type union. + * + * The JSON representation for `NullValue` is JSON `null`. + */ +export enum NullValue { + /** NULL_VALUE - Null value. */ + NULL_VALUE = 0, + UNRECOGNIZED = -1, +} + +/** + * `Struct` represents a structured data value, consisting of fields + * which map to dynamically typed values. In some languages, `Struct` + * might be supported by a native representation. For example, in + * scripting languages like JS a struct is represented as an + * object. The details of that representation are described together + * with the proto support for the language. + * + * The JSON representation for `Struct` is JSON object. + */ +export interface Struct { + /** Unordered map of dynamically typed values. */ + fields: { [key: string]: any | undefined }; +} + +export interface Struct_FieldsEntry { + key: string; + value: any | undefined; +} + +/** + * `Value` represents a dynamically typed value which can be either + * null, a number, a string, a boolean, a recursive struct value, or a + * list of values. A producer of value is expected to set one of these + * variants. Absence of any variant indicates an error. + * + * The JSON representation for `Value` is JSON value. + */ +export interface Value { + /** Represents a null value. */ + nullValue?: + | NullValue + | undefined; + /** Represents a double value. */ + numberValue?: + | number + | undefined; + /** Represents a string value. */ + stringValue?: + | string + | undefined; + /** Represents a boolean value. */ + boolValue?: + | boolean + | undefined; + /** Represents a structured value. */ + structValue?: + | { [key: string]: any } + | undefined; + /** Represents a repeated `Value`. */ + listValue?: Array | undefined; +} + +/** + * `ListValue` is a wrapper around a repeated field of values. + * + * The JSON representation for `ListValue` is JSON array. + */ +export interface ListValue { + /** Repeated field of dynamically typed values. */ + values: any[]; +} + +export const GOOGLE_PROTOBUF_PACKAGE_NAME = "google.protobuf"; + +function createBaseStruct(): Struct { + return { fields: {} }; +} + +export const Struct = { + wrap(object: { [key: string]: any } | undefined): Struct { + const struct = createBaseStruct(); + if (object !== undefined) { + Object.keys(object).forEach((key) => { + struct.fields[key] = Value.wrap(object[key]); + }); + } + return struct; + }, + + unwrap(message: Struct): { [key: string]: any } { + const object: { [key: string]: any } = {}; + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = Value.unwrap(message.fields[key]); + }); + } + return object; + }, +}; + +function createBaseValue(): Value { + return {}; +} + +export const Value = { + wrap(value: any): Value { + const result = {} as any; + if (value === null) { + result.nullValue = NullValue.NULL_VALUE; + } else if (typeof value === "boolean") { + result.boolValue = value; + } else if (typeof value === "number") { + result.numberValue = value; + } else if (typeof value === "string") { + result.stringValue = value; + } else if (Array.isArray(value)) { + result.listValue = ListValue.wrap(value); + } else if (typeof value === "object") { + result.structValue = Struct.wrap(value); + } else if (typeof value !== "undefined") { + throw new Error("Unsupported any value type: " + typeof value); + } + return result; + }, + + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message?.hasOwnProperty("stringValue") && message.stringValue !== undefined) { + return message.stringValue; + } else if (message?.hasOwnProperty("numberValue") && message?.numberValue !== undefined) { + return message.numberValue; + } else if (message?.hasOwnProperty("boolValue") && message?.boolValue !== undefined) { + return message.boolValue; + } else if (message?.hasOwnProperty("structValue") && message?.structValue !== undefined) { + return Struct.unwrap(message.structValue as any); + } else if (message?.hasOwnProperty("listValue") && message?.listValue !== undefined) { + return ListValue.unwrap(message.listValue); + } else if (message?.hasOwnProperty("nullValue") && message?.nullValue !== undefined) { + return null; + } + return undefined; + }, +}; + +function createBaseListValue(): ListValue { + return { values: [] }; +} + +export const ListValue = { + wrap(array: Array | undefined): ListValue { + const result = createBaseListValue(); + result.values = (array ?? []).map(Value.wrap); + return result; + }, + + unwrap(message: ListValue): Array { + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values.map(Value.unwrap); + } else { + return message as any; + } + }, +}; + +wrappers[".google.protobuf.Struct"] = { fromObject: Struct.wrap, toObject: Struct.unwrap } as any; diff --git a/integration/nestjs-simple/hero.bin b/integration/nestjs-simple/hero.bin index 3ce8c0002..cf9d51c3e 100644 Binary files a/integration/nestjs-simple/hero.bin and b/integration/nestjs-simple/hero.bin differ diff --git a/integration/nestjs-simple/hero.proto b/integration/nestjs-simple/hero.proto index 91b160b71..29ae07c9b 100644 --- a/integration/nestjs-simple/hero.proto +++ b/integration/nestjs-simple/hero.proto @@ -2,6 +2,7 @@ syntax = "proto3"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; +import "google/protobuf/struct.proto"; package hero; @@ -25,7 +26,8 @@ message VillainById { message Hero { int32 id = 1; string name = 2; - google.protobuf.Timestamp birth_date = 3; + .google.protobuf.Timestamp birth_date = 3; + .google.protobuf.Struct external_data = 4; } message Villain { diff --git a/integration/nestjs-simple/hero.ts b/integration/nestjs-simple/hero.ts index 02e2fe522..a6d0e0788 100644 --- a/integration/nestjs-simple/hero.ts +++ b/integration/nestjs-simple/hero.ts @@ -1,7 +1,9 @@ /* eslint-disable */ import { GrpcMethod, GrpcStreamMethod } from "@nestjs/microservices"; +import { wrappers } from "protobufjs"; import { Observable } from "rxjs"; import { Empty } from "./google/protobuf/empty"; +import { Struct } from "./google/protobuf/struct"; import { Timestamp } from "./google/protobuf/timestamp"; export const protobufPackage = "hero"; @@ -18,6 +20,7 @@ export interface Hero { id: number; name: string; birthDate: Timestamp | undefined; + externalData: { [key: string]: any } | undefined; } export interface Villain { @@ -27,6 +30,8 @@ export interface Villain { export const HERO_PACKAGE_NAME = "hero"; +wrappers[".google.protobuf.Struct"] = { fromObject: Struct.wrap, toObject: Struct.unwrap } as any; + export interface HeroServiceClient { addOneHero(request: Hero): Observable; diff --git a/integration/nestjs-simple/nestjs-project/hero.controller.ts b/integration/nestjs-simple/nestjs-project/hero.controller.ts index ff1543604..bd026fc8b 100644 --- a/integration/nestjs-simple/nestjs-project/hero.controller.ts +++ b/integration/nestjs-simple/nestjs-project/hero.controller.ts @@ -1,16 +1,29 @@ -import { Controller } from '@nestjs/common'; -import { Observable, Subject } from 'rxjs'; -import { Hero, HeroById, HeroServiceController, HeroServiceControllerMethods, Villain, VillainById } from '../hero'; +import { Controller } from "@nestjs/common"; +import { Observable, Subject } from "rxjs"; +import { Hero, HeroById, HeroServiceController, HeroServiceControllerMethods, Villain, VillainById } from "../hero"; -@Controller('hero') +@Controller("hero") @HeroServiceControllerMethods() export class HeroController implements HeroServiceController { private readonly heroes: Hero[] = [ - { id: 1, name: 'Stephenh', birthDate: { seconds: 1, nanos: 2 } }, - { id: 2, name: 'Iangregsondev', birthDate: { seconds: 1, nanos: 3 } }, + { + id: 1, + name: "Stephenh", + birthDate: { seconds: 1, nanos: 2 }, + externalData: { foo: "bar", fizz: 1, nested: { isFailing: false, arr: [1, "foo", ["bar"]] } }, + }, + { + id: 2, + name: "Iangregsondev", + birthDate: { seconds: 1, nanos: 3 }, + externalData: { bar: 10, baz: "foo", isPassing: true }, + }, ]; - private readonly villains: Villain[] = [{ id: 1, name: 'John' }, { id: 2, name: 'Doe' }]; + private readonly villains: Villain[] = [ + { id: 1, name: "John" }, + { id: 2, name: "Doe" }, + ]; addOneHero(request: Hero) { this.heroes.push(request); diff --git a/integration/nestjs-simple/nestjs-simple-test.ts b/integration/nestjs-simple/nestjs-simple-test.ts index 5670966f6..8c988fae8 100644 --- a/integration/nestjs-simple/nestjs-simple-test.ts +++ b/integration/nestjs-simple/nestjs-simple-test.ts @@ -1,18 +1,18 @@ -import { SampleService } from './sample-service'; -import { createApp } from './nestjs-project/main'; -import { INestMicroservice } from '@nestjs/common'; -import { ClientGrpc } from '@nestjs/microservices'; -import { HeroServiceClient, VillainById, Villain, HERO_SERVICE_NAME, HERO_PACKAGE_NAME } from './hero'; -import { Subject } from 'rxjs'; +import { SampleService } from "./sample-service"; +import { createApp } from "./nestjs-project/main"; +import { INestMicroservice } from "@nestjs/common"; +import { ClientGrpc } from "@nestjs/microservices"; +import { HeroServiceClient, VillainById, Villain, HERO_SERVICE_NAME, HERO_PACKAGE_NAME } from "./hero"; +import { Subject } from "rxjs"; -describe('nestjs-simple-test', () => { - it('compiles', () => { +describe("nestjs-simple-test", () => { + it("compiles", () => { const service = new SampleService(); expect(service).not.toBeUndefined(); }); }); -describe('nestjs-simple-test nestjs', () => { +describe("nestjs-simple-test nestjs", () => { let app: INestMicroservice; let client: ClientGrpc; let heroService: HeroServiceClient; @@ -28,30 +28,37 @@ describe('nestjs-simple-test nestjs', () => { await app.close(); }); - it('should get grpc client', async () => { + it("should get grpc client", async () => { expect(client).not.toBeUndefined(); }); - it('should get heroService', async () => { + it("should get heroService", async () => { expect(heroService).not.toBeUndefined(); }); - it('should addOneHero', async () => { - const emptyResponse = await heroService.addOneHero({ id: 3, name: 'Toon', birthDate: undefined }).toPromise(); + it("should addOneHero", async () => { + const emptyResponse = await heroService + .addOneHero({ id: 3, name: "Toon", birthDate: undefined, externalData: { some: "data" } }) + .toPromise(); expect(emptyResponse).toEqual({}); }); - it('should findOneHero', async () => { + it("should findOneHero", async () => { const hero = await heroService.findOneHero({ id: 1 }).toPromise(); - expect(hero).toEqual({ id: 1, name: 'Stephenh', birthDate: { seconds: 1, nanos: 2 } }); + expect(hero).toEqual({ + id: 1, + name: "Stephenh", + birthDate: { seconds: 1, nanos: 2 }, + externalData: { foo: "bar", fizz: 1, nested: { isFailing: false, arr: [1, "foo", ["bar"]] } }, + }); }); - it('should findOneVillain', async () => { + it("should findOneVillain", async () => { const villain = await heroService.findOneVillain({ id: 1 }).toPromise(); - expect(villain).toEqual({ id: 1, name: 'John' }); + expect(villain).toEqual({ id: 1, name: "John" }); }); - it('should findManyVillain', (done) => { + it("should findManyVillain", (done) => { const villainIdSubject = new Subject(); const villains: Villain[] = []; @@ -61,8 +68,8 @@ describe('nestjs-simple-test nestjs', () => { }, complete: () => { expect(villains).toEqual([ - { id: 1, name: 'John' }, - { id: 2, name: 'Doe' }, + { id: 1, name: "John" }, + { id: 2, name: "Doe" }, ]); done(); }, diff --git a/integration/nestjs-simple/sample-service.ts b/integration/nestjs-simple/sample-service.ts index 1999780ee..f08de8259 100644 --- a/integration/nestjs-simple/sample-service.ts +++ b/integration/nestjs-simple/sample-service.ts @@ -1,22 +1,27 @@ -import { HeroServiceController, HeroById, Hero, Villain, VillainById } from './hero'; -import { Observable, Subject } from 'rxjs'; +import { HeroServiceController, HeroById, Hero, Villain, VillainById } from "./hero"; +import { Observable, Subject } from "rxjs"; export class SampleService implements HeroServiceController { addOneHero(request: Hero): void {} findOneHero(request: HeroById): Promise { - return Promise.resolve({ id: 1, name: 'test', birthDate: undefined }); + return Promise.resolve({ + id: 1, + name: "test", + birthDate: undefined, + externalData: { foo: "bar", fizz: 1, nested: { isFailing: false, array: [1, "foo", ["bar"]] } }, + }); } findOneVillain(request: VillainById): Promise { - return Promise.resolve({ id: 1, name: 'test' }); + return Promise.resolve({ id: 1, name: "test" }); } findManyVillain(request: Observable): Observable { const hero$ = new Subject(); const onNext = (villainById: VillainById) => { - hero$.next({ id: 1, name: 'test' }); + hero$.next({ id: 1, name: "test" }); }; const onComplete = () => hero$.complete(); request.subscribe(onNext, null, onComplete); @@ -25,13 +30,13 @@ export class SampleService implements HeroServiceController { } async findManyVillainStreamIn(request: Observable): Promise { - return { id: 1, name: 'test' }; + return { id: 1, name: "test" }; } findManyVillainStreamOut(request: VillainById): Observable { // Kinda making this up const hero$ = new Subject(); - hero$.next({ id: 1, name: 'test' }); + hero$.next({ id: 1, name: "test" }); hero$.complete(); return hero$.asObservable(); } diff --git a/integration/nice-grpc/google/protobuf/struct.ts b/integration/nice-grpc/google/protobuf/struct.ts index 05fcd4f06..8a5a6a439 100644 --- a/integration/nice-grpc/google/protobuf/struct.ts +++ b/integration/nice-grpc/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/oneof-unions-snake/google/protobuf/struct.ts b/integration/oneof-unions-snake/google/protobuf/struct.ts index fa4e1e60e..1cf3d7cc5 100644 --- a/integration/oneof-unions-snake/google/protobuf/struct.ts +++ b/integration/oneof-unions-snake/google/protobuf/struct.ts @@ -172,9 +172,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -378,7 +380,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.kind = { $case: "null_value", null_value: NullValue.NULL_VALUE }; } else if (typeof value === "boolean") { @@ -394,7 +395,6 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, @@ -471,16 +471,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/oneof-unions/google/protobuf/struct.ts b/integration/oneof-unions/google/protobuf/struct.ts index edc0424c4..c135f74d2 100644 --- a/integration/oneof-unions/google/protobuf/struct.ts +++ b/integration/oneof-unions/google/protobuf/struct.ts @@ -172,9 +172,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -369,7 +371,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.kind = { $case: "nullValue", nullValue: NullValue.NULL_VALUE }; } else if (typeof value === "boolean") { @@ -385,7 +386,6 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, @@ -462,16 +462,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/simple-snake/google/protobuf/struct.ts b/integration/simple-snake/google/protobuf/struct.ts index 26f96d343..4dbdbd8a4 100644 --- a/integration/simple-snake/google/protobuf/struct.ts +++ b/integration/simple-snake/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.null_value = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.string_value !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.string_value !== undefined) { return message.string_value; } else if (message?.number_value !== undefined) { return message.number_value; } else if (message?.bool_value !== undefined) { return message.bool_value; } else if (message?.struct_value !== undefined) { - return message.struct_value; + return message.struct_value as any; } else if (message?.list_value !== undefined) { return message.list_value; } else if (message?.null_value !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/simple-string-enums/google/protobuf/struct.ts b/integration/simple-string-enums/google/protobuf/struct.ts index 532be4633..09cb4ca9b 100644 --- a/integration/simple-string-enums/google/protobuf/struct.ts +++ b/integration/simple-string-enums/google/protobuf/struct.ts @@ -197,9 +197,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -370,7 +372,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -386,19 +387,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -462,16 +462,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/struct/google/protobuf/struct.ts b/integration/struct/google/protobuf/struct.ts index 675831508..cc3f1bebd 100644 --- a/integration/struct/google/protobuf/struct.ts +++ b/integration/struct/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/type-registry/google/protobuf/struct.ts b/integration/type-registry/google/protobuf/struct.ts index 419bdfa1a..4eb87ba66 100644 --- a/integration/type-registry/google/protobuf/struct.ts +++ b/integration/type-registry/google/protobuf/struct.ts @@ -198,9 +198,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -385,7 +387,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -401,19 +402,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -481,16 +481,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/use-map-type/google/protobuf/struct.ts b/integration/use-map-type/google/protobuf/struct.ts index 815bfb055..ffcb6b973 100644 --- a/integration/use-map-type/google/protobuf/struct.ts +++ b/integration/use-map-type/google/protobuf/struct.ts @@ -360,7 +360,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +375,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +450,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/use-numeric-enum-json/google/protobuf/struct.ts b/integration/use-numeric-enum-json/google/protobuf/struct.ts index c2655202a..78ae9698f 100644 --- a/integration/use-numeric-enum-json/google/protobuf/struct.ts +++ b/integration/use-numeric-enum-json/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/use-readonly-types/google/protobuf/field_mask.ts b/integration/use-readonly-types/google/protobuf/field_mask.ts index 850752961..7f2e0b019 100644 --- a/integration/use-readonly-types/google/protobuf/field_mask.ts +++ b/integration/use-readonly-types/google/protobuf/field_mask.ts @@ -264,9 +264,7 @@ export const FieldMask = { wrap(paths: readonly string[]): FieldMask { const result = createBaseFieldMask() as any; - result.paths = paths; - return result; }, diff --git a/integration/use-readonly-types/google/protobuf/struct.ts b/integration/use-readonly-types/google/protobuf/struct.ts index a1a5fc0ae..42c77fe9f 100644 --- a/integration/use-readonly-types/google/protobuf/struct.ts +++ b/integration/use-readonly-types/google/protobuf/struct.ts @@ -172,9 +172,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -369,7 +371,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue() as any; - if (value === null) { result.kind = { $case: "nullValue", nullValue: NullValue.NULL_VALUE }; } else if (typeof value === "boolean") { @@ -385,7 +386,6 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, @@ -462,16 +462,18 @@ export const ListValue = { return message; }, - wrap(value: ReadonlyArray | undefined): ListValue { + wrap(array: ReadonlyArray | undefined): ListValue { const result = createBaseListValue() as any; - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: any): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/integration/value/google/protobuf/struct.ts b/integration/value/google/protobuf/struct.ts index 675831508..cc3f1bebd 100644 --- a/integration/value/google/protobuf/struct.ts +++ b/integration/value/google/protobuf/struct.ts @@ -187,9 +187,11 @@ export const Struct = { unwrap(message: Struct): { [key: string]: any } { const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach((key) => { - object[key] = message.fields[key]; - }); + if (message.fields) { + Object.keys(message.fields).forEach((key) => { + object[key] = message.fields[key]; + }); + } return object; }, }; @@ -360,7 +362,6 @@ export const Value = { wrap(value: any): Value { const result = createBaseValue(); - if (value === null) { result.nullValue = NullValue.NULL_VALUE; } else if (typeof value === "boolean") { @@ -376,19 +377,18 @@ export const Value = { } else if (typeof value !== "undefined") { throw new Error("Unsupported any value type: " + typeof value); } - return result; }, - unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.stringValue !== undefined) { + unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.stringValue !== undefined) { return message.stringValue; } else if (message?.numberValue !== undefined) { return message.numberValue; } else if (message?.boolValue !== undefined) { return message.boolValue; } else if (message?.structValue !== undefined) { - return message.structValue; + return message.structValue as any; } else if (message?.listValue !== undefined) { return message.listValue; } else if (message?.nullValue !== undefined) { @@ -452,16 +452,18 @@ export const ListValue = { return message; }, - wrap(value: Array | undefined): ListValue { + wrap(array: Array | undefined): ListValue { const result = createBaseListValue(); - - result.values = value ?? []; - + result.values = array ?? []; return result; }, unwrap(message: ListValue): Array { - return message.values; + if (message?.hasOwnProperty("values") && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } }, }; diff --git a/package.json b/package.json index c8f5a6b36..7b3e77b4e 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "author": "", "license": "ISC", "devDependencies": { - "@grpc/grpc-js": "^1.2.12", - "@grpc/proto-loader": "^0.5.6", + "@grpc/grpc-js": "^1.3.0", + "@grpc/proto-loader": "^0.6.0", "@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web-node-http-transport": "^0.14.0", "@nestjs/common": "^8.2.2", diff --git a/src/generate-struct-wrappers.ts b/src/generate-struct-wrappers.ts new file mode 100644 index 000000000..1a6dbbb6e --- /dev/null +++ b/src/generate-struct-wrappers.ts @@ -0,0 +1,341 @@ +import { Context } from "./context"; +import { code, Code } from "ts-poet"; +import { isAnyValueTypeName, isFieldMaskTypeName, isListValueTypeName, isStructTypeName } from "./types"; +import { OneofOption, Options } from "./options"; + +export type StructFieldNames = { + nullValue: string; + numberValue: string; + stringValue: string; + boolValue: string; + structValue: string; + listValue: string; +}; + +/** Whether we need to generate `.wrap` and `.unwrap` methods for the given type. */ +export function isWrapperType(fullProtoTypeName: string): boolean { + return ( + isStructTypeName(fullProtoTypeName) || + isAnyValueTypeName(fullProtoTypeName) || + isListValueTypeName(fullProtoTypeName) || + isFieldMaskTypeName(fullProtoTypeName) + ); +} + +/** + * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages. + * + * We do this deeply b/c NestJS does not invoke wrappers recursively. + */ +export function generateWrapDeep(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { + const chunks: Code[] = []; + if (isStructTypeName(fullProtoTypeName)) { + let setStatement = "struct.fields[key] = Value.wrap(object[key]);"; + if (ctx.options.useMapType) { + setStatement = "struct.fields.set(key, Value.wrap(object[key]));"; + } + chunks.push(code`wrap(object: {[key: string]: any} | undefined): Struct { + const struct = createBaseStruct(); + if (object !== undefined) { + Object.keys(object).forEach(key => { + ${setStatement} + }); + } + return struct; + }`); + } + + if (isAnyValueTypeName(fullProtoTypeName)) { + // Turn ts-proto representation --> proto representation + chunks.push(code`wrap(value: any): Value { + const result = {} as any; + if (value === null) { + result.${fieldNames.nullValue} = NullValue.NULL_VALUE; + } else if (typeof value === 'boolean') { + result.${fieldNames.boolValue} = value; + } else if (typeof value === 'number') { + result.${fieldNames.numberValue} = value; + } else if (typeof value === 'string') { + result.${fieldNames.stringValue} = value; + } else if (Array.isArray(value)) { + result.${fieldNames.listValue} = ListValue.wrap(value); + } else if (typeof value === 'object') { + result.${fieldNames.structValue} = Struct.wrap(value); + } else if (typeof value !== 'undefined') { + throw new Error('Unsupported any value type: ' + typeof value); + } + return result; + }`); + } + + if (isListValueTypeName(fullProtoTypeName)) { + const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : ""; + chunks.push(code`wrap(array: ${maybeReadyOnly}Array | undefined): ListValue { + const result = createBaseListValue()${maybeAsAny(ctx.options)}; + result.values = (array ?? []).map(Value.wrap); + return result; + }`); + } + + if (isFieldMaskTypeName(fullProtoTypeName)) { + chunks.push(code`wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask { + const result = createBaseFieldMask()${maybeAsAny(ctx.options)}; + result.paths = paths; + return result; + }`); + } + + return chunks; +} + +/** + * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation. + * + * We do this deeply b/c NestJS does not invoke wrappers recursively. + */ +export function generateUnwrapDeep(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { + const chunks: Code[] = []; + if (isStructTypeName(fullProtoTypeName)) { + if (ctx.options.useMapType) { + chunks.push(code`unwrap(message: Struct): {[key: string]: any} { + const object: { [key: string]: any } = {}; + [...message.fields.keys()].forEach((key) => { + object[key] = Value.unwrap(message.fields.get(key)); + }); + return object; + }`); + } else { + chunks.push(code`unwrap(message: Struct): {[key: string]: any} { + const object: { [key: string]: any } = {}; + if (message.fields) { + Object.keys(message.fields).forEach(key => { + object[key] = Value.unwrap(message.fields[key]); + }); + } + return object; + }`); + } + } + + if (isAnyValueTypeName(fullProtoTypeName)) { + // We check hasOwnProperty because the incoming `message` has been serde-ing + // by the NestJS/protobufjs runtime, and so has a base class with default values + // that throw off the simpler checks we do in generateUnwrapShallow + chunks.push(code`unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message?.hasOwnProperty('${fieldNames.stringValue}') && message.${fieldNames.stringValue} !== undefined) { + return message.${fieldNames.stringValue}; + } else if (message?.hasOwnProperty('${fieldNames.numberValue}') && message?.${fieldNames.numberValue} !== undefined) { + return message.${fieldNames.numberValue}; + } else if (message?.hasOwnProperty('${fieldNames.boolValue}') && message?.${fieldNames.boolValue} !== undefined) { + return message.${fieldNames.boolValue}; + } else if (message?.hasOwnProperty('${fieldNames.structValue}') && message?.${fieldNames.structValue} !== undefined) { + return Struct.unwrap(message.${fieldNames.structValue} as any); + } else if (message?.hasOwnProperty('${fieldNames.listValue}') && message?.${fieldNames.listValue} !== undefined) { + return ListValue.unwrap(message.${fieldNames.listValue}); + } else if (message?.hasOwnProperty('${fieldNames.nullValue}') && message?.${fieldNames.nullValue} !== undefined) { + return null; + } + return undefined; + }`); + } + + if (isListValueTypeName(fullProtoTypeName)) { + chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array { + if (message?.hasOwnProperty('values') && Array.isArray(message.values)) { + return message.values.map(Value.unwrap); + } else { + return message as any; + } + }`); + } + + if (isFieldMaskTypeName(fullProtoTypeName)) { + chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] { + return message.paths; + }`); + } + + return chunks; +} + +/** + * Converts ts-proto's idiomatic Struct/Value/ListValue representation to the proto messages. + * + * We do this shallow's b/c ts-proto's encode methods handle the recursion. + */ +export function generateWrapShallow(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { + const chunks: Code[] = []; + if (isStructTypeName(fullProtoTypeName)) { + let setStatement = "struct.fields[key] = object[key];"; + if (ctx.options.useMapType) { + setStatement = "struct.fields.set(key, object[key]);"; + } + chunks.push(code`wrap(object: {[key: string]: any} | undefined): Struct { + const struct = createBaseStruct(); + if (object !== undefined) { + Object.keys(object).forEach(key => { + ${setStatement} + }); + } + return struct; + }`); + } + + if (isAnyValueTypeName(fullProtoTypeName)) { + if (ctx.options.oneof === OneofOption.UNIONS) { + chunks.push(code`wrap(value: any): Value { + const result = createBaseValue()${maybeAsAny(ctx.options)}; + if (value === null) { + result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE}; + } else if (typeof value === 'boolean') { + result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value}; + } else if (typeof value === 'number') { + result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value}; + } else if (typeof value === 'string') { + result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value}; + } else if (Array.isArray(value)) { + result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value}; + } else if (typeof value === 'object') { + result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value}; + } else if (typeof value !== 'undefined') { + throw new Error('Unsupported any value type: ' + typeof value); + } + return result; + }`); + } else { + chunks.push(code`wrap(value: any): Value { + const result = createBaseValue()${maybeAsAny(ctx.options)}; + if (value === null) { + result.${fieldNames.nullValue} = NullValue.NULL_VALUE; + } else if (typeof value === 'boolean') { + result.${fieldNames.boolValue} = value; + } else if (typeof value === 'number') { + result.${fieldNames.numberValue} = value; + } else if (typeof value === 'string') { + result.${fieldNames.stringValue} = value; + } else if (Array.isArray(value)) { + result.${fieldNames.listValue} = value; + } else if (typeof value === 'object') { + result.${fieldNames.structValue} = value; + } else if (typeof value !== 'undefined') { + throw new Error('Unsupported any value type: ' + typeof value); + } + return result; + }`); + } + } + + if (isListValueTypeName(fullProtoTypeName)) { + const maybeReadyOnly = ctx.options.useReadonlyTypes ? "Readonly" : ""; + chunks.push(code`wrap(array: ${maybeReadyOnly}Array | undefined): ListValue { + const result = createBaseListValue()${maybeAsAny(ctx.options)}; + result.values = array ?? []; + return result; + }`); + } + + if (isFieldMaskTypeName(fullProtoTypeName)) { + chunks.push(code`wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask { + const result = createBaseFieldMask()${maybeAsAny(ctx.options)}; + result.paths = paths; + return result; + }`); + } + + return chunks; +} + +/** + * Converts proto's Struct/Value?listValue messages to ts-proto's idiomatic representation. + * + * We do this shallowly b/c ts-proto's decode methods handle recursion. + */ +export function generateUnwrapShallow(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { + const chunks: Code[] = []; + if (isStructTypeName(fullProtoTypeName)) { + if (ctx.options.useMapType) { + chunks.push(code`unwrap(message: Struct): {[key: string]: any} { + const object: { [key: string]: any } = {}; + [...message.fields.keys()].forEach((key) => { + object[key] = message.fields.get(key); + }); + return object; + }`); + } else { + chunks.push(code`unwrap(message: Struct): {[key: string]: any} { + const object: { [key: string]: any } = {}; + if (message.fields) { + Object.keys(message.fields).forEach(key => { + object[key] = message.fields[key]; + }); + } + return object; + }`); + } + } + + if (isAnyValueTypeName(fullProtoTypeName)) { + if (ctx.options.oneof === OneofOption.UNIONS) { + chunks.push(code`unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { + if (message.kind?.$case === '${fieldNames.nullValue}') { + return null; + } else if (message.kind?.$case === '${fieldNames.numberValue}') { + return message.kind?.${fieldNames.numberValue}; + } else if (message.kind?.$case === '${fieldNames.stringValue}') { + return message.kind?.${fieldNames.stringValue}; + } else if (message.kind?.$case === '${fieldNames.boolValue}') { + return message.kind?.${fieldNames.boolValue}; + } else if (message.kind?.$case === '${fieldNames.structValue}') { + return message.kind?.${fieldNames.structValue}; + } else if (message.kind?.$case === '${fieldNames.listValue}') { + return message.kind?.${fieldNames.listValue}; + } else { + return undefined; + } + }`); + } else { + chunks.push(code`unwrap(message: any): string | number | boolean | Object | null | Array | undefined { + if (message.${fieldNames.stringValue} !== undefined) { + return message.${fieldNames.stringValue}; + } else if (message?.${fieldNames.numberValue} !== undefined) { + return message.${fieldNames.numberValue}; + } else if (message?.${fieldNames.boolValue} !== undefined) { + return message.${fieldNames.boolValue}; + } else if (message?.${fieldNames.structValue} !== undefined) { + return message.${fieldNames.structValue} as any; + } else if (message?.${fieldNames.listValue} !== undefined) { + return message.${fieldNames.listValue}; + } else if (message?.${fieldNames.nullValue} !== undefined) { + return null; + } + return undefined; + }`); + } + } + + if (isListValueTypeName(fullProtoTypeName)) { + chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array { + if (message?.hasOwnProperty('values') && Array.isArray(message.values)) { + return message.values; + } else { + return message as any; + } + }`); + } + + if (isFieldMaskTypeName(fullProtoTypeName)) { + chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] { + return message.paths; + }`); + } + + return chunks; +} + +function maybeReadonly(options: Options): string { + return options.useReadonlyTypes ? "readonly " : ""; +} + +function maybeAsAny(options: Options): string { + return options.useReadonlyTypes ? " as any" : ""; +} diff --git a/src/main.ts b/src/main.ts index 67926acc8..2bcabc23b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -78,6 +78,15 @@ import { ConditionalOutput } from "ts-poet/build/ConditionalOutput"; import { generateGrpcJsService } from "./generate-grpc-js"; import { generateGenericServiceDefinition } from "./generate-generic-service-definition"; import { generateNiceGrpcService } from "./generate-nice-grpc"; +import { + generateUnwrap, + generateUnwrapDeep, + generateUnwrapShallow, + generateWrap, + generateWrapDeep, + generateWrapShallow, + isWrapperType, +} from "./generate-struct-wrappers"; export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [string, Code] { const { options, utils } = ctx; @@ -156,15 +165,22 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri } } - if (options.outputEncodeMethods || options.outputJsonMethods || options.outputTypeRegistry) { + // We add `nestJs` here because enough though it doesn't use our encode/decode methods + // for most/vanilla messages, we do generate static wrap/unwrap methods for the special + // Struct/Value/wrapper types and use the `wrappers[...]` to have NestJS know about them. + if (options.outputEncodeMethods || options.outputJsonMethods || options.outputTypeRegistry || options.nestJs) { // then add the encoder/decoder/base instance visit( fileDesc, sourceInfo, (fullName, message, _sInfo, fullProtoTypeName) => { const fullTypeName = maybePrefixPackage(fileDesc, fullProtoTypeName); + const outputWrapAndUnwrap = isWrapperType(fullTypeName); - chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName)); + // Only decode, fromPartial, and wrap use the createBase method + if (options.outputEncodeMethods || options.outputPartialMethods || outputWrapAndUnwrap) { + chunks.push(generateBaseInstanceFactory(ctx, fullName, message, fullTypeName)); + } const staticMembers: Code[] = []; @@ -196,18 +212,24 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri structValue: maybeSnakeToCamel("struct_value", ctx.options), listValue: maybeSnakeToCamel("list_value", ctx.options), }; - staticMembers.push(...generateWrap(ctx, fullTypeName, structFieldNames)); - staticMembers.push(...generateUnwrap(ctx, fullTypeName, structFieldNames)); + if (options.nestJs) { + staticMembers.push(...generateWrapDeep(ctx, fullTypeName, structFieldNames)); + staticMembers.push(...generateUnwrapDeep(ctx, fullTypeName, structFieldNames)); + } else { + staticMembers.push(...generateWrapShallow(ctx, fullTypeName, structFieldNames)); + staticMembers.push(...generateUnwrapShallow(ctx, fullTypeName, structFieldNames)); + } - chunks.push(code` - export const ${def(fullName)} = { - ${joinCode(staticMembers, { on: ",\n\n" })} - }; - `); + if (staticMembers.length > 0) { + chunks.push(code` + export const ${def(fullName)} = { + ${joinCode(staticMembers, { on: ",\n\n" })} + }; + `); + } if (options.outputTypeRegistry) { const messageTypeRegistry = impFile(options, "messageTypeRegistry@./typeRegistry"); - chunks.push(code` ${messageTypeRegistry}.set(${fullName}.$type, ${fullName}); `); @@ -217,6 +239,12 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri ); } + if (options.nestJs) { + if (fileDesc.messageType.find((message) => message.field.find(isStructType))) { + chunks.push(makeProtobufStructWrapper(options)); + } + } + let hasServerStreamingMethods = false; let hasStreamingMethods = false; @@ -360,6 +388,16 @@ function makeProtobufTimestampWrapper() { } as any;`; } +function makeProtobufStructWrapper(options: Options) { + const wrappers = imp("wrappers@protobufjs"); + const Struct = impProto(options, "google/protobuf/struct", "Struct"); + return code` + ${wrappers}['.google.protobuf.Struct'] = { + fromObject: ${Struct}.wrap, + toObject: ${Struct}.unwrap, + } as any;`; +} + function makeLongUtils(options: Options, bytes: ReturnType) { // Regardless of which `forceLong` config option we're using, we always use // the `long` library to either represent or at least sanity-check 64-bit values @@ -1759,183 +1797,6 @@ function generateFromPartial(ctx: Context, fullName: string, messageDesc: Descri return joinCode(chunks, { on: "\n" }); } -type StructFieldNames = { - nullValue: string; - numberValue: string; - stringValue: string; - boolValue: string; - structValue: string; - listValue: string; -}; - -function generateWrap(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { - const chunks: Code[] = []; - if (isStructTypeName(fullProtoTypeName)) { - let setStatement = "struct.fields[key] = object[key];"; - if (ctx.options.useMapType) { - setStatement = "struct.fields.set(key, object[key]);"; - } - - chunks.push(code`wrap(object: {[key: string]: any} | undefined): Struct { - const struct = createBaseStruct(); - if (object !== undefined) { - Object.keys(object).forEach(key => { - ${setStatement} - }); - } - return struct; - }`); - } - - if (isAnyValueTypeName(fullProtoTypeName)) { - if (ctx.options.oneof === OneofOption.UNIONS) { - chunks.push(code`wrap(value: any): Value { - const result = createBaseValue()${maybeAsAny(ctx.options)}; - - if (value === null) { - result.kind = {$case: '${fieldNames.nullValue}', ${fieldNames.nullValue}: NullValue.NULL_VALUE}; - } else if (typeof value === 'boolean') { - result.kind = {$case: '${fieldNames.boolValue}', ${fieldNames.boolValue}: value}; - } else if (typeof value === 'number') { - result.kind = {$case: '${fieldNames.numberValue}', ${fieldNames.numberValue}: value}; - } else if (typeof value === 'string') { - result.kind = {$case: '${fieldNames.stringValue}', ${fieldNames.stringValue}: value}; - } else if (Array.isArray(value)) { - result.kind = {$case: '${fieldNames.listValue}', ${fieldNames.listValue}: value}; - } else if (typeof value === 'object') { - result.kind = {$case: '${fieldNames.structValue}', ${fieldNames.structValue}: value}; - } else if (typeof value !== 'undefined') { - throw new Error('Unsupported any value type: ' + typeof value); - } - - return result; - }`); - } else { - chunks.push(code`wrap(value: any): Value { - const result = createBaseValue()${maybeAsAny(ctx.options)}; - - if (value === null) { - result.${fieldNames.nullValue} = NullValue.NULL_VALUE; - } else if (typeof value === 'boolean') { - result.${fieldNames.boolValue} = value; - } else if (typeof value === 'number') { - result.${fieldNames.numberValue} = value; - } else if (typeof value === 'string') { - result.${fieldNames.stringValue} = value; - } else if (Array.isArray(value)) { - result.${fieldNames.listValue} = value; - } else if (typeof value === 'object') { - result.${fieldNames.structValue} = value; - } else if (typeof value !== 'undefined') { - throw new Error('Unsupported any value type: ' + typeof value); - } - - return result; - }`); - } - } - - if (isListValueTypeName(fullProtoTypeName)) { - chunks.push(code`wrap(value: ${ - ctx.options.useReadonlyTypes ? "ReadonlyArray" : "Array" - } | undefined): ListValue { - const result = createBaseListValue()${maybeAsAny(ctx.options)}; - - result.values = value ?? []; - - return result; - }`); - } - - if (isFieldMaskTypeName(fullProtoTypeName)) { - chunks.push(code`wrap(paths: ${maybeReadonly(ctx.options)} string[]): FieldMask { - const result = createBaseFieldMask()${maybeAsAny(ctx.options)}; - - result.paths = paths; - - return result; - }`); - } - - return chunks; -} - -function generateUnwrap(ctx: Context, fullProtoTypeName: string, fieldNames: StructFieldNames): Code[] { - const chunks: Code[] = []; - if (isStructTypeName(fullProtoTypeName)) { - if (ctx.options.useMapType) { - chunks.push(code`unwrap(message: Struct): {[key: string]: any} { - const object: { [key: string]: any } = {}; - [...message.fields.keys()].forEach((key) => { - object[key] = message.fields.get(key); - }); - return object; - }`); - } else { - chunks.push(code`unwrap(message: Struct): {[key: string]: any} { - const object: { [key: string]: any } = {}; - Object.keys(message.fields).forEach(key => { - object[key] = message.fields[key]; - }); - return object; - }`); - } - } - - if (isAnyValueTypeName(fullProtoTypeName)) { - if (ctx.options.oneof === OneofOption.UNIONS) { - chunks.push(code`unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message.kind?.$case === '${fieldNames.nullValue}') { - return null; - } else if (message.kind?.$case === '${fieldNames.numberValue}') { - return message.kind?.${fieldNames.numberValue}; - } else if (message.kind?.$case === '${fieldNames.stringValue}') { - return message.kind?.${fieldNames.stringValue}; - } else if (message.kind?.$case === '${fieldNames.boolValue}') { - return message.kind?.${fieldNames.boolValue}; - } else if (message.kind?.$case === '${fieldNames.structValue}') { - return message.kind?.${fieldNames.structValue}; - } else if (message.kind?.$case === '${fieldNames.listValue}') { - return message.kind?.${fieldNames.listValue}; - } else { - return undefined; - } - }`); - } else { - chunks.push(code`unwrap(message: Value): string | number | boolean | Object | null | Array | undefined { - if (message?.${fieldNames.stringValue} !== undefined) { - return message.${fieldNames.stringValue}; - } else if (message?.${fieldNames.numberValue} !== undefined) { - return message.${fieldNames.numberValue}; - } else if (message?.${fieldNames.boolValue} !== undefined) { - return message.${fieldNames.boolValue}; - } else if (message?.${fieldNames.structValue} !== undefined) { - return message.${fieldNames.structValue}; - } else if (message?.${fieldNames.listValue} !== undefined) { - return message.${fieldNames.listValue}; - } else if (message?.${fieldNames.nullValue} !== undefined) { - return null; - } - return undefined; - }`); - } - } - - if (isListValueTypeName(fullProtoTypeName)) { - chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "ListValue"}): Array { - return message.values; - }`); - } - - if (isFieldMaskTypeName(fullProtoTypeName)) { - chunks.push(code`unwrap(message: ${ctx.options.useReadonlyTypes ? "any" : "FieldMask"}): string[] { - return message.paths; - }`); - } - - return chunks; -} - export const contextTypeVar = "Context extends DataLoaders"; function maybeCastToNumber( diff --git a/yarn.lock b/yarn.lock index b8a63353a..eaae7939c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -530,14 +530,13 @@ __metadata: languageName: node linkType: hard -"@grpc/grpc-js@npm:^1.2.12": - version: 1.2.12 - resolution: "@grpc/grpc-js@npm:1.2.12" +"@grpc/grpc-js@npm:^1.3.0": + version: 1.8.4 + resolution: "@grpc/grpc-js@npm:1.8.4" dependencies: + "@grpc/proto-loader": ^0.7.0 "@types/node": ">=12.12.47" - google-auth-library: ^6.1.1 - semver: ^6.2.0 - checksum: 366ecbfab45d7e59fb071c7ca064ba9fb12cfa24abc6c9d65b1dd9842b3b2bbcfb975b17987a92c7aa00f36389305758099bda284e57d7a95ea89637ae22222c + checksum: dfec3a47dab85eb92e7d1f42c8ad336dd465d3b7d3af08d7c189cc2b267dc4ca45d95e4885c94a9be41a2114dbad04ea839ed2252ccc051882a6856c8f0dc641 languageName: node linkType: hard @@ -551,13 +550,18 @@ __metadata: languageName: node linkType: hard -"@grpc/proto-loader@npm:^0.5.6": - version: 0.5.6 - resolution: "@grpc/proto-loader@npm:0.5.6" +"@grpc/proto-loader@npm:^0.6.0": + version: 0.6.13 + resolution: "@grpc/proto-loader@npm:0.6.13" dependencies: + "@types/long": ^4.0.1 lodash.camelcase: ^4.3.0 - protobufjs: ^6.8.6 - checksum: 13fe76d84ab1a516f3dc47d06df4dd682f6f1515a7a4aa3f8cddcc8f8256f33cbf529bd0b6729946f548f7459acfcd9b5b026c10572e21d40213a358115658b5 + long: ^4.0.0 + protobufjs: ^6.11.3 + yargs: ^16.2.0 + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 863417e961cfa3acb579124f5c2bbfbeaee4d507c33470dc0af3b6792892c68706c6c61e26629f5ff3d28cb631dc4f0a00233323135e322406e3cb19a0b92823 languageName: node linkType: hard @@ -576,6 +580,21 @@ __metadata: languageName: node linkType: hard +"@grpc/proto-loader@npm:^0.7.0": + version: 0.7.4 + resolution: "@grpc/proto-loader@npm:0.7.4" + dependencies: + "@types/long": ^4.0.1 + lodash.camelcase: ^4.3.0 + long: ^4.0.0 + protobufjs: ^7.0.0 + yargs: ^16.2.0 + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 7789a959060535287a74cef8e13783e9a1506ae22365a48e0cfb29f48697ac946b461fe12ee711d280c4690a333c705f504076303a806f2fef81cc3e532637ac + languageName: node + linkType: hard + "@improbable-eng/grpc-web-node-http-transport@npm:^0.14.0": version: 0.14.0 resolution: "@improbable-eng/grpc-web-node-http-transport@npm:0.14.0" @@ -920,12 +939,12 @@ __metadata: linkType: hard "@nestjs/common@npm:^8.2.2": - version: 8.2.2 - resolution: "@nestjs/common@npm:8.2.2" + version: 8.4.7 + resolution: "@nestjs/common@npm:8.4.7" dependencies: - axios: 0.24.0 + axios: 0.27.2 iterare: 1.2.1 - tslib: 2.3.1 + tslib: 2.4.0 uuid: 8.3.2 peerDependencies: cache-manager: "*" @@ -940,20 +959,20 @@ __metadata: optional: true class-validator: optional: true - checksum: 5d74143630d9bbe28d18bb9c47a948f84c2b62aed0974de0963ec3e94da9964b9b214da3622b4b6970456dfe03dc12439728c57570ffca5e2283b4fe3ac6daee + checksum: b3fa20618d4e60f9f5089215c800f2deb5d08b9f88443102b066036d7bacf17ff5926ad5ff0ef7ace42feb8e0ca561dc7e75fd05dbd028f5e675d5e7062eb685 languageName: node linkType: hard "@nestjs/core@npm:^8.2.2": - version: 8.2.2 - resolution: "@nestjs/core@npm:8.2.2" + version: 8.4.7 + resolution: "@nestjs/core@npm:8.4.7" dependencies: "@nuxtjs/opencollective": 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 - object-hash: 2.2.0 + object-hash: 3.0.0 path-to-regexp: 3.2.0 - tslib: 2.3.1 + tslib: 2.4.0 uuid: 8.3.2 peerDependencies: "@nestjs/common": ^8.0.0 @@ -969,17 +988,16 @@ __metadata: optional: true "@nestjs/websockets": optional: true - checksum: 5dee8d70b9c8e09c3810f028fdaa91b0025fd11bf2fd47b74d44ee44b6cce9245709f3b69a1661be23ba67688ed67eeb85770a2d0c1812534429976ba103d8e8 + checksum: 58d18b04e03cdae4b06e06f1a60705cdbe2e81824e0a85d0be422cf8dbd39c263287196b85939718659d7eeedc2856f29744487931dbe5c2695fcd374a5fbe40 languageName: node linkType: hard "@nestjs/microservices@npm:^8.2.2": - version: 8.2.2 - resolution: "@nestjs/microservices@npm:8.2.2" + version: 8.4.7 + resolution: "@nestjs/microservices@npm:8.4.7" dependencies: iterare: 1.2.1 - json-socket: 0.3.0 - tslib: 2.3.1 + tslib: 2.4.0 peerDependencies: "@grpc/grpc-js": "*" "@nestjs/common": ^8.0.0 @@ -1013,7 +1031,7 @@ __metadata: optional: true redis: optional: true - checksum: f81987a1773ddf76d89bacfb95c199a1a63753f009aa3df5d2b087ee047001373c136d73afda67ed692295128c23c15a70d1f4ef1fbc53e7c083db1ed26e9911 + checksum: 830946c2c9e696a0fc9e17f96702b6ec43651f327df43636fd32efeb3e9252869a8dc12b822e0f5598524b0b88b7cf497c9ccf1902de6fa3a481de06702e09d8 languageName: node linkType: hard @@ -2114,15 +2132,6 @@ __metadata: languageName: node linkType: hard -"abort-controller@npm:^3.0.0": - version: 3.0.0 - resolution: "abort-controller@npm:3.0.0" - dependencies: - event-target-shim: ^5.0.0 - checksum: 170bdba9b47b7e65906a28c8ce4f38a7a369d78e2271706f020849c1bfe0ee2067d4261df8bbb66eb84f79208fd5b710df759d64191db58cfba7ce8ef9c54b75 - languageName: node - linkType: hard - "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -2352,13 +2361,6 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^2.0.0": - version: 2.0.1 - resolution: "arrify@npm:2.0.1" - checksum: 067c4c1afd182806a82e4c1cb8acee16ab8b5284fbca1ce29408e6e91281c36bb5b612f6ddfbd40a0f7a7e0c75bf2696eb94c027f6e328d6e9c52465c98e4209 - languageName: node - linkType: hard - "asap@npm:^2.0.0": version: 2.0.6 resolution: "asap@npm:2.0.6" @@ -2410,12 +2412,13 @@ __metadata: languageName: node linkType: hard -"axios@npm:0.24.0": - version: 0.24.0 - resolution: "axios@npm:0.24.0" +"axios@npm:0.27.2": + version: 0.27.2 + resolution: "axios@npm:0.27.2" dependencies: - follow-redirects: ^1.14.4 - checksum: 468cf496c08a6aadfb7e699bebdac02851e3043d4e7d282350804ea8900e30d368daa6e3cd4ab83b8ddb5a3b1e17a5a21ada13fc9cebd27b74828f47a4236316 + follow-redirects: ^1.14.9 + form-data: ^4.0.0 + checksum: 38cb7540465fe8c4102850c4368053c21683af85c5fdf0ea619f9628abbcb59415d1e22ebc8a6390d2bbc9b58a9806c874f139767389c862ec9b772235f06854 languageName: node linkType: hard @@ -2502,7 +2505,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -2525,13 +2528,6 @@ __metadata: languageName: node linkType: hard -"bignumber.js@npm:^9.0.0": - version: 9.0.1 - resolution: "bignumber.js@npm:9.0.1" - checksum: 6e72f6069d9db32fc8d27561164de9f811b15f9144be61f323d8b36150a239eea50c92e20ba38af2ba5e717af10b8ef12db8f9948fe2ff02bf17ede5239d15d3 - languageName: node - linkType: hard - "bin-links@npm:^2.2.1": version: 2.2.1 resolution: "bin-links@npm:2.2.1" @@ -2650,13 +2646,6 @@ __metadata: languageName: node linkType: hard -"buffer-equal-constant-time@npm:1.0.1": - version: 1.0.1 - resolution: "buffer-equal-constant-time@npm:1.0.1" - checksum: 80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.1 resolution: "buffer-from@npm:1.1.1" @@ -3093,7 +3082,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.6, combined-stream@npm:~1.0.6": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -3512,15 +3501,6 @@ __metadata: languageName: node linkType: hard -"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": - version: 1.0.11 - resolution: "ecdsa-sig-formatter@npm:1.0.11" - dependencies: - safe-buffer: ^5.0.1 - checksum: 207f9ab1c2669b8e65540bce29506134613dd5f122cccf1e6a560f4d63f2732d427d938f8481df175505aad94583bcb32c688737bb39a6df0625f903d6d93c03 - languageName: node - linkType: hard - "electron-to-chromium@npm:^1.4.172": version: 1.4.177 resolution: "electron-to-chromium@npm:1.4.177" @@ -3838,13 +3818,6 @@ __metadata: languageName: node linkType: hard -"event-target-shim@npm:^5.0.0": - version: 5.0.1 - resolution: "event-target-shim@npm:5.0.1" - checksum: 1ffe3bb22a6d51bdeb6bf6f7cf97d2ff4a74b017ad12284cc9e6a279e727dc30a5de6bb613e5596ff4dc3e517841339ad09a7eec44266eccb1aa201a30448166 - languageName: node - linkType: hard - "execa@npm:^4.0.0": version: 4.1.0 resolution: "execa@npm:4.1.0" @@ -3899,7 +3872,7 @@ __metadata: languageName: node linkType: hard -"extend@npm:^3.0.2, extend@npm:~3.0.2": +"extend@npm:~3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 @@ -3948,13 +3921,6 @@ __metadata: languageName: node linkType: hard -"fast-text-encoding@npm:^1.0.0": - version: 1.0.3 - resolution: "fast-text-encoding@npm:1.0.3" - checksum: 3e51365896f06d0dcab128092d095a0037d274deec419fecbd2388bc236d7b387610e0c72f920c6126e00c885ab096fbfaa3645712f5b98f721bef6b064916a8 - languageName: node - linkType: hard - "fastest-levenshtein@npm:^1.0.12": version: 1.0.12 resolution: "fastest-levenshtein@npm:1.0.12" @@ -4035,13 +4001,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.4": - version: 1.14.8 - resolution: "follow-redirects@npm:1.14.8" +"follow-redirects@npm:^1.14.9": + version: 1.15.2 + resolution: "follow-redirects@npm:1.15.2" peerDependenciesMeta: debug: optional: true - checksum: 40c67899c2e3149a27e8b6498a338ff27f39fe138fde8d7f0756cb44b073ba0bfec3d52af28f20c5bdd67263d564d0d8d7b5efefd431de95c18c42f7b4aef457 + checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 languageName: node linkType: hard @@ -4052,6 +4018,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.8 + mime-types: ^2.1.12 + checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c + languageName: node + linkType: hard + "form-data@npm:~2.3.2": version: 2.3.3 resolution: "form-data@npm:2.3.3" @@ -4170,29 +4147,6 @@ __metadata: languageName: node linkType: hard -"gaxios@npm:^4.0.0": - version: 4.2.0 - resolution: "gaxios@npm:4.2.0" - dependencies: - abort-controller: ^3.0.0 - extend: ^3.0.2 - https-proxy-agent: ^5.0.0 - is-stream: ^2.0.0 - node-fetch: ^2.3.0 - checksum: 85b45adcf8eaee849d74816987bcf74fb777d97408e546c76d373a349a727dd7e2694e6d2de77963cf2ae12a76db203dba066e9a4c2069793a4ce8a29323da52 - languageName: node - linkType: hard - -"gcp-metadata@npm:^4.2.0": - version: 4.2.1 - resolution: "gcp-metadata@npm:4.2.1" - dependencies: - gaxios: ^4.0.0 - json-bigint: ^1.0.0 - checksum: 970908f7c74684787f1e76f82b1303f13a65882be8946144cbaf7b9236d5315d045bdba6a1bc15b26b2ac70303511bd9f05dcb23023e8c9241486abc6ceac619 - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -4317,34 +4271,6 @@ __metadata: languageName: node linkType: hard -"google-auth-library@npm:^6.1.1": - version: 6.1.6 - resolution: "google-auth-library@npm:6.1.6" - dependencies: - arrify: ^2.0.0 - base64-js: ^1.3.0 - ecdsa-sig-formatter: ^1.0.11 - fast-text-encoding: ^1.0.0 - gaxios: ^4.0.0 - gcp-metadata: ^4.2.0 - gtoken: ^5.0.4 - jws: ^4.0.0 - lru-cache: ^6.0.0 - checksum: d65fcb43c8c43763dbe086e374675fbef6d55630d68726110f3f96ce3876c5cf36d3de7362b4421efa76979509f92de38268a65486c076055099a5104d4b73db - languageName: node - linkType: hard - -"google-p12-pem@npm:^3.0.3": - version: 3.0.3 - resolution: "google-p12-pem@npm:3.0.3" - dependencies: - node-forge: ^0.10.0 - bin: - gp12-pem: build/src/bin/gp12-pem.js - checksum: b4698748bb14356ce19fdcfe5d58922704a1719f34063e0a2c2533624cd47a0d329bab17464b8f7f21ac1b221e1c5e24aaaa5fe53f5433d6ffa2bcb2d0baf1a2 - languageName: node - linkType: hard - "graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.3, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": version: 4.2.6 resolution: "graceful-fs@npm:4.2.6" @@ -4359,17 +4285,6 @@ __metadata: languageName: node linkType: hard -"gtoken@npm:^5.0.4": - version: 5.2.1 - resolution: "gtoken@npm:5.2.1" - dependencies: - gaxios: ^4.0.0 - google-p12-pem: ^3.0.3 - jws: ^4.0.0 - checksum: fcf476535cc698fb96b8a9438a0396b434294aa5120f39b32dfc49856ef947088764c6c3c5d0b79dbe374dea4cf5cb8ba328e366753f70e3cea607a34fe8d8dd - languageName: node - linkType: hard - "handlebars@npm:^4.7.6, handlebars@npm:^4.7.7": version: 4.7.7 resolution: "handlebars@npm:4.7.7" @@ -5526,15 +5441,6 @@ __metadata: languageName: node linkType: hard -"json-bigint@npm:^1.0.0": - version: 1.0.0 - resolution: "json-bigint@npm:1.0.0" - dependencies: - bignumber.js: ^9.0.0 - checksum: c67bb93ccb3c291e60eb4b62931403e378906aab113ec1c2a8dd0f9a7f065ad6fd9713d627b732abefae2e244ac9ce1721c7a3142b2979532f12b258634ce6f6 - languageName: node - linkType: hard - "json-parse-better-errors@npm:^1.0.1": version: 1.0.2 resolution: "json-parse-better-errors@npm:1.0.2" @@ -5563,13 +5469,6 @@ __metadata: languageName: node linkType: hard -"json-socket@npm:0.3.0": - version: 0.3.0 - resolution: "json-socket@npm:0.3.0" - checksum: 43e112523547c26a7f54259ac2cc222276d59a619d4d467554d8903f4d56752fd5ed6e23f27b14767dbe55bf0d8eab98640e0c09825fdf329cb8257e38eb6a45 - languageName: node - linkType: hard - "json-stringify-nice@npm:^1.1.2": version: 1.1.3 resolution: "json-stringify-nice@npm:1.1.3" @@ -5660,27 +5559,6 @@ __metadata: languageName: node linkType: hard -"jwa@npm:^2.0.0": - version: 2.0.0 - resolution: "jwa@npm:2.0.0" - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: ^5.0.1 - checksum: 8f00b71ad5fe94cb55006d0d19202f8f56889109caada2f7eeb63ca81755769ce87f4f48101967f398462e3b8ae4faebfbd5a0269cb755dead5d63c77ba4d2f1 - languageName: node - linkType: hard - -"jws@npm:^4.0.0": - version: 4.0.0 - resolution: "jws@npm:4.0.0" - dependencies: - jwa: ^2.0.0 - safe-buffer: ^5.0.1 - checksum: d68d07aa6d1b8cb35c363a9bd2b48f15064d342a5d9dc18a250dbbce8dc06bd7e4792516c50baa16b8d14f61167c19e851fd7f66b59ecc68b7f6a013759765f7 - languageName: node - linkType: hard - "kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -6056,6 +5934,13 @@ __metadata: languageName: node linkType: hard +"long@npm:^5.0.0": + version: 5.2.1 + resolution: "long@npm:5.2.1" + checksum: 9264da12d1b7df67e5aa6da4498144293caf1ad12e7f092efe4e9a2d32c53f0bbf7334f7cef997080a2a3af061142558ab366efa71698d98b1cdb883477445a7 + languageName: node + linkType: hard + "lru-cache@npm:^6.0.0": version: 6.0.0 resolution: "lru-cache@npm:6.0.0" @@ -6589,7 +6474,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.3.0, node-fetch@npm:^2.6.1": +"node-fetch@npm:^2.6.1": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" dependencies: @@ -6603,13 +6488,6 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^0.10.0": - version: 0.10.0 - resolution: "node-forge@npm:0.10.0" - checksum: 5aa6dc9922e424a20ef101d2f517418e2bc9cfc0255dd22e0701c0fad1568445f510ee67f6f3fcdf085812c4ca1b847b8ba45683b34776828e41f5c1794e42e1 - languageName: node - linkType: hard - "node-gyp@npm:^7.1.0, node-gyp@npm:^7.1.2": version: 7.1.2 resolution: "node-gyp@npm:7.1.2" @@ -7168,10 +7046,10 @@ __metadata: languageName: node linkType: hard -"object-hash@npm:2.2.0": - version: 2.2.0 - resolution: "object-hash@npm:2.2.0" - checksum: 55ba841e3adce9c4f1b9b46b41983eda40f854e0d01af2802d3ae18a7085a17168d6b81731d43fdf1d6bcbb3c9f9c56d22c8fea992203ad90a38d7d919bc28f1 +"object-hash@npm:3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: 80b4904bb3857c52cc1bfd0b52c0352532ca12ed3b8a6ff06a90cd209dfda1b95cee059a7625eb9da29537027f68ac4619363491eedb2f5d3dddbba97494fd6c languageName: node linkType: hard @@ -7695,7 +7573,7 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:^6.8.6, protobufjs@npm:^6.8.8": +"protobufjs@npm:^6.8.8": version: 6.10.2 resolution: "protobufjs@npm:6.10.2" dependencies: @@ -7719,6 +7597,26 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.0.0": + version: 7.1.2 + resolution: "protobufjs@npm:7.1.2" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/node": ">=13.7.0" + long: ^5.0.0 + checksum: ae41669b1b0372fb1d49f506f2d1f2b0fb3dc3cece85987b17bcb544e4cef7c8d27f480486cdec324146ad0a5d22a327166a7ea864a9b3e49cc3c92a5d3f6500 + languageName: node + linkType: hard + "psl@npm:^1.1.28": version: 1.8.0 resolution: "psl@npm:1.8.0" @@ -8212,7 +8110,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0": +"semver@npm:^6.0.0, semver@npm:^6.3.0": version: 6.3.0 resolution: "semver@npm:6.3.0" bin: @@ -8947,8 +8845,8 @@ __metadata: version: 0.0.0-use.local resolution: "ts-proto@workspace:." dependencies: - "@grpc/grpc-js": ^1.2.12 - "@grpc/proto-loader": ^0.5.6 + "@grpc/grpc-js": ^1.3.0 + "@grpc/proto-loader": ^0.6.0 "@improbable-eng/grpc-web": ^0.14.0 "@improbable-eng/grpc-web-node-http-transport": ^0.14.0 "@nestjs/common": ^8.2.2 @@ -8988,10 +8886,10 @@ __metadata: languageName: unknown linkType: soft -"tslib@npm:2.3.1": - version: 2.3.1 - resolution: "tslib@npm:2.3.1" - checksum: de17a98d4614481f7fcb5cd53ffc1aaf8654313be0291e1bfaee4b4bb31a20494b7d218ff2e15017883e8ea9626599b3b0e0229c18383ba9dce89da2adf15cb9 +"tslib@npm:2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 8c4aa6a3c5a754bf76aefc38026134180c053b7bd2f81338cb5e5ebf96fefa0f417bff221592bf801077f5bf990562f6264fecbc42cd3309b33872cb6fc3b113 languageName: node linkType: hard