From e35f971022bf987669d1a531398d5fa5fc479b08 Mon Sep 17 00:00:00 2001 From: jevin Date: Thu, 15 May 2025 10:05:37 -0400 Subject: [PATCH 1/4] Creating metadata object to not expose raw protobuf --- src/v1.test.ts | 183 ++++++++++++++++++++++++++++++++++++++++++++++++- src/v1.ts | 18 +++++ 2 files changed, 200 insertions(+), 1 deletion(-) diff --git a/src/v1.test.ts b/src/v1.test.ts index d40a70a..a91b210 100644 --- a/src/v1.test.ts +++ b/src/v1.test.ts @@ -1,4 +1,5 @@ import * as grpc from "@grpc/grpc-js"; +import {} from "@grpc/grpc-js"; import { generateTestToken } from "./__utils__/helpers.js"; import { Struct } from "./authzedapi/google/protobuf/struct.js"; import { PreconnectServices, deadlineInterceptor } from "./util.js"; @@ -18,6 +19,7 @@ import { LookupSubjectsResponse, NewClient, ObjectReference, +PermissionsServiceClient, Relationship, RelationshipUpdate, RelationshipUpdate_Operation, @@ -25,8 +27,10 @@ import { WriteRelationshipsRequest, WriteRelationshipsResponse, WriteSchemaRequest, + createStructFromObject, + PbNullValue, } from "./v1.js"; -import { describe, it, expect, beforeEach } from "vitest"; +import { describe, it, expect, beforeEach, vi } from "vitest"; describe("a check with an unknown namespace", () => { it("should raise a failed precondition", () => @@ -681,4 +685,181 @@ describe("Experimental Service", () => { }); }); })); + +describe("WriteRelationships with transaction metadata (Integration Test)", () => { + it("should successfully write relationships with metadata and verify metadata transmission", () => + new Promise((done, fail) => { + const testToken = generateTestToken("v1-int-tx-metadata"); + const client = NewClient( + testToken, + "localhost:50051", + ClientSecurity.INSECURE_LOCALHOST_ALLOWED, + PreconnectServices.SCHEMA_SERVICE | PreconnectServices.PERMISSIONS_SERVICE, + ); + + const writeSpy = vi.spyOn(PermissionsServiceClient.prototype, "writeRelationships"); + + const schema = ` + definition test/user {} + definition test/document { + relation viewer: test/user + permission view = viewer + } + `; + const writeSchemaRequest = WriteSchemaRequest.create({ schema }); + + client.writeSchema(writeSchemaRequest, (schemaErr, schemaResponse) => { + if (schemaErr) { + client.close(); + fail(schemaErr); + return; + } + expect(schemaResponse).toBeDefined(); + + const uniqueSuffix = Date.now(); + const resource = ObjectReference.create({ + objectType: "test/document", + objectId: `doc-${uniqueSuffix}`, + }); + + const user = ObjectReference.create({ + objectType: "test/user", + objectId: `user-${uniqueSuffix}`, + }); + + const updates = [ + RelationshipUpdate.create({ + relationship: Relationship.create({ + resource, + relation: "viewer", + subject: SubjectReference.create({ object: user }), + }), + operation: RelationshipUpdate_Operation.CREATE, + }), + ]; + + const metadataObject = { transaction_id: "test-tx-123", other_data: "sample" }; + const transactionMetadata = createStructFromObject(metadataObject); + + const writeRequest = WriteRelationshipsRequest.create({ + updates, + optionalTransactionMetadata: transactionMetadata, + }); + + client.writeRelationships(writeRequest, (err, response) => { + if (err) { + client.close(); + fail(err); + return; + } + + expect(err).toBeNull(); + expect(response).toBeDefined(); + expect(response?.writtenAt).toBeDefined(); + + expect(writeSpy).toHaveBeenCalledTimes(1); + + const actualRequest = writeSpy.mock.calls[0][0] as WriteRelationshipsRequest; + + expect(actualRequest.updates).toEqual(updates); + + expect(actualRequest.optionalTransactionMetadata).toBeDefined(); + expect(actualRequest.optionalTransactionMetadata).toEqual(transactionMetadata); + + const transactionIdField = actualRequest.optionalTransactionMetadata?.fields?.['transaction_id']; + expect(transactionIdField?.kind?.oneofKind).toBe('stringValue'); + if (transactionIdField?.kind?.oneofKind === 'stringValue') { + expect(transactionIdField.kind.stringValue).toBe("test-tx-123"); + } + + const otherDataField = actualRequest.optionalTransactionMetadata?.fields?.['other_data']; + expect(otherDataField?.kind?.oneofKind).toBe('stringValue'); + if (otherDataField?.kind?.oneofKind === 'stringValue') { + expect(otherDataField.kind.stringValue).toBe("sample"); + } + + client.close(); + done(); + }); + }); + })); +}); +}); + +describe("createStructFromObject unit tests", () => { + it("should convert a simple JS object with primitive types", () => { + const obj = { + stringProp: "hello", + numberProp: 123, + booleanProp: true, + }; + const struct = createStructFromObject(obj); + expect(struct.fields.stringProp?.kind.oneofKind).toBe('stringValue'); + expect(struct.fields.stringProp?.kind.oneofKind === 'stringValue' && struct.fields.stringProp?.kind.stringValue).toBe("hello"); + expect(struct.fields.numberProp?.kind.oneofKind).toBe('numberValue'); + expect(struct.fields.numberProp?.kind.oneofKind === 'numberValue' && struct.fields.numberProp?.kind.numberValue).toBe(123); + expect(struct.fields.booleanProp?.kind.oneofKind).toBe('boolValue'); + expect(struct.fields.booleanProp?.kind.oneofKind === 'boolValue' && struct.fields.booleanProp?.kind.boolValue).toBe(true); + }); + + it("should convert a JS object with null values", () => { + const obj = { + nullProp: null, + }; + const struct = createStructFromObject(obj); + expect(struct.fields.nullProp?.kind.oneofKind).toBe('nullValue'); + expect(struct.fields.nullProp?.kind.oneofKind === 'nullValue' && struct.fields.nullProp?.kind.nullValue).toBe(PbNullValue.NULL_VALUE); + }); + + it("should convert a JS object with nested objects", () => { + const obj = { + nestedProp: { + innerString: "world", + innerNumber: 456, + }, + }; + const struct = createStructFromObject(obj); + const nestedStruct = struct.fields.nestedProp?.kind.oneofKind === 'structValue' && struct.fields.nestedProp.kind.structValue; + expect(nestedStruct).toBeTruthy(); + if (nestedStruct) { + expect(nestedStruct.fields.innerString?.kind.oneofKind).toBe('stringValue'); + expect(nestedStruct.fields.innerString?.kind.oneofKind === 'stringValue' && nestedStruct.fields.innerString?.kind.stringValue).toBe("world"); + expect(nestedStruct.fields.innerNumber?.kind.oneofKind).toBe('numberValue'); + expect(nestedStruct.fields.innerNumber?.kind.oneofKind === 'numberValue' && nestedStruct.fields.innerNumber?.kind.numberValue).toBe(456); + } + }); + + it("should convert an empty JS object to an empty Struct", () => { + const obj = {}; + const struct = createStructFromObject(obj); + expect(Object.keys(struct.fields).length).toBe(0); + }); + + it("should throw an error for null input", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => createStructFromObject(null as any)).toThrow( + "Input data for createStructFromObject must be a non-null object." + ); + }); + + it("should throw an error for non-object input (string)", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => createStructFromObject("not an object" as any)).toThrow( + "Input data for createStructFromObject must be a non-null object." + ); + }); + + it("should throw an error for non-object input (number)", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => createStructFromObject(123 as any)).toThrow( + "Input data for createStructFromObject must be a non-null object." + ); + }); + + it("should throw an error for array input", () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + expect(() => createStructFromObject([] as any)).toThrow( + "Input data for createStructFromObject must be a non-null object." + ); + }); }); diff --git a/src/v1.ts b/src/v1.ts index e107e3f..5ad5327 100644 --- a/src/v1.ts +++ b/src/v1.ts @@ -17,6 +17,10 @@ import { } from "./util.js"; import type { OmitBaseMethods, PromisifiedClient } from "./types.js"; +import { Struct as ImportedPbStruct, NullValue as ImportedPbNullValue } from "./authzedapi/google/protobuf/struct.js"; +import type { JsonObject } from "@protobuf-ts/runtime"; + +export { ImportedPbStruct as PbStruct, ImportedPbNullValue as PbNullValue }; // A merge of the three generated gRPC clients, with their base methods omitted export type ZedDefaultClientInterface = OmitBaseMethods< @@ -344,6 +348,20 @@ export function NewClientWithChannelCredentials( ): ZedClientInterface { return ZedCombinedClient.create(endpoint, creds, preconnect, options); } +/** + * Creates a google.protobuf.Struct object suitable for use as + * optionalTransactionMetadata in WriteRelationshipsRequest. + * + * @param data A simple JavaScript object (e.g., { key: "value" }) to be converted into a Struct. + * @returns A google.protobuf.Struct object. + */ +export function createStructFromObject(data: JsonObject): ImportedPbStruct { + if (data === null || typeof data !== 'object' || Array.isArray(data)) { + // Or handle this case as per library's error handling philosophy + throw new Error('Input data for createStructFromObject must be a non-null object.'); + } + return ImportedPbStruct.fromJson(data); +} export * from "./authzedapi/authzed/api/v1/core.js"; export * from "./authzedapi/authzed/api/v1/experimental_service.js"; From 1ce4fdfea8a5185135975e6c888fd1e897466bdf Mon Sep 17 00:00:00 2001 From: jevin Date: Wed, 21 May 2025 10:53:18 -0400 Subject: [PATCH 2/4] prettier --- src/v1.test.ts | 222 +++++++++++++++++++++++++++++-------------------- src/v1.ts | 15 ++-- 2 files changed, 140 insertions(+), 97 deletions(-) diff --git a/src/v1.test.ts b/src/v1.test.ts index a91b210..a2302a8 100644 --- a/src/v1.test.ts +++ b/src/v1.test.ts @@ -19,7 +19,7 @@ import { LookupSubjectsResponse, NewClient, ObjectReference, -PermissionsServiceClient, + PermissionsServiceClient, Relationship, RelationshipUpdate, RelationshipUpdate_Operation, @@ -686,104 +686,118 @@ describe("Experimental Service", () => { }); })); -describe("WriteRelationships with transaction metadata (Integration Test)", () => { - it("should successfully write relationships with metadata and verify metadata transmission", () => - new Promise((done, fail) => { - const testToken = generateTestToken("v1-int-tx-metadata"); - const client = NewClient( - testToken, - "localhost:50051", - ClientSecurity.INSECURE_LOCALHOST_ALLOWED, - PreconnectServices.SCHEMA_SERVICE | PreconnectServices.PERMISSIONS_SERVICE, - ); + describe("WriteRelationships with transaction metadata (Integration Test)", () => { + it("should successfully write relationships with metadata and verify metadata transmission", () => + new Promise((done, fail) => { + const testToken = generateTestToken("v1-int-tx-metadata"); + const client = NewClient( + testToken, + "localhost:50051", + ClientSecurity.INSECURE_LOCALHOST_ALLOWED, + PreconnectServices.SCHEMA_SERVICE | + PreconnectServices.PERMISSIONS_SERVICE, + ); - const writeSpy = vi.spyOn(PermissionsServiceClient.prototype, "writeRelationships"); + const writeSpy = vi.spyOn( + PermissionsServiceClient.prototype, + "writeRelationships", + ); - const schema = ` + const schema = ` definition test/user {} definition test/document { relation viewer: test/user permission view = viewer } `; - const writeSchemaRequest = WriteSchemaRequest.create({ schema }); + const writeSchemaRequest = WriteSchemaRequest.create({ schema }); - client.writeSchema(writeSchemaRequest, (schemaErr, schemaResponse) => { - if (schemaErr) { - client.close(); - fail(schemaErr); - return; - } - expect(schemaResponse).toBeDefined(); + client.writeSchema(writeSchemaRequest, (schemaErr, schemaResponse) => { + if (schemaErr) { + client.close(); + fail(schemaErr); + return; + } + expect(schemaResponse).toBeDefined(); - const uniqueSuffix = Date.now(); - const resource = ObjectReference.create({ - objectType: "test/document", - objectId: `doc-${uniqueSuffix}`, - }); + const uniqueSuffix = Date.now(); + const resource = ObjectReference.create({ + objectType: "test/document", + objectId: `doc-${uniqueSuffix}`, + }); - const user = ObjectReference.create({ - objectType: "test/user", - objectId: `user-${uniqueSuffix}`, - }); + const user = ObjectReference.create({ + objectType: "test/user", + objectId: `user-${uniqueSuffix}`, + }); - const updates = [ - RelationshipUpdate.create({ - relationship: Relationship.create({ - resource, - relation: "viewer", - subject: SubjectReference.create({ object: user }), + const updates = [ + RelationshipUpdate.create({ + relationship: Relationship.create({ + resource, + relation: "viewer", + subject: SubjectReference.create({ object: user }), + }), + operation: RelationshipUpdate_Operation.CREATE, }), - operation: RelationshipUpdate_Operation.CREATE, - }), - ]; - - const metadataObject = { transaction_id: "test-tx-123", other_data: "sample" }; - const transactionMetadata = createStructFromObject(metadataObject); + ]; - const writeRequest = WriteRelationshipsRequest.create({ - updates, - optionalTransactionMetadata: transactionMetadata, - }); + const metadataObject = { + transaction_id: "test-tx-123", + other_data: "sample", + }; + const transactionMetadata = createStructFromObject(metadataObject); - client.writeRelationships(writeRequest, (err, response) => { - if (err) { - client.close(); - fail(err); - return; - } + const writeRequest = WriteRelationshipsRequest.create({ + updates, + optionalTransactionMetadata: transactionMetadata, + }); - expect(err).toBeNull(); - expect(response).toBeDefined(); - expect(response?.writtenAt).toBeDefined(); + client.writeRelationships(writeRequest, (err, response) => { + if (err) { + client.close(); + fail(err); + return; + } - expect(writeSpy).toHaveBeenCalledTimes(1); + expect(err).toBeNull(); + expect(response).toBeDefined(); + expect(response?.writtenAt).toBeDefined(); - const actualRequest = writeSpy.mock.calls[0][0] as WriteRelationshipsRequest; + expect(writeSpy).toHaveBeenCalledTimes(1); - expect(actualRequest.updates).toEqual(updates); + const actualRequest = writeSpy.mock + .calls[0][0] as WriteRelationshipsRequest; - expect(actualRequest.optionalTransactionMetadata).toBeDefined(); - expect(actualRequest.optionalTransactionMetadata).toEqual(transactionMetadata); + expect(actualRequest.updates).toEqual(updates); - const transactionIdField = actualRequest.optionalTransactionMetadata?.fields?.['transaction_id']; - expect(transactionIdField?.kind?.oneofKind).toBe('stringValue'); - if (transactionIdField?.kind?.oneofKind === 'stringValue') { - expect(transactionIdField.kind.stringValue).toBe("test-tx-123"); - } + expect(actualRequest.optionalTransactionMetadata).toBeDefined(); + expect(actualRequest.optionalTransactionMetadata).toEqual( + transactionMetadata, + ); - const otherDataField = actualRequest.optionalTransactionMetadata?.fields?.['other_data']; - expect(otherDataField?.kind?.oneofKind).toBe('stringValue'); - if (otherDataField?.kind?.oneofKind === 'stringValue') { - expect(otherDataField.kind.stringValue).toBe("sample"); - } + const transactionIdField = + actualRequest.optionalTransactionMetadata?.fields?.[ + "transaction_id" + ]; + expect(transactionIdField?.kind?.oneofKind).toBe("stringValue"); + if (transactionIdField?.kind?.oneofKind === "stringValue") { + expect(transactionIdField.kind.stringValue).toBe("test-tx-123"); + } + + const otherDataField = + actualRequest.optionalTransactionMetadata?.fields?.["other_data"]; + expect(otherDataField?.kind?.oneofKind).toBe("stringValue"); + if (otherDataField?.kind?.oneofKind === "stringValue") { + expect(otherDataField.kind.stringValue).toBe("sample"); + } - client.close(); - done(); + client.close(); + done(); + }); }); - }); - })); -}); + })); + }); }); describe("createStructFromObject unit tests", () => { @@ -794,12 +808,21 @@ describe("createStructFromObject unit tests", () => { booleanProp: true, }; const struct = createStructFromObject(obj); - expect(struct.fields.stringProp?.kind.oneofKind).toBe('stringValue'); - expect(struct.fields.stringProp?.kind.oneofKind === 'stringValue' && struct.fields.stringProp?.kind.stringValue).toBe("hello"); - expect(struct.fields.numberProp?.kind.oneofKind).toBe('numberValue'); - expect(struct.fields.numberProp?.kind.oneofKind === 'numberValue' && struct.fields.numberProp?.kind.numberValue).toBe(123); - expect(struct.fields.booleanProp?.kind.oneofKind).toBe('boolValue'); - expect(struct.fields.booleanProp?.kind.oneofKind === 'boolValue' && struct.fields.booleanProp?.kind.boolValue).toBe(true); + expect(struct.fields.stringProp?.kind.oneofKind).toBe("stringValue"); + expect( + struct.fields.stringProp?.kind.oneofKind === "stringValue" && + struct.fields.stringProp?.kind.stringValue, + ).toBe("hello"); + expect(struct.fields.numberProp?.kind.oneofKind).toBe("numberValue"); + expect( + struct.fields.numberProp?.kind.oneofKind === "numberValue" && + struct.fields.numberProp?.kind.numberValue, + ).toBe(123); + expect(struct.fields.booleanProp?.kind.oneofKind).toBe("boolValue"); + expect( + struct.fields.booleanProp?.kind.oneofKind === "boolValue" && + struct.fields.booleanProp?.kind.boolValue, + ).toBe(true); }); it("should convert a JS object with null values", () => { @@ -807,8 +830,11 @@ describe("createStructFromObject unit tests", () => { nullProp: null, }; const struct = createStructFromObject(obj); - expect(struct.fields.nullProp?.kind.oneofKind).toBe('nullValue'); - expect(struct.fields.nullProp?.kind.oneofKind === 'nullValue' && struct.fields.nullProp?.kind.nullValue).toBe(PbNullValue.NULL_VALUE); + expect(struct.fields.nullProp?.kind.oneofKind).toBe("nullValue"); + expect( + struct.fields.nullProp?.kind.oneofKind === "nullValue" && + struct.fields.nullProp?.kind.nullValue, + ).toBe(PbNullValue.NULL_VALUE); }); it("should convert a JS object with nested objects", () => { @@ -819,13 +845,25 @@ describe("createStructFromObject unit tests", () => { }, }; const struct = createStructFromObject(obj); - const nestedStruct = struct.fields.nestedProp?.kind.oneofKind === 'structValue' && struct.fields.nestedProp.kind.structValue; + const nestedStruct = + struct.fields.nestedProp?.kind.oneofKind === "structValue" && + struct.fields.nestedProp.kind.structValue; expect(nestedStruct).toBeTruthy(); if (nestedStruct) { - expect(nestedStruct.fields.innerString?.kind.oneofKind).toBe('stringValue'); - expect(nestedStruct.fields.innerString?.kind.oneofKind === 'stringValue' && nestedStruct.fields.innerString?.kind.stringValue).toBe("world"); - expect(nestedStruct.fields.innerNumber?.kind.oneofKind).toBe('numberValue'); - expect(nestedStruct.fields.innerNumber?.kind.oneofKind === 'numberValue' && nestedStruct.fields.innerNumber?.kind.numberValue).toBe(456); + expect(nestedStruct.fields.innerString?.kind.oneofKind).toBe( + "stringValue", + ); + expect( + nestedStruct.fields.innerString?.kind.oneofKind === "stringValue" && + nestedStruct.fields.innerString?.kind.stringValue, + ).toBe("world"); + expect(nestedStruct.fields.innerNumber?.kind.oneofKind).toBe( + "numberValue", + ); + expect( + nestedStruct.fields.innerNumber?.kind.oneofKind === "numberValue" && + nestedStruct.fields.innerNumber?.kind.numberValue, + ).toBe(456); } }); @@ -838,28 +876,28 @@ describe("createStructFromObject unit tests", () => { it("should throw an error for null input", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(() => createStructFromObject(null as any)).toThrow( - "Input data for createStructFromObject must be a non-null object." + "Input data for createStructFromObject must be a non-null object.", ); }); it("should throw an error for non-object input (string)", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(() => createStructFromObject("not an object" as any)).toThrow( - "Input data for createStructFromObject must be a non-null object." + "Input data for createStructFromObject must be a non-null object.", ); }); it("should throw an error for non-object input (number)", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(() => createStructFromObject(123 as any)).toThrow( - "Input data for createStructFromObject must be a non-null object." + "Input data for createStructFromObject must be a non-null object.", ); }); it("should throw an error for array input", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any expect(() => createStructFromObject([] as any)).toThrow( - "Input data for createStructFromObject must be a non-null object." + "Input data for createStructFromObject must be a non-null object.", ); }); }); diff --git a/src/v1.ts b/src/v1.ts index 5ad5327..b41d755 100644 --- a/src/v1.ts +++ b/src/v1.ts @@ -17,7 +17,10 @@ import { } from "./util.js"; import type { OmitBaseMethods, PromisifiedClient } from "./types.js"; -import { Struct as ImportedPbStruct, NullValue as ImportedPbNullValue } from "./authzedapi/google/protobuf/struct.js"; +import { + Struct as ImportedPbStruct, + NullValue as ImportedPbNullValue, +} from "./authzedapi/google/protobuf/struct.js"; import type { JsonObject } from "@protobuf-ts/runtime"; export { ImportedPbStruct as PbStruct, ImportedPbNullValue as PbNullValue }; @@ -349,16 +352,18 @@ export function NewClientWithChannelCredentials( return ZedCombinedClient.create(endpoint, creds, preconnect, options); } /** - * Creates a google.protobuf.Struct object suitable for use as + * Creates a google.protobuf.Struct object suitable for use as * optionalTransactionMetadata in WriteRelationshipsRequest. - * + * * @param data A simple JavaScript object (e.g., { key: "value" }) to be converted into a Struct. * @returns A google.protobuf.Struct object. */ export function createStructFromObject(data: JsonObject): ImportedPbStruct { - if (data === null || typeof data !== 'object' || Array.isArray(data)) { + if (data === null || typeof data !== "object" || Array.isArray(data)) { // Or handle this case as per library's error handling philosophy - throw new Error('Input data for createStructFromObject must be a non-null object.'); + throw new Error( + "Input data for createStructFromObject must be a non-null object.", + ); } return ImportedPbStruct.fromJson(data); } From d10348cea736f5bf7c8f795fe2907df4504a2bbb Mon Sep 17 00:00:00 2001 From: jevin Date: Wed, 11 Jun 2025 11:38:16 -0400 Subject: [PATCH 3/4] Responding to comments --- src/v1.test.ts | 1 - src/v1.ts | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/v1.test.ts b/src/v1.test.ts index a2302a8..0bbe47f 100644 --- a/src/v1.test.ts +++ b/src/v1.test.ts @@ -1,5 +1,4 @@ import * as grpc from "@grpc/grpc-js"; -import {} from "@grpc/grpc-js"; import { generateTestToken } from "./__utils__/helpers.js"; import { Struct } from "./authzedapi/google/protobuf/struct.js"; import { PreconnectServices, deadlineInterceptor } from "./util.js"; diff --git a/src/v1.ts b/src/v1.ts index 29c705b..63bda7f 100644 --- a/src/v1.ts +++ b/src/v1.ts @@ -403,13 +403,19 @@ export function NewClientWithChannelCredentials( * @returns A google.protobuf.Struct object. */ export function createStructFromObject(data: JsonObject): ImportedPbStruct { - if (data === null || typeof data !== "object" || Array.isArray(data)) { - // Or handle this case as per library's error handling philosophy - throw new Error( - "Input data for createStructFromObject must be a non-null object.", - ); + try { + return ImportedPbStruct.fromJson(data); + } catch (error) { + if ( + error instanceof Error && + error.message.includes("Unable to parse message google.protobuf.Struct from JSON") + ) { + throw new Error( + "Input data for createStructFromObject must be a non-null object.", + ); + } + throw error; } - return ImportedPbStruct.fromJson(data); } export * from "./authzedapi/authzed/api/v1/core.js"; From 3c5010560f2028861f83b67e702265dc3f4edd53 Mon Sep 17 00:00:00 2001 From: jevin Date: Thu, 12 Jun 2025 13:27:11 -0400 Subject: [PATCH 4/4] Formatting changes --- src/v1.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/v1.ts b/src/v1.ts index 63bda7f..497ca8c 100644 --- a/src/v1.ts +++ b/src/v1.ts @@ -408,7 +408,9 @@ export function createStructFromObject(data: JsonObject): ImportedPbStruct { } catch (error) { if ( error instanceof Error && - error.message.includes("Unable to parse message google.protobuf.Struct from JSON") + error.message.includes( + "Unable to parse message google.protobuf.Struct from JSON", + ) ) { throw new Error( "Input data for createStructFromObject must be a non-null object.",