From 37458dd0c6275f1466c95b36e8594bda4285ab14 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Mon, 2 Dec 2024 12:53:41 -0500 Subject: [PATCH 1/3] Add test to make sure invalid keys are impossible Signed-off-by: Paul Sachs --- .../connect-query-core/src/connect-query-key.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/connect-query-core/src/connect-query-key.test.ts b/packages/connect-query-core/src/connect-query-key.test.ts index adb071e7..5bdc7552 100644 --- a/packages/connect-query-core/src/connect-query-key.test.ts +++ b/packages/connect-query-core/src/connect-query-key.test.ts @@ -122,4 +122,16 @@ describe("createConnectQueryKey", () => { expect(key[1].serviceName).toBe(ElizaService.typeName); expect(key[1].methodName).toBeUndefined(); }); + + // eslint-disable-next-line vitest/expect-expect -- using ts-expect-error + it("cannot except invalid input", () => { + createConnectQueryKey({ + schema: ElizaService.method.say, + input: { + // @ts-expect-error(2322) cannot create a key with invalid input + sentence: 1, + }, + cardinality: undefined, + }); + }); }); From 2414529c8e8e9c9f4147e12d15dfd56f0030b5de Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Mon, 9 Dec 2024 12:33:49 -0500 Subject: [PATCH 2/3] Ensure createConnectQueryKey enforces type on input Signed-off-by: Paul Sachs --- .../src/connect-query-key.test.ts | 3 +- .../src/connect-query-key.ts | 91 ++++++++++--------- .../src/create-query-options.test.ts | 13 +++ 3 files changed, 62 insertions(+), 45 deletions(-) diff --git a/packages/connect-query-core/src/connect-query-key.test.ts b/packages/connect-query-core/src/connect-query-key.test.ts index 5bdc7552..56365569 100644 --- a/packages/connect-query-core/src/connect-query-key.test.ts +++ b/packages/connect-query-core/src/connect-query-key.test.ts @@ -123,12 +123,11 @@ describe("createConnectQueryKey", () => { expect(key[1].methodName).toBeUndefined(); }); - // eslint-disable-next-line vitest/expect-expect -- using ts-expect-error it("cannot except invalid input", () => { + // @ts-expect-error(2322) cannot create a key with invalid input createConnectQueryKey({ schema: ElizaService.method.say, input: { - // @ts-expect-error(2322) cannot create a key with invalid input sentence: 1, }, cardinality: undefined, diff --git a/packages/connect-query-core/src/connect-query-key.ts b/packages/connect-query-core/src/connect-query-key.ts index 2395e699..c7e3d58f 100644 --- a/packages/connect-query-core/src/connect-query-key.ts +++ b/packages/connect-query-core/src/connect-query-key.ts @@ -13,7 +13,9 @@ // limitations under the License. import type { + DescMessage, DescMethod, + DescMethodUnary, DescService, MessageInitShape, } from "@bufbuild/protobuf"; @@ -72,46 +74,46 @@ export type ConnectQueryKey = [ }, ]; -type KeyParams = Desc extends DescMethod - ? { - /** - * Set `serviceName` and `methodName` in the key. - */ - schema: Desc; - /** - * Set `input` in the key: - * - If a SkipToken is provided, `input` is "skipped". - * - If an init shape is provided, `input` is set to a message key. - * - If omitted or undefined, `input` is not set in the key. - */ - input?: MessageInitShape | SkipToken | undefined; - /** - * Set `transport` in the key. - */ - transport?: Transport; - /** - * Set `cardinality` in the key - undefined is used for filters to match both finite and infinite queries. - */ - cardinality: "finite" | "infinite" | undefined; - /** - * If omit the field with this name from the key for infinite queries. - */ - pageParamKey?: keyof MessageInitShape; - } - : { - /** - * Set `serviceName` in the key, and omit `methodName`. - */ - schema: Desc; - /** - * Set `transport` in the key. - */ - transport?: Transport; - /** - * Set `cardinality` in the key - undefined is used for filters to match both finite and infinite queries. - */ - cardinality: "finite" | "infinite" | undefined; - }; +type KeyParamsForMethod = { + /** + * Set `serviceName` and `methodName` in the key. + */ + schema: Desc; + /** + * Set `input` in the key: + * - If a SkipToken is provided, `input` is "skipped". + * - If an init shape is provided, `input` is set to a message key. + * - If omitted or undefined, `input` is not set in the key. + */ + input?: MessageInitShape | SkipToken | undefined; + /** + * Set `transport` in the key. + */ + transport?: Transport; + /** + * Set `cardinality` in the key - undefined is used for filters to match both finite and infinite queries. + */ + cardinality: "finite" | "infinite" | undefined; + /** + * If omit the field with this name from the key for infinite queries. + */ + pageParamKey?: keyof MessageInitShape; +}; + +type KeyParamsForService = { + /** + * Set `serviceName` in the key, and omit `methodName`. + */ + schema: Desc; + /** + * Set `transport` in the key. + */ + transport?: Transport; + /** + * Set `cardinality` in the key - undefined is used for filters to match both finite and infinite queries. + */ + cardinality: "finite" | "infinite" | undefined; +}; /** * TanStack Query manages query caching for you based on query keys. In Connect Query, keys are structured, and can easily be created using this factory function. @@ -151,9 +153,12 @@ type KeyParams = Desc extends DescMethod * @see ConnectQueryKey for information on the components of Connect-Query's keys. */ export function createConnectQueryKey< - Desc extends DescMethod | DescService, - Params extends KeyParams, ->(params: Params): ConnectQueryKey { + I extends DescMessage, + O extends DescMessage, + Desc extends DescService, +>( + params: KeyParamsForMethod> | KeyParamsForService, +): ConnectQueryKey { const props: ConnectQueryKey[1] = params.schema.kind == "rpc" ? { diff --git a/packages/connect-query-core/src/create-query-options.test.ts b/packages/connect-query-core/src/create-query-options.test.ts index 79286e07..c259715c 100644 --- a/packages/connect-query-core/src/create-query-options.test.ts +++ b/packages/connect-query-core/src/create-query-options.test.ts @@ -58,4 +58,17 @@ describe("createQueryOptions", () => { ); expect(opt.queryKey).toStrictEqual(want); }); + + it("ensures type safety of parameters", () => { + // @ts-expect-error(2322) cannot provide invalid parameters + createQueryOptions( + sayMethodDescriptor, + { + sentence: 1, + }, + { + transport: mockedElizaTransport, + }, + ); + }); }); From d4d7f9c9adeecc00b75f1f34b2aa9b5d65af18f7 Mon Sep 17 00:00:00 2001 From: Paul Sachs Date: Mon, 9 Dec 2024 12:41:20 -0500 Subject: [PATCH 3/3] Move ts exception Signed-off-by: Paul Sachs --- packages/connect-query-core/src/connect-query-key.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/connect-query-core/src/connect-query-key.test.ts b/packages/connect-query-core/src/connect-query-key.test.ts index 56365569..19379650 100644 --- a/packages/connect-query-core/src/connect-query-key.test.ts +++ b/packages/connect-query-core/src/connect-query-key.test.ts @@ -124,10 +124,10 @@ describe("createConnectQueryKey", () => { }); it("cannot except invalid input", () => { - // @ts-expect-error(2322) cannot create a key with invalid input createConnectQueryKey({ schema: ElizaService.method.say, input: { + // @ts-expect-error(2322) cannot create a key with invalid input sentence: 1, }, cardinality: undefined,