diff --git a/README.md b/README.md
index 11ada246..88d827dc 100644
--- a/README.md
+++ b/README.md
@@ -7,63 +7,76 @@
[![License](https://img.shields.io/github/license/connectrpc/connect-query-es?color=blue)](./LICENSE) [![Build](https://github.com/connectrpc/connect-query-es/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/connectrpc/connect-query-es/actions/workflows/ci.yaml) [![NPM Version](https://img.shields.io/npm/v/@connectrpc/connect-query/latest?color=green&label=%40connectrpc%2Fconnect-query)](https://www.npmjs.com/package/@connectrpc/connect-query) [![NPM Version](https://img.shields.io/npm/v/@connectrpc/protoc-gen-connect-query/latest?color=green&label=%40connectrpc%2Fprotoc-gen-connect-query)](https://www.npmjs.com/package/@connectrpc/protoc-gen-connect-query)
-Connect-Query is an expansion pack for [TanStack Query](https://tanstack.com/query) (react-query), written in TypeScript and thoroughly tested. It enables effortless communication with servers that speak the [Connect Protocol](https://connectrpc.com/docs/protocol).
+Connect-Query is an wrapper around [TanStack Query](https://tanstack.com/query) (react-query), written in TypeScript and thoroughly tested. It enables effortless communication with servers that speak the [Connect Protocol](https://connectrpc.com/docs/protocol).
- [Quickstart](#quickstart)
+ - [Install](#install)
+ - [Usage](#usage)
- [Generated Code](#generated-code)
-- [Usage](#usage)
- [Connect-Query API](#connect-query-api)
- - [`createQueryService`](#createqueryservice)
- - [`createUnaryHooks`](#createunaryhooks)
+ - [`MethodUnaryDescriptor`](#methodunarydescriptor)
- [`TransportProvider`](#transportprovider)
- [`useTransport`](#usetransport)
- - [`UnaryFunctions.createData`](#unaryfunctionscreatedata)
- - [`UnaryFunctions.createUseQueryOptions`](#unaryfunctionscreateusequeryoptions)
- - [`UnaryFunctions.createUseInfiniteQueryOptions`](#unaryfunctionscreateuseinfinitequeryoptions)
- - [`UnaryFunctions.createUseMutationOptions`](#unaryfunctionscreateusemutationoptions)
- - [`UnaryFunctions.getPartialQueryKey`](#unaryfunctionsgetpartialquerykey)
- - [`UnaryFunctions.getQueryKey`](#unaryfunctionsgetquerykey)
- - [`UnaryFunctions.methodInfo`](#unaryfunctionsmethodinfo)
- - [`UnaryFunctions.setQueryData`](#unaryfunctionssetquerydata)
- - [`UnaryFunctions.setQueriesData`](#unaryfunctionssetqueriesdata)
- - [`UnaryHooks.useInfiniteQuery`](#unaryhooksuseinfinitequery)
- - [`UnaryHooks.useMutation`](#unaryhooksusemutation)
- - [`UnaryHooks.useQuery`](#unaryhooksusequery)
+ - [`useQuery](#usequery)
+ - [`useSuspenseQuery`](#usesuspensequery)
+ - [`useInfiniteQuery`](#useinfinitequery)
+ - [`useSuspenseInfiniteQuery`](#usesuspenseinfinitequery)
+ - [`useMutation`](#usemutation)
+ - [`createConnectQueryKey`](#createconnectquerykey)
+ - [`createUseQueryOptions`](#createusequeryoptions)
+ - [`createUseInfiniteQueryOptions`](#createuseinfinitequeryoptions)
+ - [`createUseSuspenseQueryOptions`](#createusesuspensequeryoptions)
+ - [`createUseSuspenseInfiniteQueryOptions`](#createusesuspenseinfinitequeryoptions)
+ - [`createProtobufSafeUpdater`](#createprotobufsafeupdater)
- [`ConnectQueryKey`](#connectquerykey)
- - [`ConnectPartialQueryKey`](#connectpartialquerykey)
-- [Frequently Asked Questions](#frequently-asked-questions)
- - [How do I pass other TanStack Query options?](#how-do-i-pass-other-tanstack-query-options)
- - [Is this ready for production?](#is-this-ready-for-production)
- - [What is Connect-Query's relationship to Connect-Web and Protobuf-ES?](#what-is-connect-querys-relationship-to-connect-web-and-protobuf-es)
- - [What is `Transport`](#what-is-transport)
- - [What if I already use Connect-Web?](#what-if-i-already-use-connect-web)
- - [What if I use gRPC-web?](#what-if-i-use-grpc-web)
- - [Do I have to use a code generator?](#do-i-have-to-use-a-code-generator)
- - [What if I have a custom `Transport`?](#what-if-i-have-a-custom-transport)
- - [Does this only work with React?](#does-this-only-work-with-react)
- - [How do I do Prefetching?](#how-do-i-do-prefetching)
- - [What about Streaming?](#what-about-streaming)
## Quickstart
### Install
```sh
-npm install @connectrpc/connect-query
+npm install @connectrpc/connect-query @connectrpc/connect-web
```
-Note: If you are using something that doesn't automatically install peerDependencies (npm older than v7), you'll want to make sure you also have `@bufbuild/protobuf` and `@connectrpc/connect` installed.
+Note: If you are using something that doesn't automatically install peerDependencies (npm older than v7), you'll want to make sure you also have `@bufbuild/protobuf`, `@connectrpc/connect`, and `@tanstack/react-query` installed. `@connectrpc/connect-web` is required for defining
+the transport to be used by the client.
### Usage
-Connect-Query will immediately feel familiar to you if you've used TanStack Query. It provides a set of convenient helpers that you can pass to the same TanStack Query functions you're already using:
+Connect-Query will immediately feel familiar to you if you've used TanStack Query. It provides a similar API, but instead takes a definition for your endpoint and returns a typesafe API for that endpoint.
+
+First, make sure you've configured your provider and query client:
+
+```tsx
+import { createConnectTransport } from "@connectrpc/connect-web";
+import { TransportProvider } from "@connectrpc/connect-query";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+
+const finalTransport = createConnectTransport({
+ baseUrl: "https://demo.connectrpc.com",
+});
+
+const queryClient = new QueryClient();
+
+function App() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+With configuration completed, you can now use the `useQuery` hook to make a request:
```ts
-import { useQuery } from '@tanstack/react-query';
+import { useQuery } from '@connectrpc/connect-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';
export const Example: FC = () => {
- const { data } = useQuery(example.useQuery({}));
+ const { data } = useQuery(example);
return
{data}
;
};
```
@@ -74,118 +87,46 @@ The [code generator](packages/protoc-gen-connect-query/README.md) does all the w
One of the best features of this library is that once you write your schema in Protobuf form, the TypeScript types are generated and then inferred. You never again need to specify the types of your data since the library does it automatically.
-
-
-
-
-
-
-
-
-
-
-
-
- play the above video to see the TypeScript types in action
-
-
-
-
-
-
### Generated Code
This example shows the best developer experience using code generation. Here's what that generated code looks like:
```ts title="your-generated-code/example-ExampleService_connectquery"
-import {
- createQueryService,
- createUnaryHooks,
-} from "@connectrpc/connect-query";
import { MethodKind } from "@bufbuild/protobuf";
import { ExampleRequest, ExampleResponse } from "./example_pb.js";
-export const typeName = "your.company.com.example.v1.ExampleService";
-
-export const ExampleService = {
- methods: {
- example: {
- name: "Example",
- kind: MethodKind.Unary,
- I: ExampleRequest,
- O: ExampleResponse,
- },
- },
- typeName,
-} as const;
-
-const $queryService = createQueryService({ service: ExampleService });
-
export const example = {
- ...$queryService.example,
- ...createUnaryHooks($queryService.example),
+ name: "Example",
+ kind: MethodKind.Unary,
+ I: ExampleRequest,
+ O: ExampleResponse,
+ service: {
+ typeName: "your.company.com.example.v1.ExampleService",
+ },
};
```
-If you want to use Connect-Query dynamically without code generation, you can call [`createQueryService`](#createqueryservice) exactly as the generated code does.
+The above code doesn't have to be generated and can be manually used to describe any given endpoint.
For more information on code generation, see the [documentation](./packages/protoc-gen-connect-query/README.md) for `protoc-gen-connect-query`.
## Connect-Query API
-### `createQueryService`
-
-```ts
-const createQueryService: ({
- service,
- transport,
-}: {
- service: Service;
- transport?: Transport;
-}) => QueryHooks;
-```
-
-`createQueryService` is the main entrypoint for Connect-Query.
+### `MethodUnaryDescriptor`
-Pass in a service and you will receive an object with properties for each of your services and values that provide hooks for those services that you can then give to Tanstack Query. The `ServiceType` TypeScript interface is provided by Protobuf-ES (`@bufbuild/protobuf`) while generated service definitions are provided by Connect-Web (`@connectrpc/connect-web`).
+A type that describes a single unary method. It describes the following properties:
-`Transport` refers to the mechanism by which your client will make the actual network calls. If you want to use a custom transport, you can optionally provide one with a call to `useTransport`, which Connect-Query exports. Otherwise, the default transport from React context will be used. This default transport is placed on React context by the `TransportProvider`. Whether you pass a custom transport or you use `TransportProvider`, in both cases you'll need to use one of `@connectrpc/connect-web`'s exports `createConnectTransport` or `createGrpcWebTransport`.
+- `name`: The name of the method.
+- `kind`: The kind of method. In this case, it's usually `MethodKind.Unary`.
+- `I`: The input message type.
+- `O`: The output message type.
+- `service.typeName`: The fully qualified name of the service the method exists on.
-Note that the most memory performant approach is to use the transport on React Context by using the `TransportProvider` because that provider is memoized by React, but also that any calls to `createQueryService` with the same service is cached by this function.
-
-Here's an example of a simple usage:
-
-```ts
-const queryService = createQueryService({
- service: {
- methods: {
- example: {
- name: "Example",
- kind: MethodKind.Unary,
- I: ExampleRequest,
- O: ExampleResponse,
- },
- },
- typeName: "your.company.com.example.v1.ExampleService",
- },
-});
-
-const example = {
- ...queryService.say,
- ...createUnaryHooks($queryService.say),
-};
-
-const { data, isLoading, ...etc } = useQuery(example.useQuery());
-```
-
-### `createUnaryHooks`
-
-This creates some helper functions for unary methods that automatically include the transport from context. It's a distinct function from `createQueryService` so the core function can be separate from specific React APIs.
+This type is core to how connect-query can stay lightweight and
+limit the amount of code actually generated. The descriptor is expected to be passed to almost all the methods in this library.
### `TransportProvider`
-> Note: This API can only be used with React
-
```ts
const TransportProvider: FC<
PropsWithChildren<{
@@ -229,213 +170,128 @@ export const App() {
### `useTransport`
-> Note: This API can only be used with React
-
```ts
const useTransport: () => Transport;
```
Use this helper to get the default transport that's currently attached to the React context for the calling component.
-### `UnaryFunctions.createData`
+### `useQuery`
```ts
-const createData: (data: PartialMessage) => O;
+function useQuery, O extends Message>(
+ methodSig: MethodUnaryDescriptor,
+ input?: DisableQuery | PartialMessage,
+ options?: {
+ transport?: Transport;
+ callOptions?: CallOptions;
+ } & UseQueryOptions
+): UseQueryResult;
```
-Use this to create a data object that can be used as `placeholderData` or initialData.
+The `useQuery` hook is the primary way to make a unary request. It's a wrapper around TanStack Query's [`useQuery`](https://tanstack.com/query/v5/docs/react/reference/useQuery) hook, but it's preconfigured with the correct `queryKey` and `queryFn` for the given method.
-### `UnaryFunctions.createUseQueryOptions`
-
-```ts
-const createUseQueryOptions: (
- input: DisableQuery | PartialMessage | undefined,
- options: {
- getPlaceholderData?: (enabled: boolean) => PartialMessage | undefined;
- onError?: (error: ConnectError) => void;
- transport: Transport;
- callOptions?: CallOptions | undefined;
- },
-) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (context?: QueryFunctionContext>) => Promise;
- placeholderData?: () => O | undefined;
- onError?: (error: ConnectError) => void;
-};
-```
+Any additional `options` you pass to `useQuery` will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.
-`createUseQueryOptions` is intended to be used with TanStack's [`useQuery`](https://tanstack.com/query/v4/docs/react/reference/useQuery) hook. The difference is that `createUseQueryOptions` is not a hook. Since hooks cannot be called conditionally, it can sometimes be helpful to use `createUseQueryOptions` to prepare an input to TanStack's `useQuery`.
+### `useSuspenseQuery`
-It is also useful to use alongside TanStack's [`useQueries`](https://tanstack.com/query/v4/docs/react/reference/useQueries) hook since hooks cannot be called in loops.
+Identical to useQuery but mapping to the `useSuspenseQuery` hook from [TanStack Query](https://tanstack.com/query/v5/docs/react/reference/useSuspenseQuery). This includes the benefits of narrowing the resulting data type (data will never be undefined).
-### `UnaryFunctions.createUseMutationOptions`
+### `useInfiniteQuery`
```ts
-const createUseMutationOptions: (options: {
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
-}) => {
- mutationFn: (
- input: PartialMessage,
- context?: QueryFunctionContext>,
- ) => Promise;
- onError?: (error: ConnectError) => void;
-};
-```
-
-`createUseMutationOptions` is intended to be used with TanStack's [`useMutation`](https://tanstack.com/query/v4/docs/react/reference/useMutation) hook. The difference is that `createUseMutationOptions` is not a hook and doesn't read from `TransportProvider` for its transport.
-
-### `UnaryFunctions.createUseInfiniteQueryOptions`
-
-```ts
-const createUseInfiniteQueryOptions: >(
- input: DisableQuery | PartialMessage,
+function useInfiniteQuery<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage,
+ Input extends PartialMessage & Required, ParamKey>>,
+>(
+ methodSig: MethodUnaryDescriptor,
+ input: DisableQuery | Input,
options: {
pageParamKey: ParamKey;
- getNextPageParam: (lastPage: O, allPages: O[]) => unknown;
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- },
-) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (
- context: QueryFunctionContext<
- ConnectQueryKey,
- PlainMessage[ParamKey]
- >,
- ) => Promise;
- getNextPageParam: GetNextPageParamFunction;
- onError?: (error: ConnectError) => void;
-};
+ transport?: Transport;
+ callOptions?: CallOptions;
+ getNextPageParam: GetNextPageParamFunction[ParamKey], O>;
+ }
+): UseInfiniteQueryResult, ConnectError>;
```
-`createUseInfiniteQueryOptions` is intended to be used with TanStack's [`useInfiniteQuery`](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) hook. The difference is that `createUseInfiniteQueryOptions` is not a hook and doesn't read from `TransportProvider` for its transport.
+The `useInfiniteQuery` is a wrapper around TanStack Query's [`useInfiniteQuery`](https://tanstack.com/query/v5/docs/react/reference/useInfiniteQuery) hook, but it's preconfigured with the correct `queryKey` and `queryFn` for the given method.
-### `UnaryFunctions.getPartialQueryKey`
+There are some required options for `useInfiniteQuery`, primarily `pageParamKey` and `getNextPageParam`. These are required because Connect-Query doesn't know how to paginate your data. You must provide a mapping from the output of the previous page and getting the next page. All other options passed to `useInfiniteQuery` will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.
-```ts
-const getPartialQueryKey: () => ConnectPartialQueryKey;
-```
+### `useSuspenseInfiniteQuery`
-This helper is useful for getting query keys matching a wider set of queries associated to this Connect `Service`, per TanStack Query's [partial matching](https://tanstack.com/query/v4/docs/react/guides/query-invalidation#query-matching-with-invalidatequeries) mechanism.
+Identical to useInfiniteQuery but mapping to the `useSuspenseInfiniteQuery` hook from [TanStack Query](https://tanstack.com/query/v5/docs/react/reference/useSuspenseInfiniteQuery). This includes the benefits of narrowing the resulting data type (data will never be undefined).
-### `UnaryFunctions.getQueryKey`
+### `useMutation`
```ts
-const getQueryKey: (
- input?: DisableQuery | PartialMessage,
-) => ConnectQueryKey;
-```
-
-This helper is useful to manually compute the [`queryKey`](https://tanstack.com/query/v4/docs/react/guides/query-keys) sent to TanStack Query. This function has no side effects.
-
-### `UnaryFunctions.methodInfo`
-
-```ts
-const methodInfo: MethodInfoUnary;
+function useMutation, O extends Message>(
+ methodSig: MethodUnaryDescriptor,
+ options?: {
+ transport?: Transport;,
+ callOptions?: CallOptions;
+ },
+): UseMutationResult>
```
-This is the metadata associated with this method.
-
-### `UnaryFunctions.setQueryData`
+The `useMutation` is a wrapper around TanStack Query's [`useMutation`](https://tanstack.com/query/v5/docs/react/reference/useMutation) hook, but it's preconfigured with the correct `mutationFn` for the given method.
-```ts
-const setQueryData: (
- updater: PartialMessage | ((prev?: O) => PartialMessage),
- input?: PartialMessage,
-) => [queryKey: ConnectQueryKey, updater: (prev?: O) => O | undefined];
-```
+Any additional `options` you pass to `useMutation` will be merged with the options that Connect-Query provides to @tanstack/react-query. This means that you can pass any additional options that TanStack Query supports.
-This helper is intended to be used with TanStack Query `QueryClient`'s [`setQueryData`](https://tanstack.com/query/v4/docs/react/reference/QueryClient#queryclientsetquerydata) function.
-
-### `UnaryFunctions.setQueriesData`
+### `createConnectQueryKey`
```ts
-const setQueriesData: (
- updater: PartialMessage | ((prev?: O) => PartialMessage),
-) => [queryKey: ConnectPartialQueryKey, updater: (prev?: O) => O | undefined];
+function createConnectQueryKey, O extends Message>(
+ methodDescriptor: Pick, "I" | "name" | "service">,
+ input?: DisableQuery | PartialMessage | undefined
+): ConnectQueryKey;
```
-This helper is intended to be used with TanStack Query `QueryClient`'s [`setQueriesData`](https://tanstack.com/query/v4/docs/react/reference/QueryClient#queryclientsetqueriesdata) function.
-
-### `UnaryHooks.useInfiniteQuery`
+This helper is useful to manually compute the [`queryKey`](https://tanstack.com/query/v4/docs/react/guides/query-keys) sent to TanStack Query. This function has no side effects.
-> Note: This API can only be used with React
+### `callUnaryMethod`
```ts
-const useInfiniteQuery: >(
- input: DisableQuery | PartialMessage,
- options: {
- pageParamKey: ParamKey;
- getNextPageParam: (lastPage: O, allPages: O[]) => unknown;
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
+function callUnaryMethod, O extends Message>(
+ methodType: MethodUnaryDescriptor,
+ input: PartialMessage | undefined,
+ {
+ callOptions,
+ transport,
+ }: {
+ transport: Transport;
callOptions?: CallOptions | undefined;
- },
-) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (
- context: QueryFunctionContext<
- ConnectQueryKey,
- PlainMessage[ParamKey]
- >,
- ) => Promise;
- getNextPageParam: GetNextPageParamFunction;
- onError?: (error: ConnectError) => void;
-};
+ }
+): Promise;
```
-This helper is intended to be used with TanStack Query's [`useInfiniteQuery`](https://tanstack.com/query/v4/docs/react/reference/useInfiniteQuery) function.
+This API allows you to directly call the method using the provided transport. Use this if you need to manually call a method outside of the context of a React component, or need to call it where you can't use hooks.
-### `UnaryHooks.useMutation`
+### `createProtobufSafeUpdater`
-> Note: This API can only be used with React
+Creates a typesafe updater that can be used to update data in a query cache. Used in combination with a queryClient.
```ts
-const useMutation: (options?: {
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
-}) => {
- mutationFn: (
- input: PartialMessage,
- context?: QueryFunctionContext>,
- ) => Promise;
- onError?: (error: ConnectError) => void;
-};
-```
+import { createProtobufSafeUpdater } from '@connectrpc/connect-query';
+import { useQueryClient } from "@tanstack/react-query";
+
+...
+const queryClient = useQueryClient();
+queryClient.setQueryData(
+ createConnectQueryKey(example),
+ createProtobufSafeUpdater(example, (prev) => {
+ return {
+ ...prev,
+ completed: true,
+ };
+ })
+);
-This function is intended to be used with TanStack Query's [`useMutation`](https://tanstack.com/query/v4/docs/react/reference/useMutation) function.
-
-### `UnaryHooks.useQuery`
-
-> Note: This API can only be used with React
-
-```ts
-const useQuery: (
- input?: DisableQuery | PartialMessage,
- options?: {
- getPlaceholderData?: (enabled: boolean) => PartialMessage | undefined;
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- },
-) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (context?: QueryFunctionContext>) => Promise;
- placeholderData?: () => O | undefined;
- onError?: (error: ConnectError) => void;
-};
```
-This function is intended to be used with Tanstack Query's [`useQuery`](https://tanstack.com/query/v4/docs/react/reference/useQuery) function.
-
### `ConnectQueryKey`
```ts
@@ -460,14 +316,6 @@ For example, a query key might look like this:
];
```
-### `ConnectPartialQueryKey`
-
-This type is useful In situations where you want to use partial matching for TanStack Query `queryKey`s.
-
-```ts
-type ConnectPartialQueryKey = [serviceTypeName: string, methodName: string];
-```
-
For example, a partial query key might look like this:
```ts
@@ -484,44 +332,39 @@ For playwright, you can see a sample test [here](https://github.com/connectrpc/c
### How do I pass other TanStack Query options?
-Say you have an query that looks something like this:
+Each function that interacts with TanStack Query also provides for options that can be passed through.
```ts
-import { useQuery } from '@tanstack/react-query';
+import { useQuery } from '@connectrpc/connect-query';
import { example } from 'your-generated-code/example-ExampleService_connectquery';
export const Example: FC = () => {
- const { data } = useQuery(example.useQuery({}));
+ const { data } = useQuery(example, undefined, {
+ // These are typesafe options that are passed to underlying TanStack Query.
+ refetchInterval: 1000,
+ });
return {data}
;
};
```
-On line 5, `example.useQuery({})` just returns an object with a few TanStack Query options preconfigured for you (for example, `queryKey` and `queryFn` and `onError`). All of the Connect-Query hooks APIs work this way, so you can always inspect the TypeScript type to see which specific TanStack query options are configured.
+> Why was this changed from the previous version of Connect-Query?
+>
+> Originally, all we did was pass options to TanStack Query. This was done as an intentional way to keep ourselves separate from TanStack Query. However, as usage increased, it became obvious that were still tied to the API of TanStack Query, and it only meant that we increased the burden on the developer to understand that underlying connection. This new API removes most of that burden and reduces the surface area of the API significantly.
-That means, that if you want to add extra TanStack Query options, you can simply spread the object resulting from Connect-Query:
+### Is this ready for production?
-```ts
-const { data } = useQuery({
- ...example.useQuery({}),
+Buf has been using Connect-Query in production for some time. Also, there is 100% mandatory test coverage in this project which covers quite a lot of edge cases. That said, this package is given a `v0.x` semver to designate that it's a new project, and we want to make sure the API is exactly what our users want before we call it "production ready". That also means that some parts of the API may change before `v1.0` is reached.
- // Add any extra TanStack Query options here.
- // TypeScript will ensure they're valid!
- refetchInterval: 1000,
-});
-```
+### Using BigInt with RPC inputs
-> Why does it work this way?
->
-> You may be familiar with other projects that directly wrap react-query directly (such as tRPC). We worked with the TanStack team to develop this API and determined that it's most flexible to simply return an options object.
->
-> 1. You have full control over what's actually passed to TanStack Query. For example, if you have a query where you'd like to modify the `queryKey`, you can do so directly.
-> 1. It provides full transparency into what Connect Query is actually doing. This means that if you want to see what _exactly_ Connect Query is doing, you can simply inspect the object. This makes for a much more straightforward experience when you're debugging your app.
-> 1. This means that the resulting call is plain TanStack Query in every way, which means that you can still integrate with any existing TanStack Query plugins or extensions you may already be using.
-> 1. Not wrapping TanStack Query itself means that you can immediately use Connect-Query with any new functionality or options of TanStack Query.
+Since Connect-Query use the inputs as keys for the query, if you have a field with type `int64`, those fields will cause serialization problems. For this reason, Connect-Query ships with defaultOptions that can be passed to the QueryClient to make sure serializing BigInt fields is done properly:
-### Is this ready for production?
+```ts
+import { defaultOptions } from "@connectrpc/connect-query";
+import { QueryClient } from "@tanstack/react-query";
-Buf has been using Connect-Query in production for some time. Also, there is 100% mandatory test coverage in this project which covers quite a lot of edge cases. That said, this package is given a `v0.x` semver to designate that it's a new project, and we want to make sure the API is exactly what our users want before we call it "production ready". That also means that some parts of the API may change before `v1.0` is reached.
+const queryClient = new QueryClient({ defaultOptions });
+```
### What is Connect-Query's relationship to Connect-Web and Protobuf-ES?
@@ -558,17 +401,15 @@ That said, we encourage you to check out the [Connect protocol](https://connectr
### Do I have to use a code generator?
-No. The code generator just calls [`createQueryService`](#createqueryservice) and [`createUnaryHooks`](#createunaryhooks) with the arguments already added, but you are free to do that yourself if you wish.
+No. The code generator just generates the method descriptors, but you are free to do that yourself if you wish.
### What if I have a custom `Transport`?
-If the `Transport` attached to React Context via the `TransportProvider` isn't working for you, then you can override transport at every level. For example, you can pass a custom transport directly to the lowest-level API like `UnaryHooks.useQuery`, but you can also pass it to `createQueryHooks` or even as high as `createQueryService`. It's an optional argument for all of those cases and if you choose to omit the `transport` property, the `Transport` provided by `TransportProvider` will be used (only where necessary).
+If the `Transport` attached to React Context via the `TransportProvider` isn't working for you, then you can override transport at every level. For example, you can pass a custom transport directly to the lowest-level API like `useQuery` or `createUseQueryOptions`.
### Does this only work with React?
-You can use Connect-Query with any TanStack variant (React, Solid, Svelte, Vue). All methods which are part of the `UnaryFunctions` type can be used by any framework. The only APIs which are React specific are the `TransportProvider`, and any APIs starting with `use`.
-
-> Tip: If you're a TanStack Query user that uses something other than React, we'd love to hear from you. Please reach out to us on the [Buf Slack](https://buf.build/links/slack).
+Connect-Query does require React, but the core (`createUseQueryOptions`) is not React specific so splitting off a `connect-solid-query` is possible.
### How do I do Prefetching?
@@ -579,7 +420,13 @@ import { say } from "./gen/eliza-ElizaService_connectquery";
function prefetch() {
return queryClient.prefetchQuery(
- say.createUseQueryOptions({ sentence: "Hello", transport: myTransport }),
+ createUseQueryOptions(
+ say,
+ { sentence: "Hello" },
+ {
+ transport: myTransport,
+ }
+ )
);
}
```
diff --git a/assets/connect-query_dependency_graph.excalidraw b/assets/connect-query_dependency_graph.excalidraw
index 6997568d..bd3120d3 100644
--- a/assets/connect-query_dependency_graph.excalidraw
+++ b/assets/connect-query_dependency_graph.excalidraw
@@ -1027,12 +1027,12 @@
"locked": false,
"fontSize": 20,
"fontFamily": 3,
- "text": "Connect-Query",
+ "text": "Connect-React-Query",
"baseline": 19,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "NLMbxwdPMz1O8gu8ku7cT",
- "originalText": "Connect-Query"
+ "originalText": "Connect-React-Query"
},
{
"type": "rectangle",
diff --git a/cspell.config.json b/cspell.config.json
index ea86dd58..6e832ffa 100644
--- a/cspell.config.json
+++ b/cspell.config.json
@@ -36,7 +36,8 @@
"tsdoc",
"corepack",
"printables",
- "arethetypeswrong"
+ "arethetypeswrong",
+ "typesafe"
],
"ignorePaths": ["**/*.svg", "**/*.ai", "**/pnpm-lock.yaml", "*.excalidraw"]
}
diff --git a/examples/react/basic/buf.gen.yaml b/examples/react/basic/buf.gen.yaml
index 97ef37e6..263eb4d2 100644
--- a/examples/react/basic/buf.gen.yaml
+++ b/examples/react/basic/buf.gen.yaml
@@ -3,13 +3,13 @@
version: v1
plugins:
- name: es
- path: ./node_modules/.bin/protoc-gen-es
+ path: protoc-gen-es
out: src/gen
opt:
- target=ts
- name: connect-query
- path: ./node_modules/.bin/protoc-gen-connect-query
+ path: protoc-gen-connect-query
out: src/gen
opt:
- target=ts
diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json
index 948b25e8..4ae2a951 100644
--- a/examples/react/basic/package.json
+++ b/examples/react/basic/package.json
@@ -1,5 +1,5 @@
{
- "name": "@connectrpc/connect-query-example-react-basic",
+ "name": "@connectrpc/connect-query-example-basic",
"version": "0.0.0",
"private": true,
"type": "module",
@@ -12,13 +12,13 @@
"format": "prettier . --write && eslint . --fix && license-header"
},
"dependencies": {
- "@bufbuild/buf": "1.27.2",
- "@bufbuild/protobuf": "^1.4.1",
- "@bufbuild/protoc-gen-es": "^1.4.1",
- "@connectrpc/connect": "^1.1.3",
+ "@bufbuild/buf": "1.28.1",
+ "@bufbuild/protobuf": "^1.5.1",
+ "@bufbuild/protoc-gen-es": "^1.5.1",
+ "@connectrpc/connect": "^1.1.4",
"@connectrpc/connect-query": "workspace:*",
- "@connectrpc/connect-web": "^1.1.3",
- "@connectrpc/protoc-gen-connect-es": "^1.1.3",
+ "@connectrpc/connect-web": "^1.1.4",
+ "@connectrpc/protoc-gen-connect-es": "^1.1.4",
"@connectrpc/protoc-gen-connect-query": "workspace:*",
"@tanstack/react-query": "^5.4.3",
"@tanstack/react-query-devtools": "^5.4.3",
diff --git a/examples/react/basic/src/example.tsx b/examples/react/basic/src/example.tsx
index 8eacb31e..b12aa0cd 100644
--- a/examples/react/basic/src/example.tsx
+++ b/examples/react/basic/src/example.tsx
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { useQuery } from "@tanstack/react-query";
+import { useQuery } from "@connectrpc/connect-query";
import type { FC } from "react";
import { Data, Datum } from "./datum";
@@ -21,12 +21,10 @@ import { Indicator, Indicators } from "./indicator";
import { Page } from "./page";
/**
- * This example demonstrates a basic usage of Connect-Query with `useQuery`
+ * This example demonstrates a basic usage of Connect-React-Query with `useQuery`
*/
export const Example: FC = () => {
- const { status, fetchStatus, error, data } = useQuery({
- ...say.useQuery({}),
- });
+ const { status, fetchStatus, error, data } = useQuery(say);
return (
diff --git a/examples/react/basic/src/gen/eliza-BigIntService_connectquery.ts b/examples/react/basic/src/gen/eliza-BigIntService_connectquery.ts
index 46ff2665..1ba4d968 100644
--- a/examples/react/basic/src/gen/eliza-BigIntService_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-BigIntService_connectquery.ts
@@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import { CountRequest, CountResponse } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.BigIntService";
+import { CountRequest, CountResponse } from "./eliza_pb";
/**
- * @generated from service connectrpc.eliza.v1.BigIntService
+ * @generated from rpc connectrpc.eliza.v1.BigIntService.Count
*/
-export const BigIntService = {
- typeName: "connectrpc.eliza.v1.BigIntService",
- methods: {
- /**
- * @generated from rpc connectrpc.eliza.v1.BigIntService.Count
- */
- count: {
- name: "Count",
- I: CountRequest,
- O: CountResponse,
- kind: MethodKind.Unary,
- },
+export const count = {
+ localName: "count",
+ name: "Count",
+ kind: MethodKind.Unary,
+ I: CountRequest,
+ O: CountResponse,
+ service: {
+ typeName: "connectrpc.eliza.v1.BigIntService",
},
} as const;
-
-const $queryService = createQueryService({ service: BigIntService });
-
-/**
- * @generated from rpc connectrpc.eliza.v1.BigIntService.Count
- */
-export const count: UnaryFunctionsWithHooks = {
- ...$queryService.count,
- ...createUnaryHooks($queryService.count),
-};
diff --git a/examples/react/basic/src/gen/eliza-ElizaService_connectquery.ts b/examples/react/basic/src/gen/eliza-ElizaService_connectquery.ts
index a1f80a7c..9f72e648 100644
--- a/examples/react/basic/src/gen/eliza-ElizaService_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-ElizaService_connectquery.ts
@@ -12,108 +12,41 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import {
- ConverseRequest,
- ConverseResponse,
- IntroduceRequest,
- IntroduceResponse,
- SayRequest,
- SayResponse,
-} from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.ElizaService";
-
-/**
- * ElizaService provides a way to talk to Eliza, a port of the DOCTOR script
- * for Joseph Weizenbaum's original ELIZA program. Created in the mid-1960s at
- * the MIT Artificial Intelligence Laboratory, ELIZA demonstrates the
- * superficiality of human-computer communication. DOCTOR simulates a
- * psychotherapist, and is commonly found as an Easter egg in emacs
- * distributions.
- *
- * @generated from service connectrpc.eliza.v1.ElizaService
- */
-export const ElizaService = {
- typeName: "connectrpc.eliza.v1.ElizaService",
- methods: {
- /**
- * Say is a unary RPC. Eliza responds to the prompt with a single sentence.
- *
- * @generated from rpc connectrpc.eliza.v1.ElizaService.Say
- */
- say: {
- name: "Say",
- I: SayRequest,
- O: SayResponse,
- kind: MethodKind.Unary,
- },
- /**
- * SayAgain is a unary RPC. Eliza responds to the prompt with a single sentence.
- *
- * @generated from rpc connectrpc.eliza.v1.ElizaService.SayAgain
- */
- sayAgain: {
- name: "SayAgain",
- I: SayRequest,
- O: SayResponse,
- kind: MethodKind.Unary,
- },
- /**
- * Converse is a bidirectional RPC. The caller may exchange multiple
- * back-and-forth messages with Eliza over a long-lived connection. Eliza
- * responds to each ConverseRequest with a ConverseResponse.
- *
- * @generated from rpc connectrpc.eliza.v1.ElizaService.Converse
- */
- converse: {
- name: "Converse",
- I: ConverseRequest,
- O: ConverseResponse,
- kind: MethodKind.BiDiStreaming,
- },
- /**
- * Introduce is a server streaming RPC. Given the caller's name, Eliza
- * returns a stream of sentences to introduce itself.
- *
- * @generated from rpc connectrpc.eliza.v1.ElizaService.Introduce
- */
- introduce: {
- name: "Introduce",
- I: IntroduceRequest,
- O: IntroduceResponse,
- kind: MethodKind.ServerStreaming,
- },
- },
-} as const;
-
-const $queryService = createQueryService({ service: ElizaService });
+import { SayRequest, SayResponse } from "./eliza_pb";
/**
* Say is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.Say
*/
-export const say: UnaryFunctionsWithHooks = {
- ...$queryService.say,
- ...createUnaryHooks($queryService.say),
-};
+export const say = {
+ localName: "say",
+ name: "Say",
+ kind: MethodKind.Unary,
+ I: SayRequest,
+ O: SayResponse,
+ service: {
+ typeName: "connectrpc.eliza.v1.ElizaService",
+ },
+} as const;
/**
* SayAgain is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.ElizaService.SayAgain
*/
-export const sayAgain: UnaryFunctionsWithHooks = {
- ...$queryService.sayAgain,
- ...createUnaryHooks($queryService.sayAgain),
-};
+export const sayAgain = {
+ localName: "sayAgain",
+ name: "SayAgain",
+ kind: MethodKind.Unary,
+ I: SayRequest,
+ O: SayResponse,
+ service: {
+ typeName: "connectrpc.eliza.v1.ElizaService",
+ },
+} as const;
diff --git a/examples/react/basic/src/gen/eliza-Haberdasher_connectquery.ts b/examples/react/basic/src/gen/eliza-Haberdasher_connectquery.ts
index ba5d5d3c..e5d7ef4b 100644
--- a/examples/react/basic/src/gen/eliza-Haberdasher_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-Haberdasher_connectquery.ts
@@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import { Nothing } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.Haberdasher";
+import { Nothing } from "./eliza_pb";
/**
- * @generated from service connectrpc.eliza.v1.Haberdasher
+ * @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
*/
-export const Haberdasher = {
- typeName: "connectrpc.eliza.v1.Haberdasher",
- methods: {
- /**
- * @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
- */
- work: {
- name: "Work",
- I: Nothing,
- O: Nothing,
- kind: MethodKind.Unary,
- },
+export const work = {
+ localName: "work",
+ name: "Work",
+ kind: MethodKind.Unary,
+ I: Nothing,
+ O: Nothing,
+ service: {
+ typeName: "connectrpc.eliza.v1.Haberdasher",
},
} as const;
-
-const $queryService = createQueryService({ service: Haberdasher });
-
-/**
- * @generated from rpc connectrpc.eliza.v1.Haberdasher.Work
- */
-export const work: UnaryFunctionsWithHooks = {
- ...$queryService.work,
- ...createUnaryHooks($queryService.work),
-};
diff --git a/examples/react/basic/src/gen/eliza-PaginatedService_connectquery.ts b/examples/react/basic/src/gen/eliza-PaginatedService_connectquery.ts
index b2c7be10..8ff5de2f 100644
--- a/examples/react/basic/src/gen/eliza-PaginatedService_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-PaginatedService_connectquery.ts
@@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import { ListRequest, ListResponse } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.PaginatedService";
+import { ListRequest, ListResponse } from "./eliza_pb";
/**
- * @generated from service connectrpc.eliza.v1.PaginatedService
+ * @generated from rpc connectrpc.eliza.v1.PaginatedService.List
*/
-export const PaginatedService = {
- typeName: "connectrpc.eliza.v1.PaginatedService",
- methods: {
- /**
- * @generated from rpc connectrpc.eliza.v1.PaginatedService.List
- */
- list: {
- name: "List",
- I: ListRequest,
- O: ListResponse,
- kind: MethodKind.Unary,
- },
+export const list = {
+ localName: "list",
+ name: "List",
+ kind: MethodKind.Unary,
+ I: ListRequest,
+ O: ListResponse,
+ service: {
+ typeName: "connectrpc.eliza.v1.PaginatedService",
},
} as const;
-
-const $queryService = createQueryService({ service: PaginatedService });
-
-/**
- * @generated from rpc connectrpc.eliza.v1.PaginatedService.List
- */
-export const list: UnaryFunctionsWithHooks = {
- ...$queryService.list,
- ...createUnaryHooks($queryService.list),
-};
diff --git a/examples/react/basic/src/gen/eliza-SecondService_connectquery.ts b/examples/react/basic/src/gen/eliza-SecondService_connectquery.ts
index 2412ef59..08e1e9b0 100644
--- a/examples/react/basic/src/gen/eliza-SecondService_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-SecondService_connectquery.ts
@@ -12,82 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import {
- ConverseRequest,
- ConverseResponse,
- IntroduceRequest,
- IntroduceResponse,
- SayRequest,
- SayResponse,
-} from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.SecondService";
-
-/**
- * Second Service just to make sure multiple file generation works
- *
- * @generated from service connectrpc.eliza.v1.SecondService
- */
-export const SecondService = {
- typeName: "connectrpc.eliza.v1.SecondService",
- methods: {
- /**
- * Say is a unary RPC. Eliza responds to the prompt with a single sentence.
- *
- * @generated from rpc connectrpc.eliza.v1.SecondService.Say
- */
- say: {
- name: "Say",
- I: SayRequest,
- O: SayResponse,
- kind: MethodKind.Unary,
- },
- /**
- * Converse is a bidirectional RPC. The caller may exchange multiple
- * back-and-forth messages with Eliza over a long-lived connection. Eliza
- * responds to each ConverseRequest with a ConverseResponse.
- *
- * @generated from rpc connectrpc.eliza.v1.SecondService.Converse
- */
- converse: {
- name: "Converse",
- I: ConverseRequest,
- O: ConverseResponse,
- kind: MethodKind.BiDiStreaming,
- },
- /**
- * Introduce is a server streaming RPC. Given the caller's name, Eliza
- * returns a stream of sentences to introduce itself.
- *
- * @generated from rpc connectrpc.eliza.v1.SecondService.Introduce
- */
- introduce: {
- name: "Introduce",
- I: IntroduceRequest,
- O: IntroduceResponse,
- kind: MethodKind.ServerStreaming,
- },
- },
-} as const;
-
-const $queryService = createQueryService({ service: SecondService });
+import { SayRequest, SayResponse } from "./eliza_pb";
/**
* Say is a unary RPC. Eliza responds to the prompt with a single sentence.
*
* @generated from rpc connectrpc.eliza.v1.SecondService.Say
*/
-export const say: UnaryFunctionsWithHooks = {
- ...$queryService.say,
- ...createUnaryHooks($queryService.say),
-};
+export const say = {
+ localName: "say",
+ name: "Say",
+ kind: MethodKind.Unary,
+ I: SayRequest,
+ O: SayResponse,
+ service: {
+ typeName: "connectrpc.eliza.v1.SecondService",
+ },
+} as const;
diff --git a/examples/react/basic/src/gen/eliza-Slouch_connectquery.ts b/examples/react/basic/src/gen/eliza-Slouch_connectquery.ts
index 48712e8e..b8c5a88a 100644
--- a/examples/react/basic/src/gen/eliza-Slouch_connectquery.ts
+++ b/examples/react/basic/src/gen/eliza-Slouch_connectquery.ts
@@ -12,44 +12,23 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-connect-query v0.6.0 with parameter "target=ts,import_extension=none,ts_nocheck=false"
+// @generated by protoc-gen-connect-query v1.0.0-rc.1 with parameter "target=ts,import_extension=none,ts_nocheck=false"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
-import { Nothing } from "./eliza_pb";
import { MethodKind } from "@bufbuild/protobuf";
-import {
- createQueryService,
- createUnaryHooks,
- UnaryFunctionsWithHooks,
-} from "@connectrpc/connect-query";
-
-export const typeName = "connectrpc.eliza.v1.Slouch";
+import { Nothing } from "./eliza_pb";
/**
- * @generated from service connectrpc.eliza.v1.Slouch
+ * @generated from rpc connectrpc.eliza.v1.Slouch.Work
*/
-export const Slouch = {
- typeName: "connectrpc.eliza.v1.Slouch",
- methods: {
- /**
- * @generated from rpc connectrpc.eliza.v1.Slouch.Work
- */
- work: {
- name: "Work",
- I: Nothing,
- O: Nothing,
- kind: MethodKind.Unary,
- },
+export const work = {
+ localName: "work",
+ name: "Work",
+ kind: MethodKind.Unary,
+ I: Nothing,
+ O: Nothing,
+ service: {
+ typeName: "connectrpc.eliza.v1.Slouch",
},
} as const;
-
-const $queryService = createQueryService({ service: Slouch });
-
-/**
- * @generated from rpc connectrpc.eliza.v1.Slouch.Work
- */
-export const work: UnaryFunctionsWithHooks = {
- ...$queryService.work,
- ...createUnaryHooks($queryService.work),
-};
diff --git a/examples/react/basic/src/gen/eliza_pb.ts b/examples/react/basic/src/gen/eliza_pb.ts
index e67f2e15..ace9534b 100644
--- a/examples/react/basic/src/gen/eliza_pb.ts
+++ b/examples/react/basic/src/gen/eliza_pb.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// @generated by protoc-gen-es v1.4.1 with parameter "target=ts"
+// @generated by protoc-gen-es v1.5.1 with parameter "target=ts"
// @generated from file eliza.proto (package connectrpc.eliza.v1, syntax proto3)
/* eslint-disable */
// @ts-nocheck
diff --git a/examples/react/basic/src/main.test.tsx b/examples/react/basic/src/main.test.tsx
index 5f475e69..2dbc18bf 100644
--- a/examples/react/basic/src/main.test.tsx
+++ b/examples/react/basic/src/main.test.tsx
@@ -17,17 +17,15 @@ import "@testing-library/jest-dom";
import { createRouterTransport } from "@connectrpc/connect";
import { render, screen } from "@testing-library/react";
-import { ElizaService } from "./gen/eliza-ElizaService_connectquery";
+import * as methods from "./gen/eliza-ElizaService_connectquery";
import Main from "./main";
describe("Application", () => {
it("should show success status and response data", async () => {
- const transport = createRouterTransport(({ service }) => {
- service(ElizaService, {
- say: () => ({
- sentence: "Hello, world!",
- }),
- });
+ const transport = createRouterTransport(({ rpc }) => {
+ rpc(methods.say, () => ({
+ sentence: "Hello, world!",
+ }));
});
render();
const text = await screen.findByText("Status: success");
diff --git a/packages/connect-query/README.md b/packages/connect-query/README.md
index dfc2d699..9ff8ddac 100644
--- a/packages/connect-query/README.md
+++ b/packages/connect-query/README.md
@@ -2,8 +2,6 @@
This is the runtime library package for Connect-Query. You'll find its code generator at [@connectrpc/protoc-gen-connect-query](https://www.npmjs.com/package/@connectrpc/protoc-gen-connect-query).
-Connect-Query is an expansion pack for [TanStack Query](https://tanstack.com/query) (react-query), written in TypeScript and thoroughly tested. It enables effortless communication with servers that speak the [Connect Protocol](https://connectrpc.com/docs/protocol).
-
-The procedures are defined in a [Protocol Buffer](https://developers.google.com/protocol-buffers) schema implemented by your backend, and Connect-Query generates the clients and related types to access the backend with TanStack Query. The clients support two protocols: gRPC-web, and Connect's own protocol.
+Connect-Query is a wrapper around [TanStack Query](https://tanstack.com/query) (react-query), written in TypeScript and thoroughly tested. It enables effortless communication with servers that speak the [Connect Protocol](https://connectrpc.com/docs/protocol).
To get started, head over to the [docs](https://connectrpc.com/docs/query/getting-started) for a tutorial, or take a look at [our examples](https://github.com/connectrpc/connect-query-es/examples) for integration with various frameworks.
diff --git a/packages/connect-query/package-lock.json b/packages/connect-query/package-lock.json
deleted file mode 100644
index 55a3c9ed..00000000
--- a/packages/connect-query/package-lock.json
+++ /dev/null
@@ -1,1948 +0,0 @@
-{
- "name": "@connectrpc/connect-query",
- "version": "0.6.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "@connectrpc/connect-query",
- "version": "0.6.0",
- "license": "Apache-2.0",
- "dependencies": {
- "stable-hash": "^0.0.3"
- },
- "devDependencies": {
- "@arethetypeswrong/cli": "^0.11.0",
- "@bufbuild/buf": "1.27.0",
- "@bufbuild/jest-environment-jsdom": "^0.1.1",
- "@bufbuild/protobuf": "^1.3.3",
- "@bufbuild/protoc-gen-es": "^1.3.3",
- "@connectrpc/connect": "^1.1.2",
- "@connectrpc/connect-web": "^1.1.2",
- "@connectrpc/protoc-gen-connect-es": "^1.1.2",
- "@tanstack/react-query": "^5.4.3",
- "@testing-library/react": "^14.0.0",
- "@types/react": "^18.2.30",
- "@types/react-dom": "^18.2.14",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "typescript": "^5.2.2"
- },
- "peerDependencies": {
- "@bufbuild/protobuf": "^1.3.3",
- "@connectrpc/connect": "^1.1.2",
- "@tanstack/react-query": "^5.4.3",
- "react": "^18.2.0",
- "react-dom": "^18.2.0"
- }
- },
- "../../node_modules/.pnpm/@arethetypeswrong+cli@0.11.0/node_modules/@arethetypeswrong/cli": {
- "version": "0.11.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@arethetypeswrong/core": "0.10.2",
- "chalk": "^4.1.2",
- "cli-table3": "^0.6.3",
- "commander": "^10.0.1",
- "marked": "^5.1.0",
- "marked-terminal": "^5.2.0",
- "node-fetch": "^2.6.4",
- "semver": "^7.5.4"
- },
- "bin": {
- "attw": "dist/index.js"
- },
- "devDependencies": {
- "@types/marked": "^5.0.0",
- "@types/marked-terminal": "^3.1.3",
- "@types/node": "^20.2.5",
- "@types/node-fetch": "^2.6.4",
- "@types/semver": "^7.5.3",
- "typescript": "^5.0.0-dev.20230207"
- }
- },
- "../../node_modules/.pnpm/@testing-library+react@14.0.0_react-dom@18.2.0_react@18.2.0/node_modules/@testing-library/react": {
- "version": "14.0.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.12.5",
- "@testing-library/dom": "^9.0.0",
- "@types/react-dom": "^18.0.0"
- },
- "devDependencies": {
- "@testing-library/jest-dom": "^5.11.6",
- "chalk": "^4.1.2",
- "dotenv-cli": "^4.0.0",
- "jest-diff": "^29.4.1",
- "kcd-scripts": "^13.0.0",
- "npm-run-all": "^4.1.5",
- "react": "^18.0.0",
- "react-dom": "^18.0.0",
- "rimraf": "^3.0.2",
- "typescript": "^4.1.2"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "react": "^18.0.0",
- "react-dom": "^18.0.0"
- }
- },
- "../../node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom": {
- "version": "18.2.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.1.0",
- "scheduler": "^0.23.0"
- },
- "peerDependencies": {
- "react": "^18.2.0"
- }
- },
- "../../node_modules/.pnpm/react@18.2.0/node_modules/react": {
- "version": "18.2.0",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "loose-envify": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "../../node_modules/.pnpm/stable-hash@0.0.3/node_modules/stable-hash": {
- "version": "0.0.3",
- "license": "MIT",
- "devDependencies": {
- "@types/jest": "^28.1.3",
- "base64-url": "^2.3.3",
- "esbuild": "^0.12.28",
- "flattie": "^1.1.0",
- "hash-obj": "^4.0.0",
- "jest": "^28.1.1",
- "json-stringify-deterministic": "^1.0.7",
- "nanobench": "^2.1.1",
- "prettier": "^2.7.1",
- "ts-jest": "^28.0.5",
- "typescript": "^4.7.4"
- }
- },
- "../../node_modules/.pnpm/tsup@7.2.0_ts-node@10.9.1_typescript@5.2.2/node_modules/tsup": {
- "version": "7.2.0",
- "extraneous": true,
- "license": "MIT",
- "dependencies": {
- "bundle-require": "^4.0.0",
- "cac": "^6.7.12",
- "chokidar": "^3.5.1",
- "debug": "^4.3.1",
- "esbuild": "^0.18.2",
- "execa": "^5.0.0",
- "globby": "^11.0.3",
- "joycon": "^3.0.1",
- "postcss-load-config": "^4.0.1",
- "resolve-from": "^5.0.0",
- "rollup": "^3.2.5",
- "source-map": "0.8.0-beta.0",
- "sucrase": "^3.20.3",
- "tree-kill": "^1.2.2"
- },
- "bin": {
- "tsup": "dist/cli-default.js",
- "tsup-node": "dist/cli-node.js"
- },
- "devDependencies": {
- "@rollup/plugin-json": "5.0.1",
- "@swc/core": "1.2.218",
- "@types/debug": "4.1.7",
- "@types/flat": "5.0.2",
- "@types/fs-extra": "9.0.13",
- "@types/node": "14.18.12",
- "@types/resolve": "1.20.1",
- "colorette": "2.0.16",
- "consola": "2.15.3",
- "flat": "5.0.2",
- "fs-extra": "10.0.0",
- "postcss": "8.4.12",
- "postcss-simple-vars": "6.0.3",
- "prettier": "2.5.1",
- "resolve": "1.20.0",
- "rollup-plugin-dts": "5.3.0",
- "rollup-plugin-hashbang": "3.0.0",
- "sass": "1.62.1",
- "strip-json-comments": "4.0.0",
- "svelte": "3.46.4",
- "svelte-preprocess": "5.0.3",
- "terser": "^5.16.0",
- "ts-essentials": "9.1.2",
- "tsconfig-paths": "3.12.0",
- "tsup": "7.1.0",
- "typescript": "5.0.2",
- "vitest": "0.28.4",
- "wait-for-expect": "3.0.2"
- },
- "engines": {
- "node": ">=16.14"
- },
- "peerDependencies": {
- "@swc/core": "^1",
- "postcss": "^8.4.12",
- "typescript": ">=4.1.0"
- },
- "peerDependenciesMeta": {
- "@swc/core": {
- "optional": true
- },
- "postcss": {
- "optional": true
- },
- "typescript": {
- "optional": true
- }
- }
- },
- "../../node_modules/.pnpm/typescript@5.2.2/node_modules/typescript": {
- "version": "5.2.2",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "devDependencies": {
- "@esfx/canceltoken": "^1.0.0",
- "@octokit/rest": "^19.0.13",
- "@types/chai": "^4.3.4",
- "@types/fs-extra": "^9.0.13",
- "@types/glob": "^8.1.0",
- "@types/microsoft__typescript-etw": "^0.1.1",
- "@types/minimist": "^1.2.2",
- "@types/mocha": "^10.0.1",
- "@types/ms": "^0.7.31",
- "@types/node": "latest",
- "@types/source-map-support": "^0.5.6",
- "@types/which": "^2.0.1",
- "@typescript-eslint/eslint-plugin": "^6.0.0",
- "@typescript-eslint/parser": "^6.0.0",
- "@typescript-eslint/utils": "^6.0.0",
- "azure-devops-node-api": "^12.0.0",
- "c8": "^7.14.0",
- "chai": "^4.3.7",
- "chalk": "^4.1.2",
- "chokidar": "^3.5.3",
- "del": "^6.1.1",
- "diff": "^5.1.0",
- "esbuild": "^0.18.1",
- "eslint": "^8.22.0",
- "eslint-formatter-autolinkable-stylish": "^1.2.0",
- "eslint-plugin-local": "^1.0.0",
- "eslint-plugin-no-null": "^1.0.2",
- "eslint-plugin-simple-import-sort": "^10.0.0",
- "fast-xml-parser": "^4.0.11",
- "fs-extra": "^9.1.0",
- "glob": "^8.1.0",
- "hereby": "^1.6.4",
- "jsonc-parser": "^3.2.0",
- "minimist": "^1.2.8",
- "mocha": "^10.2.0",
- "mocha-fivemat-progress-reporter": "^0.1.0",
- "ms": "^2.1.3",
- "node-fetch": "^3.2.10",
- "source-map-support": "^0.5.21",
- "tslib": "^2.5.0",
- "typescript": "^5.0.2",
- "which": "^2.0.2"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/@arethetypeswrong/cli": {
- "resolved": "../../node_modules/.pnpm/@arethetypeswrong+cli@0.11.0/node_modules/@arethetypeswrong/cli",
- "link": true
- },
- "node_modules/@babel/code-frame": {
- "version": "7.22.13",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
- "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@babel/highlight": "^7.22.13",
- "chalk": "^2.4.2"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/code-frame/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true,
- "peer": true
- },
- "node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/code-frame/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/code-frame/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/helper-validator-identifier": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
- "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight": {
- "version": "7.22.20",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
- "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.22.20",
- "chalk": "^2.4.2",
- "js-tokens": "^4.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/chalk": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
- "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "ansi-styles": "^3.2.1",
- "escape-string-regexp": "^1.0.5",
- "supports-color": "^5.3.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/@babel/highlight/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "dev": true,
- "peer": true
- },
- "node_modules/@babel/highlight/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/@babel/highlight/node_modules/has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/highlight/node_modules/supports-color": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
- "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "has-flag": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@bufbuild/buf": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.27.0.tgz",
- "integrity": "sha512-vR/ke6gUNgGSC3z7WkHLcbO+ur+zvGTC4ohHsLo2dZqEWysWySjNpcU70SKdIN3G5M4fSS1ki6MkZPes3E+83w==",
- "dev": true,
- "hasInstallScript": true,
- "bin": {
- "buf": "bin/buf",
- "protoc-gen-buf-breaking": "bin/protoc-gen-buf-breaking",
- "protoc-gen-buf-lint": "bin/protoc-gen-buf-lint"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "@bufbuild/buf-darwin-arm64": "1.27.0",
- "@bufbuild/buf-darwin-x64": "1.27.0",
- "@bufbuild/buf-linux-aarch64": "1.27.0",
- "@bufbuild/buf-linux-x64": "1.27.0",
- "@bufbuild/buf-win32-arm64": "1.27.0",
- "@bufbuild/buf-win32-x64": "1.27.0"
- }
- },
- "node_modules/@bufbuild/buf-darwin-arm64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.27.0.tgz",
- "integrity": "sha512-Bsdo9BkkIlIgBpQJ2jyIXl9ggSqDdSJ12euxgU1y4pbT5iD11mdiUA7eq5/ssxLJilUrUGj2Gk1h1KbYG/JfVA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/buf-darwin-x64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.27.0.tgz",
- "integrity": "sha512-aJmSZvO6uNxHST8+kN5cukv7/ZLgDnvklp+r6uyokocg5sk1rgWQVBqiVtGmoDWwPbotpMhb3EuqtwN9hdNrOg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/buf-linux-aarch64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.27.0.tgz",
- "integrity": "sha512-1NPHARYENNVWOK3bQxbnYsMLU09em4/kdyAnCwyGkNhr+pWUlWdCBu3X5tdrRW+mnhjeagIcomTMhgVjxIAS7g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/buf-linux-x64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.27.0.tgz",
- "integrity": "sha512-3LMTSJlwJAeOfjPuB0NBK+1Yfg1Bybadt+c1X/vF8XSXut1u0Ju1/fbRDz75BF4AlMidMQPdGS+vPWmPcb51hA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/buf-win32-arm64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.27.0.tgz",
- "integrity": "sha512-Za5d3obNvSfLZAlQW8IAWtv1Yv0gQTFDVMPyYiOh70rKIfKIxrWZxT4E4nzFLZZ54VQDFoUl81bAjOYLOgaspQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/buf-win32-x64": {
- "version": "1.27.0",
- "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.27.0.tgz",
- "integrity": "sha512-OzVK4Fz162Z6fFHAAZhHPW2GiCXjweCG/hwjOtFt2gza1t3ImYp0CwxJI6ePGY+th3Y9yu8rY0iHiI59ezMa4Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@bufbuild/jest-environment-jsdom": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@bufbuild/jest-environment-jsdom/-/jest-environment-jsdom-0.1.1.tgz",
- "integrity": "sha512-lO4dke+l/LQAUT8CLmh0SKtY37gmax63eD7YSBQu48sqwAx4hgu1hRmoheRysaqR4bO6Vudhf6+nkBm0TbctvA==",
- "dev": true,
- "peerDependencies": {
- "jest-environment-jsdom": "^29.5.0"
- }
- },
- "node_modules/@bufbuild/protobuf": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.4.1.tgz",
- "integrity": "sha512-4dthhwBGD9nlpY35ic8dMQC5R0dsND2b2xyeVO3qf+hBk8m7Y9dUs+SmMh6rqO2pGLUTKHefGXLDW+z19hBPdQ==",
- "dev": true
- },
- "node_modules/@bufbuild/protoc-gen-es": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@bufbuild/protoc-gen-es/-/protoc-gen-es-1.4.1.tgz",
- "integrity": "sha512-YPEFzLl/RslDJXZoqI505YutOsYGIz1zGrYTltTrdgk0EZkDSAQfq6/Yu36mPkUSypHEaRSoDlrlcpthIwA19w==",
- "dev": true,
- "dependencies": {
- "@bufbuild/protoplugin": "1.4.1"
- },
- "bin": {
- "protoc-gen-es": "bin/protoc-gen-es"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "@bufbuild/protobuf": "1.4.1"
- },
- "peerDependenciesMeta": {
- "@bufbuild/protobuf": {
- "optional": true
- }
- }
- },
- "node_modules/@bufbuild/protoplugin": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/@bufbuild/protoplugin/-/protoplugin-1.4.1.tgz",
- "integrity": "sha512-URC4/O5MsM94W7ed8NJMw1mUaFAKr5y0B67PMjhBdhVcDi6p8be76wz9+xW6/B6dMzlO+SpnicFYc9fEhLcQIw==",
- "dev": true,
- "dependencies": {
- "@bufbuild/protobuf": "1.4.1",
- "@typescript/vfs": "^1.4.0",
- "typescript": "4.5.2"
- }
- },
- "node_modules/@bufbuild/protoplugin/node_modules/typescript": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz",
- "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
- "node_modules/@connectrpc/connect": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@connectrpc/connect/-/connect-1.1.3.tgz",
- "integrity": "sha512-AXkbsLQe2Nm7VuoN5nqp05GEb9mPa/f5oFzDqTbHME4i8TghTrlY03uefbhuAq4wjsnfDnmuxHZvn6ndlgXmbg==",
- "dev": true,
- "peerDependencies": {
- "@bufbuild/protobuf": "^1.3.3"
- }
- },
- "node_modules/@connectrpc/connect-web": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@connectrpc/connect-web/-/connect-web-1.1.3.tgz",
- "integrity": "sha512-WfShOZt91duJngqivYF4wJFRbeRa4bF/fPMfDVN0MAYSX3VuaTMn8o9qgKN7tsg2H2ZClyOVQwMkZx6IdcP7Zw==",
- "dev": true,
- "peerDependencies": {
- "@bufbuild/protobuf": "^1.3.3",
- "@connectrpc/connect": "1.1.3"
- }
- },
- "node_modules/@connectrpc/protoc-gen-connect-es": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/@connectrpc/protoc-gen-connect-es/-/protoc-gen-connect-es-1.1.3.tgz",
- "integrity": "sha512-Irt1WM1o45KL0DNz8D8nraNfRrOyZfn7rzRsOyfrwbNzeVO1JV3rELFpARqGAvtVveYBoO9uwYtQ8TKLXsnrng==",
- "dev": true,
- "dependencies": {
- "@bufbuild/protobuf": "^1.3.3",
- "@bufbuild/protoplugin": "^1.3.3"
- },
- "bin": {
- "protoc-gen-connect-es": "bin/protoc-gen-connect-es"
- },
- "engines": {
- "node": ">=16.0.0"
- },
- "peerDependencies": {
- "@bufbuild/protoc-gen-es": "^1.3.3",
- "@connectrpc/connect": "1.1.3"
- },
- "peerDependenciesMeta": {
- "@bufbuild/protoc-gen-es": {
- "optional": true
- },
- "@connectrpc/connect": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/environment": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
- "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/fake-timers": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "jest-mock": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/fake-timers": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
- "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@sinonjs/fake-timers": "^10.0.2",
- "@types/node": "*",
- "jest-message-util": "^29.7.0",
- "jest-mock": "^29.7.0",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/schemas": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz",
- "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@sinclair/typebox": "^0.27.8"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@jest/types": {
- "version": "29.6.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
- "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/schemas": "^29.6.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/@sinclair/typebox": {
- "version": "0.27.8",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
- "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
- "dev": true,
- "peer": true
- },
- "node_modules/@sinonjs/commons": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz",
- "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "type-detect": "4.0.8"
- }
- },
- "node_modules/@sinonjs/fake-timers": {
- "version": "10.3.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
- "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@sinonjs/commons": "^3.0.0"
- }
- },
- "node_modules/@tanstack/query-core": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.4.3.tgz",
- "integrity": "sha512-fnI9ORjcuLGm1sNrKatKIosRQUpuqcD4SV7RqRSVmj8JSicX2aoMyKryHEBpVQvf6N4PaBVgBxQomjsbsGPssQ==",
- "dev": true,
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- }
- },
- "node_modules/@tanstack/react-query": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.4.3.tgz",
- "integrity": "sha512-4aSOrRNa6yEmf7mws5QPTVMn8Lp7L38tFoTZ0c1ZmhIvbr8GIA0WT7X5N3yz/nuK8hUtjw9cAzBr4BPDZZ+tzA==",
- "dev": true,
- "dependencies": {
- "@tanstack/query-core": "5.4.3"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^18.0.0",
- "react-dom": "^18.0.0",
- "react-native": "*"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- },
- "react-native": {
- "optional": true
- }
- }
- },
- "node_modules/@testing-library/react": {
- "resolved": "../../node_modules/.pnpm/@testing-library+react@14.0.0_react-dom@18.2.0_react@18.2.0/node_modules/@testing-library/react",
- "link": true
- },
- "node_modules/@tootallnate/once": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
- "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
- "integrity": "sha512-zONci81DZYCZjiLe0r6equvZut0b+dBRPBN5kBDjsONnutYNtJMoWQ9uR2RkL1gLG9NMTzvf+29e5RFfPbeKhQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/@types/istanbul-lib-report": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.2.tgz",
- "integrity": "sha512-8toY6FgdltSdONav1XtUHl4LN1yTmLza+EuDazb/fEmRNCwjyqNVIQWs2IfC74IqjHkREs/nQ2FWq5kZU9IC0w==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "node_modules/@types/istanbul-reports": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.3.tgz",
- "integrity": "sha512-1nESsePMBlf0RPRffLZi5ujYh7IH1BWL4y9pr+Bn3cJBdxz+RTP8bUFljLz9HvzhhOSWKdyBZ4DIivdL6rvgZg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@types/istanbul-lib-report": "*"
- }
- },
- "node_modules/@types/jsdom": {
- "version": "20.0.1",
- "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
- "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@types/node": "*",
- "@types/tough-cookie": "*",
- "parse5": "^7.0.0"
- }
- },
- "node_modules/@types/node": {
- "version": "20.8.9",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz",
- "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "undici-types": "~5.26.4"
- }
- },
- "node_modules/@types/prop-types": {
- "version": "15.7.9",
- "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz",
- "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==",
- "dev": true
- },
- "node_modules/@types/react": {
- "version": "18.2.33",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz",
- "integrity": "sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==",
- "dev": true,
- "dependencies": {
- "@types/prop-types": "*",
- "@types/scheduler": "*",
- "csstype": "^3.0.2"
- }
- },
- "node_modules/@types/react-dom": {
- "version": "18.2.14",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz",
- "integrity": "sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==",
- "dev": true,
- "dependencies": {
- "@types/react": "*"
- }
- },
- "node_modules/@types/scheduler": {
- "version": "0.16.5",
- "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.5.tgz",
- "integrity": "sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==",
- "dev": true
- },
- "node_modules/@types/stack-utils": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz",
- "integrity": "sha512-g7CK9nHdwjK2n0ymT2CW698FuWJRIx+RP6embAzZ2Qi8/ilIrA1Imt2LVSeHUzKvpoi7BhmmQcXz95eS0f2JXw==",
- "dev": true,
- "peer": true
- },
- "node_modules/@types/tough-cookie": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.4.tgz",
- "integrity": "sha512-95Sfz4nvMAb0Nl9DTxN3j64adfwfbBPEYq14VN7zT5J5O2M9V6iZMIIQU1U+pJyl9agHYHNCqhCXgyEtIRRa5A==",
- "dev": true,
- "peer": true
- },
- "node_modules/@types/yargs": {
- "version": "17.0.29",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.29.tgz",
- "integrity": "sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@types/yargs-parser": {
- "version": "21.0.2",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.2.tgz",
- "integrity": "sha512-5qcvofLPbfjmBfKaLfj/+f+Sbd6pN4zl7w7VSVI5uz7m9QZTuB2aZAa2uo1wHFBNN2x6g/SoTkXmd8mQnQF2Cw==",
- "dev": true,
- "peer": true
- },
- "node_modules/@typescript/vfs": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@typescript/vfs/-/vfs-1.5.0.tgz",
- "integrity": "sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==",
- "dev": true,
- "dependencies": {
- "debug": "^4.1.1"
- }
- },
- "node_modules/abab": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
- "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
- "dev": true,
- "peer": true
- },
- "node_modules/acorn": {
- "version": "8.11.2",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
- "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
- "dev": true,
- "peer": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-globals": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
- "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "acorn": "^8.1.0",
- "acorn-walk": "^8.0.2"
- }
- },
- "node_modules/acorn-walk": {
- "version": "8.3.0",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz",
- "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/agent-base": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
- "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "debug": "4"
- },
- "engines": {
- "node": ">= 6.0.0"
- }
- },
- "node_modules/ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-convert": "^2.0.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "dev": true,
- "peer": true
- },
- "node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "fill-range": "^7.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
- "node_modules/ci-info": {
- "version": "3.9.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
- "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/sibiraj-s"
- }
- ],
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "color-name": "~1.1.4"
- },
- "engines": {
- "node": ">=7.0.0"
- }
- },
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "peer": true
- },
- "node_modules/combined-stream": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
- "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "delayed-stream": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/cssom": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
- "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
- "dev": true,
- "peer": true
- },
- "node_modules/cssstyle": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
- "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "cssom": "~0.3.6"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/cssstyle/node_modules/cssom": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
- "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
- "dev": true,
- "peer": true
- },
- "node_modules/csstype": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
- "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
- "dev": true
- },
- "node_modules/data-urls": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
- "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "abab": "^2.0.6",
- "whatwg-mimetype": "^3.0.0",
- "whatwg-url": "^11.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decimal.js": {
- "version": "10.4.3",
- "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
- "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
- "dev": true,
- "peer": true
- },
- "node_modules/delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/domexception": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
- "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2"
- },
- "bin": {
- "escodegen": "bin/escodegen.js",
- "esgenerate": "bin/esgenerate.js"
- },
- "engines": {
- "node": ">=6.0"
- },
- "optionalDependencies": {
- "source-map": "~0.6.1"
- }
- },
- "node_modules/esprima": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
- "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
- "dev": true,
- "peer": true,
- "bin": {
- "esparse": "bin/esparse.js",
- "esvalidate": "bin/esvalidate.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/estraverse": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
- "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=4.0"
- }
- },
- "node_modules/esutils": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
- "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "to-regex-range": "^5.0.1"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/form-data": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
- "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "asynckit": "^0.4.0",
- "combined-stream": "^1.0.8",
- "mime-types": "^2.1.12"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/html-encoding-sniffer": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
- "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "whatwg-encoding": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/http-proxy-agent": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
- "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@tootallnate/once": "2",
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/https-proxy-agent": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
- "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "agent-base": "6",
- "debug": "4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/iconv-lite": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
- "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=0.12.0"
- }
- },
- "node_modules/is-potential-custom-element-name": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
- "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/jest-environment-jsdom": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
- "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/environment": "^29.7.0",
- "@jest/fake-timers": "^29.7.0",
- "@jest/types": "^29.6.3",
- "@types/jsdom": "^20.0.0",
- "@types/node": "*",
- "jest-mock": "^29.7.0",
- "jest-util": "^29.7.0",
- "jsdom": "^20.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- },
- "peerDependencies": {
- "canvas": "^2.5.0"
- },
- "peerDependenciesMeta": {
- "canvas": {
- "optional": true
- }
- }
- },
- "node_modules/jest-message-util": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz",
- "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^29.6.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^29.7.0",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-mock": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
- "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "jest-util": "^29.7.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/jest-util": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
- "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/types": "^29.6.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/js-tokens": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
- "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/jsdom": {
- "version": "20.0.3",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
- "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "abab": "^2.0.6",
- "acorn": "^8.8.1",
- "acorn-globals": "^7.0.0",
- "cssom": "^0.5.0",
- "cssstyle": "^2.3.0",
- "data-urls": "^3.0.2",
- "decimal.js": "^10.4.2",
- "domexception": "^4.0.0",
- "escodegen": "^2.0.0",
- "form-data": "^4.0.0",
- "html-encoding-sniffer": "^3.0.0",
- "http-proxy-agent": "^5.0.0",
- "https-proxy-agent": "^5.0.1",
- "is-potential-custom-element-name": "^1.0.1",
- "nwsapi": "^2.2.2",
- "parse5": "^7.1.1",
- "saxes": "^6.0.0",
- "symbol-tree": "^3.2.4",
- "tough-cookie": "^4.1.2",
- "w3c-xmlserializer": "^4.0.0",
- "webidl-conversions": "^7.0.0",
- "whatwg-encoding": "^2.0.0",
- "whatwg-mimetype": "^3.0.0",
- "whatwg-url": "^11.0.0",
- "ws": "^8.11.0",
- "xml-name-validator": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- },
- "peerDependencies": {
- "canvas": "^2.5.0"
- },
- "peerDependenciesMeta": {
- "canvas": {
- "optional": true
- }
- }
- },
- "node_modules/micromatch": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
- "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "braces": "^3.0.2",
- "picomatch": "^2.3.1"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
- "node_modules/nwsapi": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz",
- "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/parse5": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
- "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "entities": "^4.4.0"
- },
- "funding": {
- "url": "https://github.com/inikulin/parse5?sponsor=1"
- }
- },
- "node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pretty-format": {
- "version": "29.7.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
- "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@jest/schemas": "^29.6.3",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
- }
- },
- "node_modules/pretty-format/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/psl": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
- "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
- "dev": true,
- "peer": true
- },
- "node_modules/punycode": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
- "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/querystringify": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
- "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/react": {
- "resolved": "../../node_modules/.pnpm/react@18.2.0/node_modules/react",
- "link": true
- },
- "node_modules/react-dom": {
- "resolved": "../../node_modules/.pnpm/react-dom@18.2.0_react@18.2.0/node_modules/react-dom",
- "link": true
- },
- "node_modules/react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true,
- "peer": true
- },
- "node_modules/requires-port": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
- "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
- "dev": true,
- "peer": true
- },
- "node_modules/safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true,
- "peer": true
- },
- "node_modules/saxes": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
- "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "xmlchars": "^2.2.0"
- },
- "engines": {
- "node": ">=v12.22.7"
- }
- },
- "node_modules/slash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
- "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/stable-hash": {
- "resolved": "../../node_modules/.pnpm/stable-hash@0.0.3/node_modules/stable-hash",
- "link": true
- },
- "node_modules/stack-utils": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
- "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "escape-string-regexp": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/symbol-tree": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
- "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
- "dev": true,
- "peer": true
- },
- "node_modules/to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "is-number": "^7.0.0"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/tough-cookie": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
- "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "psl": "^1.1.33",
- "punycode": "^2.1.1",
- "universalify": "^0.2.0",
- "url-parse": "^1.5.3"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/tr46": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
- "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "punycode": "^2.1.1"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/typescript": {
- "resolved": "../../node_modules/.pnpm/typescript@5.2.2/node_modules/typescript",
- "link": true
- },
- "node_modules/undici-types": {
- "version": "5.26.5",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
- "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
- "dev": true,
- "peer": true
- },
- "node_modules/universalify": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
- "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/url-parse": {
- "version": "1.5.10",
- "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
- "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "querystringify": "^2.1.1",
- "requires-port": "^1.0.0"
- }
- },
- "node_modules/w3c-xmlserializer": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
- "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "xml-name-validator": "^4.0.0"
- },
- "engines": {
- "node": ">=14"
- }
- },
- "node_modules/webidl-conversions": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
- "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-encoding": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
- "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "iconv-lite": "0.6.3"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-mimetype": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
- "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/whatwg-url": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
- "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "tr46": "^3.0.0",
- "webidl-conversions": "^7.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/ws": {
- "version": "8.14.2",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
- "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/xml-name-validator": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
- "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/xmlchars": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
- "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
- "dev": true,
- "peer": true
- }
- }
-}
diff --git a/packages/connect-query/package.json b/packages/connect-query/package.json
index 9fd0c542..d16fd359 100644
--- a/packages/connect-query/package.json
+++ b/packages/connect-query/package.json
@@ -1,6 +1,6 @@
{
"name": "@connectrpc/connect-query",
- "version": "0.6.0",
+ "version": "1.0.0-rc.1",
"description": "TypeScript-first expansion pack for TanStack Query that gives you Protobuf superpowers.",
"license": "Apache-2.0",
"repository": {
@@ -34,13 +34,13 @@
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.13.0",
- "@bufbuild/buf": "1.27.2",
+ "@bufbuild/buf": "1.28.1",
"@bufbuild/jest-environment-jsdom": "^0.1.1",
- "@bufbuild/protobuf": "^1.4.1",
- "@bufbuild/protoc-gen-es": "^1.4.1",
- "@connectrpc/connect": "^1.1.3",
- "@connectrpc/connect-web": "^1.1.3",
- "@connectrpc/protoc-gen-connect-es": "^1.1.3",
+ "@bufbuild/protobuf": "^1.5.1",
+ "@bufbuild/protoc-gen-es": "^1.5.1",
+ "@connectrpc/connect": "^1.1.4",
+ "@connectrpc/connect-web": "^1.1.4",
+ "@connectrpc/protoc-gen-connect-es": "^1.1.4",
"@tanstack/react-query": "^5.4.3",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.33",
diff --git a/packages/connect-query/src/call-unary-method.test.ts b/packages/connect-query/src/call-unary-method.test.ts
new file mode 100644
index 00000000..bf36edfc
--- /dev/null
+++ b/packages/connect-query/src/call-unary-method.test.ts
@@ -0,0 +1,80 @@
+// Copyright 2021-2023 The Connect Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { describe, expect, it } from "@jest/globals";
+import type { QueryFunctionContext } from "@tanstack/react-query";
+import { useQueries } from "@tanstack/react-query";
+import { renderHook, waitFor } from "@testing-library/react";
+
+import { callUnaryMethod } from "./call-unary-method";
+import type { ConnectQueryKey } from "./connect-query-key";
+import { createConnectQueryKey } from "./connect-query-key";
+import { defaultOptions } from "./default-options";
+import { ElizaService } from "./gen/eliza_connect";
+import type { SayRequest } from "./gen/eliza_pb";
+import { mockEliza, wrapper } from "./jest/test-utils";
+
+const sayMethodDescriptor = {
+ ...ElizaService.methods.say,
+ service: {
+ typeName: ElizaService.typeName,
+ },
+};
+
+describe("callUnaryMethod", () => {
+ it("can be used with useQueries", async () => {
+ const { result } = renderHook(
+ () => {
+ const [query1] = useQueries({
+ queries: [
+ {
+ queryKey: createConnectQueryKey(sayMethodDescriptor, {
+ sentence: "query 1",
+ }),
+ queryFn: async ({
+ queryKey,
+ signal,
+ }: QueryFunctionContext>) => {
+ const res = await callUnaryMethod(
+ sayMethodDescriptor,
+ queryKey[2],
+ {
+ transport: mockEliza({
+ sentence: "Response 1",
+ }),
+ callOptions: {
+ signal,
+ },
+ },
+ );
+ return res;
+ },
+ },
+ ],
+ });
+ return {
+ query1,
+ };
+ },
+ wrapper({
+ defaultOptions,
+ }),
+ );
+
+ await waitFor(() => {
+ expect(result.current.query1.isSuccess).toBeTruthy();
+ });
+ expect(result.current.query1.data?.sentence).toEqual("Response 1");
+ });
+});
diff --git a/packages/connect-query/src/call-unary-method.ts b/packages/connect-query/src/call-unary-method.ts
new file mode 100644
index 00000000..a19ac2f6
--- /dev/null
+++ b/packages/connect-query/src/call-unary-method.ts
@@ -0,0 +1,46 @@
+// Copyright 2021-2023 The Connect Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import type { Message, PartialMessage } from "@bufbuild/protobuf";
+import type { CallOptions, Transport } from "@connectrpc/connect";
+
+import type { MethodUnaryDescriptor } from "./method-unary-descriptor";
+
+/**
+ * Call a unary method given it's signature and input.
+ */
+export async function callUnaryMethod<
+ I extends Message,
+ O extends Message,
+>(
+ methodType: MethodUnaryDescriptor,
+ input: PartialMessage | undefined,
+ {
+ callOptions,
+ transport,
+ }: {
+ transport: Transport;
+ callOptions?: CallOptions | undefined;
+ },
+): Promise {
+ const result = await transport.unary(
+ { typeName: methodType.service.typeName, methods: {} },
+ methodType,
+ callOptions?.signal,
+ callOptions?.timeoutMs,
+ callOptions?.headers,
+ input ?? {},
+ );
+ return result.message;
+}
diff --git a/packages/connect-query/src/connect-query-key.test.ts b/packages/connect-query/src/connect-query-key.test.ts
index c0326e25..e5808fb5 100644
--- a/packages/connect-query/src/connect-query-key.test.ts
+++ b/packages/connect-query/src/connect-query-key.test.ts
@@ -14,39 +14,37 @@
import { describe, expect, it } from "@jest/globals";
-import { makeConnectQueryKeyGetter } from "./connect-query-key";
+import { createConnectQueryKey } from "./connect-query-key";
+import { SayRequest } from "./gen/eliza_pb";
import { disableQuery } from "./utils";
describe("makeQueryKey", () => {
- const queryKey = makeConnectQueryKeyGetter(
- "service.typeName",
- "methodInfo.name",
- );
+ const methodDescriptor = {
+ I: SayRequest,
+ name: "name",
+ service: {
+ typeName: "service.typeName",
+ },
+ };
it("makes a query key with input", () => {
- const key = queryKey({ value: "someValue" });
+ const key = createConnectQueryKey(methodDescriptor, {
+ sentence: "someValue",
+ });
expect(key).toStrictEqual([
"service.typeName",
- "methodInfo.name",
- { value: "someValue" },
+ "name",
+ { sentence: "someValue" },
]);
});
it("allows empty inputs", () => {
- const key = queryKey();
- expect(key).toStrictEqual([
- "service.typeName",
- "methodInfo.name",
- {}, // TODO(paul) is it better to have an empty object or have nothing? the original implementation had an empty object
- ]);
+ const key = createConnectQueryKey(methodDescriptor);
+ expect(key).toStrictEqual(["service.typeName", "name", {}]);
});
it("makes a query key with a disabled input", () => {
- const key = queryKey(disableQuery);
- expect(key).toStrictEqual([
- "service.typeName",
- "methodInfo.name",
- {}, // TODO(paul) is it better to have an empty object or have nothing? the original implementation had an empty object
- ]);
+ const key = createConnectQueryKey(methodDescriptor, disableQuery);
+ expect(key).toStrictEqual(["service.typeName", "name", {}]);
});
});
diff --git a/packages/connect-query/src/connect-query-key.ts b/packages/connect-query/src/connect-query-key.ts
index 12d6ec8d..d53d0d4b 100644
--- a/packages/connect-query/src/connect-query-key.ts
+++ b/packages/connect-query/src/connect-query-key.ts
@@ -12,20 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import type {
- Message,
- MethodInfo,
- PartialMessage,
- ServiceType,
-} from "@bufbuild/protobuf";
+import type { Message, PartialMessage } from "@bufbuild/protobuf";
+import type { MethodUnaryDescriptor } from "./method-unary-descriptor";
import type { DisableQuery } from "./utils.js";
import { disableQuery } from "./utils.js";
/**
* TanStack Query requires query keys in order to decide when the query should automatically update.
*
- * `QueryKey`s in TanStack Query are usually arbitrary, but Connect-Query uses the approach of creating a query key that begins with the least specific information: the service's `typeName`, followed by the method name, and ending with the most specific information to identify a particular request: the input message itself.
+ * `QueryKey`s in TanStack Query are usually arbitrary, but Connect-React-Query uses the approach of creating a query key that begins with the least specific information: the service's `typeName`, followed by the method name, and ending with the most specific information to identify a particular request: the input message itself.
*
* For example, for a query key might look like this:
*
@@ -42,27 +38,23 @@ export type ConnectQueryKey> = [
input: PartialMessage,
];
-/**
- * This type is useful in situations where you want to use partial matching for TanStack Query `queryKey`s
- */
-export type ConnectPartialQueryKey = [
- serviceTypeName: string,
- methodName: string,
-];
-
/**
* TanStack Query requires query keys in order to decide when the query should automatically update.
*
- * In Connect-Query, much of this is handled automatically by this function.
+ * In Connect-React-Query, much of this is handled automatically by this function.
*
- * @see ConnectQueryKey for information on the components of Connect-Query's keys.
+ * @see ConnectQueryKey for information on the components of Connect-React-Query's keys.
*/
-export const makeConnectQueryKeyGetter =
- (typeName: ServiceType["typeName"], methodInfoName: MethodInfo["name"]) =>
- >(
- input?: DisableQuery | PartialMessage | undefined,
- ): ConnectQueryKey => [
- typeName,
- methodInfoName,
+export function createConnectQueryKey<
+ I extends Message,
+ O extends Message,
+>(
+ methodDescriptor: Pick, "I" | "name" | "service">,
+ input?: DisableQuery | PartialMessage | undefined,
+): ConnectQueryKey {
+ return [
+ methodDescriptor.service.typeName,
+ methodDescriptor.name,
input === disableQuery || !input ? {} : input,
];
+}
diff --git a/packages/connect-query/src/create-query-functions.test.ts b/packages/connect-query/src/create-query-functions.test.ts
deleted file mode 100644
index 172b121f..00000000
--- a/packages/connect-query/src/create-query-functions.test.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type {
- MethodInfo,
- MethodInfoUnary,
- PartialMessage,
- ServiceType,
-} from "@bufbuild/protobuf";
-import { MethodKind } from "@bufbuild/protobuf";
-import { describe, expect, it, jest } from "@jest/globals";
-
-import type { ConnectQueryKey } from "./connect-query-key";
-import {
- createQueryFunctions,
- isSupportedMethod,
-} from "./create-query-functions";
-import type { UnaryFunctions } from "./create-unary-functions";
-import { ElizaService } from "./gen/eliza_connect";
-import type { SayRequest, SayResponse } from "./gen/eliza_pb";
-import type { Alike, Equal, Expect, ExpectFalse } from "./jest/test-utils";
-import type { DisableQuery } from "./utils";
-
-describe("isSupportedMethod", () => {
- const patch = (kind: MethodKind) => ({
- ...ElizaService.methods.say,
- kind,
- });
-
- it("allows Unary methods", () => {
- const method = patch(MethodKind.Unary);
- const isSupported = isSupportedMethod(method);
- expect(isSupported).toBeTruthy();
-
- if (!isSupported) {
- // eslint-disable-next-line jest/no-conditional-expect -- this conditional is required for type inferencing
- expect("that this should not fail").toStrictEqual("a failure");
- return; // returning is necessary for TypeScript inference below
- }
-
- type ExpectType_Kind = Expect>;
- });
-
- it("rejects all other methods", () => {
- expect(isSupportedMethod(patch(MethodKind.BiDiStreaming))).toBeFalsy();
- expect(isSupportedMethod(patch(MethodKind.ClientStreaming))).toBeFalsy();
- expect(isSupportedMethod(patch(MethodKind.ServerStreaming))).toBeFalsy();
- });
-});
-
-describe("createQueryHooks", () => {
- const service = ElizaService;
-
- it("creates hooks for unary methods", () => {
- const hooks = createQueryFunctions({
- service: {
- ...service,
- methods: {
- ...service.methods,
- ["sayAgain"]: {
- ...service.methods.say,
- },
- },
- },
- });
-
- type ExpectType_Say = Expect<
- Equal>
- >;
-
- type ExpectType_HooksKeys = Expect<
- Equal<[keyof typeof hooks], ["say" | "sayAgain"]>
- >;
- expect(Object.keys(hooks)).toStrictEqual(["say", "sayAgain"]);
-
- type ExpectType_Converse = ExpectFalse<
- Alike
- >;
- expect(hooks).not.toHaveProperty("converse");
-
- type ExpectType_GetQueryKey = Equal<
- (typeof hooks)["say"]["getQueryKey"],
- (
- input: DisableQuery | PartialMessage,
- ) => ConnectQueryKey
- >;
- expect(hooks.say).toHaveProperty("getQueryKey", expect.any(Function));
-
- type ExpectType_MethodInfo = Expect<
- Equal<
- (typeof hooks)["say"]["methodInfo"],
- MethodInfoUnary
- >
- >;
- expect(hooks.say).toHaveProperty("methodInfo", service.methods.say);
- });
-
- it("filters out non-unary methods", () => {
- const customService: ServiceType = {
- ...service,
- methods: {
- ["BiDiStreaming"]: {
- name: "BiDiStreaming",
- kind: MethodKind.BiDiStreaming,
- } as MethodInfo,
- ["ClientStreaming"]: {
- name: "ClientStreaming",
- kind: MethodKind.ClientStreaming,
- } as MethodInfo,
- ["ServerStreaming"]: {
- name: "ServerStreaming",
- kind: MethodKind.ServerStreaming,
- } as MethodInfo,
- ["Unary"]: {
- name: "Unary",
- kind: MethodKind.Unary,
- } as MethodInfo,
- },
- };
-
- const hooks = createQueryFunctions({ service: customService });
-
- expect(Object.keys(hooks)).toStrictEqual(["Unary"]);
- });
-
- it("skips unrecognized or missing method kinds", () => {
- jest.spyOn(console, "error").mockImplementation(() => {});
-
- const customService: ServiceType = {
- ...service,
- methods: {
- ["Missing"]: {
- name: "Missing",
- } as MethodInfo,
- ["Bad"]: {
- name: "Bad",
- kind: "Bad",
- } as unknown as MethodInfo,
- ["Unary"]: {
- name: "Unary",
- kind: MethodKind.Unary,
- } as MethodInfo,
- },
- };
-
- const hooks = createQueryFunctions({ service: customService });
-
- expect(Object.keys(hooks)).toStrictEqual(["Unary"]);
-
- expect(console.error).toHaveBeenCalledWith(
- new Error("Invariant failed: unrecognized method kind: undefined"),
- );
- expect(console.error).toHaveBeenCalledWith(
- new Error("Invariant failed: unrecognized method kind: Bad"),
- );
- });
-});
diff --git a/packages/connect-query/src/create-query-functions.ts b/packages/connect-query/src/create-query-functions.ts
deleted file mode 100644
index 65c1cfa6..00000000
--- a/packages/connect-query/src/create-query-functions.ts
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type {
- Message,
- MethodInfo,
- MethodInfoUnary,
- ServiceType,
-} from "@bufbuild/protobuf";
-import { MethodKind } from "@bufbuild/protobuf";
-import type { Transport } from "@connectrpc/connect";
-
-import type { UnaryFunctions } from "./create-unary-functions.js";
-import { createUnaryFunctions } from "./create-unary-functions.js";
-import { unreachableCase } from "./utils.js";
-
-/**
- * This is an array of supported `MethodKind`s
- */
-export const supportedMethodKinds = [MethodKind.Unary];
-
-/**
- * This is a convenience type that returns the `MethodKind`s that are supported by Connect-Query
- */
-export type SupportedMethodKinds = MethodKind.Unary;
-
-/**
- * This predicate returns true if the service is a kind that's supported by Connect-Query.
- *
- * Today, only Unary services are supported.
- */
-export const isSupportedMethod = , O extends Message>(
- method: MethodInfo,
-): method is IsSupportedMethod => {
- return supportedMethodKinds.includes(method.kind);
-};
-
-/**
- * A Convenience TypeScript type that validates that a given MethodInfo is supported
- */
-export type IsSupportedMethod<
- I extends Message,
- O extends Message,
-> = MethodInfoUnary;
-
-/** This explicitly states the `MethodKind`s that are supported by Connect-Query and provides a means to convert those kinds into the Hooks types (e.g. UnaryFunctions) */
-export interface SupportedMethodInfo {
- // in the future, this type is constructed so that new method kinds can be added like this:
- // [MethodKind.BiDiStreaming]: MethodInfoBiDiStreaming ? BiDiStreamingHooks : never
-
- /** this is a mapping from a unary method kind to the hooks a unary method supports */
- [MethodKind.Unary]: MI extends MethodInfoUnary
- ? UnaryFunctions
- : never;
-}
-
-/** This is a helper for `QueryHooks` */
-type SupportedFunctions =
- MI["kind"] extends keyof SupportedMethodInfo
- ? SupportedMethodInfo[MI["kind"]]
- : never;
-
-/**
- * QueryHooks is a object that provides all the functionality you need to use TanStack Query with a Connect server.
- *
- * The properties are generated from the method name, and the values of the object depend on the method's kind (i.e. Unary, ServerStreaming, ClientStreaming, BiDiStreaming).
- *
- * Note: Today, only Unary method kinds are supported.
- */
-export type QueryFunctions = {
- [Method in keyof Service["methods"] as Exclude<
- Method,
- keyof SupportedFunctions
- >]: SupportedFunctions;
-};
-
-/**
- * Chances are, what you want to use is `createQueryService`.
- *
- * This helper creates the necessary hooks (stored in a object with one key for each method).
- *
- * It does not, however, provide any caching (like `createQueryService` does) which means that each time you call this function it will generate a fresh set of hooks, even if you call with the same service and transport.
- */
-export const createQueryFunctions = ({
- service: { typeName, methods },
- transport,
-}: {
- service: Service;
- transport?: Transport | undefined;
-}): QueryFunctions =>
- Object.entries(methods).reduce(
- (accumulator, [localName, methodInfo]) => {
- switch (methodInfo.kind) {
- case MethodKind.Unary: {
- return {
- ...accumulator,
- [localName]: createUnaryFunctions({
- methodInfo,
- typeName,
- transport,
- }),
- };
- }
-
- case MethodKind.BiDiStreaming:
- // not implemented
- return accumulator;
-
- case MethodKind.ClientStreaming:
- // not implemented
- return accumulator;
-
- case MethodKind.ServerStreaming:
- // not implemented
- return accumulator;
-
- default:
- console.error(
- unreachableCase(
- methodInfo,
- `unrecognized method kind: ${
- (methodInfo as { kind: string }).kind
- }`,
- ),
- );
- return accumulator;
- }
- },
- // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter -- making this change causes the wrong overload to be selected
- {} as QueryFunctions,
- );
diff --git a/packages/connect-query/src/create-query-service.test.tsx b/packages/connect-query/src/create-query-service.test.tsx
deleted file mode 100644
index 15135f5d..00000000
--- a/packages/connect-query/src/create-query-service.test.tsx
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type { MethodInfo, PartialMessage } from "@bufbuild/protobuf";
-import { describe, expect, it } from "@jest/globals";
-import type { QueryFunctionContext } from "@tanstack/react-query";
-import { renderHook } from "@testing-library/react";
-
-import type { ConnectQueryKey } from "./connect-query-key";
-import { createQueryService } from "./create-query-service";
-import { ElizaService } from "./gen/eliza_connect";
-import type { SayRequest, SayResponse } from "./gen/eliza_pb";
-import type { Equal, Expect } from "./jest/test-utils";
-import { mockEliza, wrapper } from "./jest/test-utils";
-import { isUnaryMethod } from "./utils";
-
-describe("createQueryService", () => {
- const service = ElizaService;
- const methodName = "say";
- const input = { sentence: "ziltoid" } satisfies PartialMessage;
-
- it("uses a custom transport", async () => {
- const transport = mockEliza();
- const { result } = renderHook(async () => {
- const { queryFn } = createQueryService({
- service,
- transport,
- }).say.createUseQueryOptions(input, { transport });
- return queryFn();
- }, wrapper());
-
- const response = await result.current;
-
- expect(response.sentence).toEqual(`Hello ${input.sentence}`);
- });
-
- it("contains the right options", () => {
- const hook = createQueryService({ service });
-
- const unaryMethods = Object.keys(service.methods).filter((key) =>
- isUnaryMethod(
- service.methods[key as keyof typeof service.methods] as MethodInfo,
- ),
- );
- expect(Object.keys(hook)).toHaveLength(unaryMethods.length);
-
- expect(hook).toHaveProperty(
- methodName,
- expect.objectContaining({
- methodInfo: service.methods[methodName],
- createUseQueryOptions: expect.any(Function),
- }),
- );
- });
-
- describe("createUseQueryOptions", () => {
- it("has the appropriate properties", () => {
- const {
- result: { current: queryOptions },
- } = renderHook(
- () =>
- createQueryService({ service }).say.createUseQueryOptions(input, {
- transport: mockEliza(),
- }),
- wrapper(),
- );
-
- type ExpectType_Enabled = Expect<
- Equal
- >;
- expect(queryOptions).toHaveProperty("enabled", true);
-
- type ExpectType_QueryKey = Expect<
- Equal>
- >;
- expect(queryOptions).toHaveProperty("queryKey", [
- service.typeName,
- service.methods[methodName].name,
- input,
- ]);
-
- type ExpectType_QueryFn = Expect<
- Equal<
- typeof queryOptions.queryFn,
- (
- context?:
- | QueryFunctionContext>
- | undefined,
- ) => Promise
- >
- >;
- expect(queryOptions).toHaveProperty("queryFn", expect.any(Function));
- });
- });
-});
diff --git a/packages/connect-query/src/create-query-service.ts b/packages/connect-query/src/create-query-service.ts
deleted file mode 100644
index 52c8da8f..00000000
--- a/packages/connect-query/src/create-query-service.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type { ServiceType } from "@bufbuild/protobuf";
-import type { Transport } from "@connectrpc/connect";
-
-import type { QueryFunctions } from "./create-query-functions.js";
-import { createQueryFunctions } from "./create-query-functions.js";
-
-const servicesToHooks = new Map>();
-
-/**
- * `createQueryService` is the main entrypoint for Connect-Query.
- *
- * Pass in a service and you will receive an object with properties for each of your services and values that provide hooks for those services that you can then give to Tanstack Query. The `ServiceType` TypeScript interface is provided by Protobuf-ES (`@bufbuild/protobuf`) while generated service definitions are provided by Connect-Web (`@connectrpc/connect-web`).
- *
- * `Transport` refers to the mechanism by which your client will make the actual network calls. If you want to use a custom transport, you can optionally provide one with a call to `useTransport`, which Connect-Query exports. Otherwise, the default transport from React context will be used. This default transport is placed on React context by the `TransportProvider`. Whether you pass a custom transport or you use `TransportProvider`, in both cases you'll need to use one of `@connectrpc/connect-web`'s exports `createConnectTransport` or `createGrpcWebTransport`.
- *
- * Note that the most memory performant approach is to use the transport on React Context by using the `TransportProvider` because that provider is memoized by React, but also that any calls to `createQueryService` with the same service is cached by this function.
- *
- * @example
- *
- * export const { say } = createQueryService({
- * service: {
- * methods: {
- * say: {
- * name: "Say",
- * kind: MethodKind.Unary,
- * I: SayRequest,
- * O: SayResponse,
- * },
- * },
- * typeName: "connectrpc.eliza.v1.ElizaService",
- * },
- * });
- *
- * const { data, isLoading, ...etc } = useQuery(say.createUseQueryOptions());
- */
-export const createQueryService = ({
- service,
- transport,
-}: {
- service: Service;
- transport?: Transport;
-}): QueryFunctions => {
- if (transport) {
- // custom transports are not cached
- return createQueryFunctions({
- service,
- transport,
- });
- }
-
- let hooks = servicesToHooks.get(service) as
- | QueryFunctions
- | undefined;
- if (!hooks) {
- hooks = createQueryFunctions({ service });
- servicesToHooks.set(service, hooks);
- }
-
- return hooks;
-};
diff --git a/packages/connect-query/src/create-unary-functions.test.ts b/packages/connect-query/src/create-unary-functions.test.ts
deleted file mode 100644
index f3fb64c6..00000000
--- a/packages/connect-query/src/create-unary-functions.test.ts
+++ /dev/null
@@ -1,1211 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type {
- Message,
- MethodInfoUnary,
- PartialMessage,
-} from "@bufbuild/protobuf";
-import { MethodKind } from "@bufbuild/protobuf";
-import type { CallOptions, Transport } from "@connectrpc/connect";
-import { Code, ConnectError } from "@connectrpc/connect";
-import { describe, expect, it, jest } from "@jest/globals";
-import type {
- GetNextPageParamFunction,
- QueryFunction,
- QueryFunctionContext,
- UseMutationResult,
-} from "@tanstack/react-query";
-import { useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query";
-import { renderHook, waitFor } from "@testing-library/react";
-
-import type {
- ConnectPartialQueryKey,
- ConnectQueryKey,
-} from "./connect-query-key";
-import { createUnaryFunctions } from "./create-unary-functions";
-import { defaultOptions } from "./default-options";
-import {
- BigIntService,
- ElizaService,
- PaginatedService,
-} from "./gen/eliza_connect";
-import type { CountRequest, CountResponse, SayRequest } from "./gen/eliza_pb";
-import { SayResponse } from "./gen/eliza_pb";
-import type { Equal, Expect } from "./jest/test-utils";
-import {
- mockBigInt,
- mockCallOptions,
- mockEliza,
- mockPaginatedTransport,
- mockStatefulBigIntTransport,
- sleep,
- wrapper,
-} from "./jest/test-utils";
-import type { DisableQuery } from "./utils";
-import { disableQuery } from "./utils";
-
-const genCount = createUnaryFunctions({
- methodInfo: BigIntService.methods.count,
- typeName: BigIntService.typeName,
-});
-
-const genSay = createUnaryFunctions({
- methodInfo: ElizaService.methods.say,
- typeName: ElizaService.typeName,
-});
-
-const genPaginated = createUnaryFunctions({
- methodInfo: PaginatedService.methods.list,
- typeName: PaginatedService.typeName,
-});
-
-describe("createUnaryFunctions", () => {
- it("produces the intended API surface", () => {
- type ExpectType_sayKeys = Expect<
- Equal<
- keyof typeof genSay,
- | "createData"
- | "createUseInfiniteQueryOptions"
- | "createUseMutationOptions"
- | "createUseQueryOptions"
- | "getPartialQueryKey"
- | "getQueryKey"
- | "methodInfo"
- | "setQueriesData"
- | "setQueryData"
- >
- >;
-
- const matchers: Record = {
- createData: expect.any(Function),
- createUseQueryOptions: expect.any(Function),
- getPartialQueryKey: expect.any(Function),
- getQueryKey: expect.any(Function),
- methodInfo: expect.objectContaining(ElizaService.methods.say),
- setQueriesData: expect.any(Function),
- setQueryData: expect.any(Function),
- createUseInfiniteQueryOptions: expect.any(Function),
- createUseMutationOptions: expect.any(Function),
- };
-
- const sorter = (a: string, b: string) => a.localeCompare(b);
- expect(Object.keys(genSay).sort(sorter)).toStrictEqual(
- Object.keys(matchers).sort(sorter),
- );
-
- expect(genSay).toMatchObject(matchers);
- });
-
- it("throws when provided non unary services", () => {
- expect(() => {
- createUnaryFunctions({
- methodInfo: {
- ...ElizaService.methods.say,
- // @ts-expect-error(2322) intentionally incorrect
- kind: MethodKind.BiDiStreaming,
- },
- service: ElizaService,
- });
- }).toThrow("createUnaryFunctions was passed a non unary method, Say");
- });
-
- it("uses a custom transport", async () => {
- const input = { sentence: "ziltoid" } satisfies PartialMessage;
- const transport = mockEliza();
-
- const { result } = renderHook(async () => {
- const { queryFn } = genSay.createUseQueryOptions(input, { transport });
-
- return queryFn();
- });
-
- const response = await result.current;
-
- expect(response.sentence).toEqual(`Hello ${input.sentence}`);
- });
-
- describe("createData", () => {
- it("creates data as expected", () => {
- const partial: PartialMessage = {
- count: 1n,
- };
- const expected = new BigIntService.methods.count.O(partial);
-
- expect(genCount.createData(partial)).toStrictEqual(expected);
- });
- });
-
- describe("methodInfo", () => {
- it("attaches the methodInfo for convenience", () => {
- type ExpectType_sayMethodInfo = Expect<
- Equal<
- typeof genSay.methodInfo,
- MethodInfoUnary
- >
- >;
- expect(genSay.methodInfo).toStrictEqual(ElizaService.methods.say);
- });
- });
-
- describe("setQueriesData & setQueryData", () => {
- /** @returns 2n */
- const partialUpdater = new BigIntService.methods.count.O({ count: 2n });
-
- const request = { add: 2n };
-
- describe("setQueriesData", () => {
- it("returns the correct queryKey", () => {
- const [{ queryKey }] = genCount.setQueriesData(partialUpdater);
- type ExpectType_QueryKey = Expect<
- Equal
- >;
- expect(queryKey).toStrictEqual([
- "connectrpc.eliza.v1.BigIntService",
- "Count",
- ]);
- });
-
- it("allows a partial message updater", async () => {
- const transport = mockBigInt();
- const { queryClient, ...rest } = wrapper({ defaultOptions });
-
- const { result, rerender } = renderHook(
- () =>
- useQuery(genCount.createUseQueryOptions(request, { transport })),
- rest,
- );
- type ExpectType_Data = Expect<
- Equal
- >;
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data?.count).toStrictEqual(1n);
-
- queryClient.setQueriesData(...genCount.setQueriesData(partialUpdater));
- rerender();
-
- // this value comes from the partial updater
- expect(result.current.data?.count).toStrictEqual(2n);
- });
- });
-
- describe("setQueryData", () => {
- it("returns the correct queryKey", () => {
- const [queryKey] = genCount.setQueryData(partialUpdater, request);
- type ExpectType_QueryKey = Expect<
- Equal>
- >;
- expect(queryKey).toStrictEqual([
- "connectrpc.eliza.v1.BigIntService",
- "Count",
- request,
- ]);
- });
-
- it("allows a partial message updater", async () => {
- const { queryClient, ...rest } = wrapper({ defaultOptions });
-
- const { result, rerender } = renderHook(
- () =>
- useQuery(
- genCount.createUseQueryOptions(request, {
- transport: mockBigInt(),
- }),
- ),
- rest,
- );
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data?.count).toStrictEqual(1n);
-
- queryClient.setQueryData(
- ...genCount.setQueryData(partialUpdater, request),
- );
- rerender();
-
- // this value comes from the partial updater
- expect(result.current.data?.count).toStrictEqual(2n);
- });
-
- it("allows a function updater", async () => {
- /** @returns input + 1n */
- const functionUpdater = (
- { count }: { count: bigint } = { count: 1n },
- ) =>
- new BigIntService.methods.count.O({
- count: count + 1n,
- });
-
- const { queryClient, ...rest } = wrapper({ defaultOptions });
- const { result, rerender } = renderHook(
- () =>
- useQuery(
- genCount.createUseQueryOptions(request, {
- transport: mockBigInt(),
- }),
- ),
- rest,
- );
-
- type ExpectType_Data = Expect<
- Equal
- >;
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data?.count).toStrictEqual(1n);
-
- queryClient.setQueryData(
- ...genCount.setQueryData(functionUpdater, request),
- );
- rerender();
-
- // this value comes from the partial updater
- expect(result.current.data?.count).toStrictEqual(2n);
- });
- });
- });
-
- describe("useInfiniteQuery", () => {
- it("has the intended API surface", () => {
- type params = Parameters;
- type ExpectType_UseInfiniteQueryParamsLength = Expect<
- Equal
- >;
-
- type ExpectType_UseInfiniteQueryParams0 = Expect<
- Equal<
- params[0],
- | DisableQuery
- | (PartialMessage &
- Required, "add">>)
- >
- >;
-
- type returnType = ReturnType<
- typeof genCount.createUseInfiniteQueryOptions
- >;
-
- type ExpectType_UseInfiniteQueryReturnKeys = Expect<
- Equal<
- keyof returnType,
- | "enabled"
- | "getNextPageParam"
- | "initialPageParam"
- | "queryFn"
- | "queryKey"
- | "throwOnError"
- >
- >;
-
- type ExpectType_UseInfiniteQueryReturn = Expect<
- Equal<
- returnType,
- {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: QueryFunction<
- CountResponse,
- ConnectQueryKey,
- bigint | undefined
- >;
- getNextPageParam: GetNextPageParamFunction<
- bigint | undefined,
- CountResponse
- >;
- throwOnError?: (error: ConnectError) => boolean;
- initialPageParam: bigint | undefined;
- }
- >
- >;
-
- expect(1).toEqual(1);
- });
-
- describe("transport", () => {
- it("prioritizes option transport", async () => {
- const mockTransportContext = mockEliza();
- const mockTransportTopLevel = mockEliza();
- const mockTransportOption = mockEliza({
- sentence: "override",
- });
- const customSay = createUnaryFunctions({
- methodInfo: ElizaService.methods.say,
- typeName: ElizaService.typeName,
- transport: mockTransportTopLevel,
- });
-
- const { result } = renderHook(
- () =>
- useInfiniteQuery(
- customSay.createUseInfiniteQueryOptions(
- { sentence: "Infinity" },
- {
- pageParamKey: "sentence",
- getNextPageParam: () => "0",
- transport: mockTransportOption,
- },
- ),
- ),
- wrapper({}, mockTransportContext),
- );
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data?.pages[0].sentence).toStrictEqual(
- "override",
- );
- });
-
- it("requires a transport", () => {
- expect(() => {
- genCount.createUseInfiniteQueryOptions(
- {
- add: 0n,
- },
- {
- getNextPageParam: (lastPage) => lastPage.count + 1n,
- pageParamKey: "add",
- },
- );
- }).toThrow(
- "Invalid assertion: createUseInfiniteQueryOptions requires you to provide a Transport.",
- );
- });
- });
-
- it("integrates with `useInfiniteQuery`", async () => {
- const input = { page: 1n };
-
- const { result, rerender } = renderHook(
- () =>
- useInfiniteQuery(
- genPaginated.createUseInfiniteQueryOptions(input, {
- pageParamKey: "page",
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport: mockPaginatedTransport(),
- }),
- ),
- wrapper({ defaultOptions }),
- );
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data?.pages).toHaveLength(1);
- expect(result.current.data?.pages[0].page).toStrictEqual(1n);
- expect(result.current.data?.pages[0].items).toStrictEqual([
- `1 Item`,
- `2 Item`,
- `3 Item`,
- ]);
-
- // execute a single increment
- await result.current.fetchNextPage();
- rerender();
-
- expect(result.current.data?.pages).toHaveLength(2);
- expect(result.current.data?.pages[1].page).toStrictEqual(2n);
- expect(result.current.data?.pages[1].items).toStrictEqual([
- `4 Item`,
- `5 Item`,
- `6 Item`,
- ]);
-
- // execute two increments at once
- await result.current.fetchNextPage();
- await result.current.fetchNextPage();
- rerender();
-
- expect(result.current.data?.pages).toHaveLength(4);
- expect(result.current.data?.pages[2].items).toStrictEqual([
- `7 Item`,
- `8 Item`,
- `9 Item`,
- ]);
- expect(result.current.data?.pages[3].items).toStrictEqual([
- `10 Item`,
- `11 Item`,
- `12 Item`,
- ]);
- });
-
- it("is disabled when input matches the `disableQuery` symbol", async () => {
- const { result } = renderHook(
- () =>
- genCount.createUseInfiniteQueryOptions(disableQuery, {
- pageParamKey: "add",
- getNextPageParam: (lastPage) => lastPage.count,
- transport: mockEliza(),
- }),
- wrapper(),
- );
-
- expect(result.current).toHaveProperty("enabled", false);
-
- await expect(result.current.queryFn).rejects.toThrow(
- "Invalid assertion: queryFn does not accept a disabled query",
- );
- });
-
- it("allows a pageParam for the queryFn", async () => {
- const input = { add: 1n };
- const getNextPageParam = (lastPage: CountResponse) => lastPage.count;
- const { result } = renderHook(
- () =>
- genCount.createUseInfiniteQueryOptions(input, {
- pageParamKey: "add",
- getNextPageParam,
- transport: mockBigInt(),
- }),
- wrapper(),
- );
-
- expect(result.current.getNextPageParam).toStrictEqual(getNextPageParam);
-
- const { count } = await result.current.queryFn({
- pageParam: 1n,
- queryKey: genCount.getQueryKey(input),
- meta: {},
- direction: "forward",
- signal: new AbortController().signal,
- });
-
- expect(count).toStrictEqual(1n);
- });
-
- it("passes through callOptions", () => {
- const transport = mockBigInt();
- const transportSpy = jest.spyOn(transport, "unary");
- renderHook(
- () =>
- useInfiniteQuery(
- genCount.createUseInfiniteQueryOptions(
- { add: 1n },
- {
- pageParamKey: "add",
- getNextPageParam: (lastPage) => lastPage.count,
- transport,
- callOptions: mockCallOptions,
- },
- ),
- ),
- wrapper({ defaultOptions }),
- );
-
- expect(transportSpy).toHaveBeenCalledWith(
- expect.anything(), // service
- expect.anything(), // method
- mockCallOptions.signal, // signal
- mockCallOptions.timeoutMs, // timeoutMs
- mockCallOptions.headers, // headers
- expect.anything(), // input
- );
- });
-
- it("passes through the current pageParam on initial fetch", () => {
- const transport = mockPaginatedTransport();
- const transportSpy = jest.spyOn(transport, "unary");
- renderHook(
- () =>
- useInfiniteQuery(
- genPaginated.createUseInfiniteQueryOptions(
- { page: 1n },
- {
- pageParamKey: "page",
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport,
- callOptions: mockCallOptions,
- },
- ),
- ),
- wrapper({ defaultOptions }),
- );
-
- expect(transportSpy).toHaveBeenCalledWith(
- expect.anything(), // service
- expect.anything(), // method
- mockCallOptions.signal, // signal
- mockCallOptions.timeoutMs, // timeoutMs
- mockCallOptions.headers, // headers
- expect.objectContaining({
- page: 1n,
- }), // input
- );
- });
-
- // eslint-disable-next-line jest/expect-expect -- this test is just for a TS error
- it("provides typescript errors if both pageParamKey and applyPageParam are provided", () => {
- const transport = mockPaginatedTransport();
- renderHook(
- () =>
- useInfiniteQuery(
- genPaginated.createUseInfiniteQueryOptions(
- { page: 1n },
- // @ts-expect-error(2345) intentionally invalid applyPageParam + pageParamKey
- {
- pageParamKey: "page",
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport,
- callOptions: mockCallOptions,
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return -- test ignore
- applyPageParam: (input: unknown) => ({
- // @ts-expect-error(2345) ignore these errors for testing
- ...input,
- }),
- },
- ),
- ),
- wrapper({ defaultOptions }),
- );
- });
-
- it("removes the specified pageParamKey by default", () => {
- const transport = mockPaginatedTransport();
- const { result } = renderHook(
- () =>
- genPaginated.createUseInfiniteQueryOptions(
- { page: 1n },
- {
- pageParamKey: "page",
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport,
- callOptions: mockCallOptions,
- },
- ),
- wrapper({ defaultOptions }),
- );
-
- expect(result.current.queryKey).toStrictEqual([
- "connectrpc.eliza.v1.PaginatedService",
- "List",
- {
- page: undefined,
- },
- ]);
- });
-
- it("allows cleaning the queryKey", () => {
- const transport = mockPaginatedTransport();
- const { result } = renderHook(
- () =>
- genPaginated.createUseInfiniteQueryOptions(
- { page: 1n },
- {
- applyPageParam: ({ pageParam, input }) => {
- if (pageParam === undefined) {
- return input;
- }
- return {
- ...input,
- page: pageParam,
- };
- },
- sanitizeInputKey: (input) => ({
- ...input,
- page: undefined,
- }),
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport,
- callOptions: mockCallOptions,
- },
- ),
- wrapper({ defaultOptions }),
- );
-
- expect(result.current.queryKey).toStrictEqual([
- "connectrpc.eliza.v1.PaginatedService",
- "List",
- {
- page: undefined,
- },
- ]);
- });
-
- it("allows applying a page param dynamically", () => {
- const transport = mockPaginatedTransport();
- const transportSpy = jest.spyOn(transport, "unary");
- renderHook(
- () =>
- useInfiniteQuery(
- genPaginated.createUseInfiniteQueryOptions(
- { page: 1n },
- {
- applyPageParam: ({ pageParam, input }) => {
- if (pageParam === undefined) {
- return {
- ...input,
- page: 2n,
- };
- }
- return {
- ...input,
- page: pageParam,
- };
- },
- getNextPageParam: (lastPage) => lastPage.page + 1n,
- transport,
- callOptions: mockCallOptions,
- },
- ),
- ),
- wrapper({ defaultOptions }),
- );
-
- expect(transportSpy).toHaveBeenCalledWith(
- expect.anything(), // service
- expect.anything(), // method
- mockCallOptions.signal, // signal
- mockCallOptions.timeoutMs, // timeoutMs
- mockCallOptions.headers, // headers
- expect.objectContaining({
- page: 2n,
- }), // input
- );
- });
- });
-
- describe("useMutation", () => {
- it("prioritizes option transport", async () => {
- const mockTransportContext = mockEliza();
- const mockTransportTopLevel = mockEliza();
- const mockTransportOption = mockEliza({
- sentence: "mockTransportOption",
- });
-
- const customSay = createUnaryFunctions({
- methodInfo: ElizaService.methods.say,
- typeName: ElizaService.typeName,
- transport: mockTransportTopLevel,
- });
- const { result } = renderHook(
- () =>
- useMutation(
- customSay.createUseMutationOptions({
- transport: mockTransportOption,
- }),
- ),
- wrapper({}, mockTransportContext),
- );
-
- result.current.mutate({});
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(result.current.data).toMatchObject({
- sentence: "mockTransportOption",
- });
- });
-
- it("requires a transport", () => {
- expect(() => {
- genCount.createUseMutationOptions();
- }).toThrow(
- "Invalid assertion: createUseMutationOptions requires you to provide a Transport.",
- );
- });
-
- it("has the intended API surface", () => {
- type params = Parameters;
-
- type ExpectType_UseMutationParamsLength = Expect<
- Equal
- >;
-
- type ExpectType_UseMutationParams0 = Expect<
- Equal<
- params[0],
- | {
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- }
- | undefined
- >
- >;
-
- type ExpectType_UseMutationReturn = Expect<
- Equal<
- ReturnType,
- {
- mutationFn: (
- input: PartialMessage,
- context?:
- | QueryFunctionContext>
- | undefined,
- ) => Promise;
- onError?: (error: ConnectError) => void;
- }
- >
- >;
-
- const { result } = renderHook(
- () =>
- genCount.createUseMutationOptions({
- transport: mockBigInt(),
- }),
- wrapper(),
- );
-
- expect(
- Object.keys(result.current).sort((a, b) => a.localeCompare(b)),
- ).toStrictEqual(["mutationFn"]);
- });
-
- it("handles a custom onError", async () => {
- jest.resetAllMocks();
- const onError = jest.fn();
-
- const { result, rerender } = renderHook(
- () =>
- useMutation({
- ...genCount.createUseMutationOptions({
- onError,
- transport: mockBigInt(),
- }),
- mutationFn: () => {
- throw new ConnectError("error", Code.Aborted);
- },
- }),
- wrapper({ defaultOptions }),
- );
-
- rerender();
-
- expect(result.current.error).toStrictEqual(null);
- expect(result.current.isError).toStrictEqual(false);
- expect(onError).toHaveBeenCalledTimes(0);
-
- type ExpectType_Error = Expect<
- Equal
- >;
-
- result.current.mutate();
-
- await sleep(10);
-
- expect(result.current.error?.code).toStrictEqual(Code.Aborted);
- expect(result.current.isError).toStrictEqual(true);
- expect(onError).toHaveBeenCalledTimes(1);
- });
-
- it("makes a mutation", async () => {
- /** this input will add one to the total count */
- const input = { add: 2n };
-
- const { queryClient, transport, ...rest } = wrapper(
- { defaultOptions },
- mockStatefulBigIntTransport(),
- );
- const { result } = renderHook(
- () => ({
- mut: useMutation({
- ...genCount.createUseMutationOptions({
- transport,
- }),
- mutationKey: genCount.getQueryKey(input),
- onSuccess: () => {
- const queryKey = genCount.getQueryKey(input);
- const { count = 0n } =
- queryClient.getQueryData(queryKey) ?? {};
-
- queryClient.setQueryData(
- ...genCount.setQueryData({ count: count + input.add }, input),
- );
- },
- }),
- get: useQuery(
- genCount.createUseQueryOptions(input, {
- transport,
- }),
- ),
- }),
- rest,
- );
-
- type ExpectType_MutationFn = Expect<
- Equal<
- typeof result.current.mut,
- UseMutationResult<
- CountResponse,
- ConnectError,
- PartialMessage
- >
- >
- >;
- expect(result.current.mut.data?.count).toStrictEqual(undefined);
- expect(result.current.get.data?.count).toStrictEqual(undefined);
-
- await waitFor(() => {
- expect(result.current.mut.isIdle).toBeTruthy();
- expect(result.current.get.isSuccess).toBeTruthy();
- });
-
- expect(result.current.mut.data?.count).toStrictEqual(undefined);
- expect(result.current.get.data?.count).toStrictEqual(2n);
-
- result.current.mut.mutate(input);
-
- await waitFor(() => {
- expect(result.current.mut.isSuccess).toBeTruthy();
- expect(result.current.get.isSuccess).toBeTruthy();
- });
-
- expect(result.current.mut.data?.count).toStrictEqual(4n);
- expect(result.current.get.data?.count).toStrictEqual(4n);
- });
-
- it("passes through callOptions", async () => {
- const transport = mockBigInt();
- const transportSpy = jest.spyOn(transport, "unary");
- const { result } = renderHook(
- () =>
- useMutation({
- ...genCount.createUseMutationOptions({
- callOptions: mockCallOptions,
- transport,
- }),
- mutationKey: genCount.getQueryKey({ add: 1n }),
- }),
- wrapper({ defaultOptions }),
- );
-
- result.current.mutate({ add: 2n });
-
- await waitFor(() => {
- expect(result.current.isSuccess).toBeTruthy();
- });
-
- expect(transportSpy).toHaveBeenCalledWith(
- expect.anything(), // service
- expect.anything(), // method
- mockCallOptions.signal, // signal
- mockCallOptions.timeoutMs, // timeoutMs
- mockCallOptions.headers, // headers
- expect.anything(), // input
- );
- });
- });
-
- describe("createUseQueryOptions", () => {
- it("has the intended API surface", () => {
- type params = Parameters;
- type ExpectType_UseQueryParams0 = Expect<
- Equal<
- params[0],
- PartialMessage | typeof disableQuery | undefined
- >
- >;
-
- type ExpectType_UseQueryParams1 = Expect<
- Equal<
- params[1],
- | {
- getPlaceholderData?: (
- enabled: boolean,
- ) => PartialMessage | undefined;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- }
- | undefined
- >
- >;
-
- type ExpectType_UseQueryParams2 = Expect<
- Equal
- >;
-
- type ExpectType_UseQueryK = Expect<
- Equal<
- ReturnType,
- {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (
- context?: QueryFunctionContext>,
- ) => Promise;
- placeholderData?: () => SayResponse | undefined;
- throwOnError?: (error: ConnectError) => boolean;
- }
- >
- >;
-
- const result = genSay.createUseQueryOptions(undefined, {
- getPlaceholderData: jest.fn(() => new SayResponse()),
- transport: mockEliza(),
- });
-
- expect(
- Object.keys(result).sort((a, b) => a.localeCompare(b)),
- ).toStrictEqual(["enabled", "placeholderData", "queryFn", "queryKey"]);
- });
-
- describe("enabled", () => {
- it("has the correct type", () => {
- expect.assertions(0);
- const result = genSay.createUseQueryOptions(
- {},
- {
- transport: mockEliza(),
- },
- );
- type ExpectType_Expect = Expect>;
- });
-
- it("is enabled when input does not match the `disableQuery` symbol", () => {
- const result = genSay.createUseQueryOptions(
- {},
- {
- transport: mockEliza(),
- },
- );
-
- expect(result).toHaveProperty("enabled", true);
- });
-
- it("is enabled with an empty input", () => {
- const result = genSay.createUseQueryOptions(undefined, {
- transport: mockEliza(),
- });
-
- expect(result).toHaveProperty("enabled", true);
- });
-
- it("is disabled when input matches the `disableQuery` symbol", () => {
- const result = genSay.createUseQueryOptions(disableQuery, {
- transport: mockEliza(),
- });
-
- expect(result).toHaveProperty("enabled", false);
- });
- });
-
- describe("placeholderData", () => {
- const placeholderSentence: PartialMessage = {
- sentence: "placeholder",
- };
-
- const input: PartialMessage = { sentence: "ziltoid" };
-
- it("has the correct type", () => {
- expect.assertions(0);
- const result = genSay.createUseQueryOptions(undefined, {
- transport: mockEliza(),
- });
- type ExpectType_GetPlaceholderData = Expect<
- Equal<
- typeof result.placeholderData,
- (() => SayResponse | undefined) | undefined
- >
- >;
- });
-
- it("passes through getPlaceholderData, when provided", () => {
- const getPlaceholderData = jest.fn(() => placeholderSentence);
- const result = genSay.createUseQueryOptions(input, {
- getPlaceholderData,
- transport: mockEliza(),
- });
-
- expect(result).toHaveProperty("placeholderData", expect.any(Function));
- expect(getPlaceholderData).not.toHaveBeenCalled();
-
- const response = (
- result.placeholderData as () => Message
- )();
-
- expect(getPlaceholderData).toHaveBeenCalledWith(true);
- expect(response.toJson()).toStrictEqual(placeholderSentence);
- expect(response).toBeInstanceOf(SayResponse);
- });
-
- it("does not pass through getPlaceholderData if not provided", () => {
- const result = genSay.createUseQueryOptions(input, {
- transport: mockEliza(),
- });
-
- expect(result).not.toHaveProperty("getPlaceholderData");
- });
-
- it("will use pass the value of `enabled` to the getPlaceholderData callback", () => {
- const getPlaceholderData = jest.fn<
- (
- enabled?: boolean | undefined,
- ) => PartialMessage | undefined
- >(() => ({}));
- const { result } = renderHook(
- () =>
- useQuery(
- genSay.createUseQueryOptions(disableQuery, {
- getPlaceholderData,
- transport: mockEliza(),
- }),
- ),
- wrapper(),
- );
-
- expect(result.current.data?.sentence).toStrictEqual("");
- expect(getPlaceholderData).toHaveBeenCalledWith(false);
- });
-
- it("will be undefined if getPlaceholderData returns undefined", () => {
- const getPlaceholderData = jest.fn<
- (
- enabled?: boolean | undefined,
- ) => PartialMessage | undefined
- >(() => undefined);
- const { result } = renderHook(
- () =>
- useQuery(
- genSay.createUseQueryOptions(disableQuery, {
- getPlaceholderData,
- transport: mockEliza(),
- }),
- ),
- wrapper(),
- );
-
- expect(result.current.data?.sentence).toStrictEqual(undefined);
- expect(getPlaceholderData).toHaveBeenCalledWith(false);
- });
- });
-
- describe("queryFn", () => {
- const input: PartialMessage = { sentence: "ziltoid" };
-
- it("has the correct type", () => {
- expect.assertions(0);
- const result = genSay.createUseQueryOptions(undefined, {
- transport: mockEliza(),
- });
- type ExpectType_QueryFn = Expect<
- Equal<
- typeof result.queryFn,
- (
- context?:
- | QueryFunctionContext>
- | undefined,
- ) => Promise
- >
- >;
- });
-
- it("generates a query function", () => {
- const result = genSay.createUseQueryOptions(input, {
- transport: mockEliza(),
- });
-
- expect(result).toHaveProperty("queryFn", expect.any(Function));
- });
-
- it("throws when the `queryFn` is passed `disabledQuery` symbol as an input", async () => {
- const result = genSay.createUseQueryOptions(disableQuery, {
- transport: mockEliza(),
- });
-
- await expect(result.queryFn).rejects.toStrictEqual(
- new Error(
- "Invalid assertion: queryFn does not accept a disabled query",
- ),
- );
- });
-
- it("passes through callOptions", () => {
- const transport = mockEliza();
- const transportSpy = jest.spyOn(transport, "unary");
- renderHook(
- () =>
- useQuery(
- genSay.createUseQueryOptions(
- {},
- {
- transport,
- callOptions: mockCallOptions,
- },
- ),
- ),
- wrapper(),
- );
-
- expect(transportSpy).toHaveBeenCalledWith(
- expect.anything(), // service
- expect.anything(), // method
- mockCallOptions.signal, // signal
- mockCallOptions.timeoutMs, // timeoutMs
- mockCallOptions.headers, // headers
- expect.anything(), // input
- );
- });
- });
-
- describe("queryKey", () => {
- const input: PartialMessage = { sentence: "ziltoid" };
-
- it("has the correct type", () => {
- expect.assertions(0);
- const result = genSay.createUseQueryOptions(undefined, {
- transport: mockEliza(),
- });
-
- type ExpectType_QueryKey = Expect<
- Equal<
- typeof result.queryKey,
- [string, string, PartialMessage]
- >
- >;
- });
-
- it("generates a query key", () => {
- const result = genSay.createUseQueryOptions(input, {
- transport: mockEliza(),
- });
-
- expect(result).toHaveProperty("queryKey", [
- ElizaService.typeName,
- ElizaService.methods.say.name,
- { sentence: "ziltoid" },
- ]);
- });
- });
- });
-
- describe("getPartialQueryKey", () => {
- it("has the return type and value", () => {
- const key = genSay.getPartialQueryKey();
-
- type ExpectType_GetPartialQueryKey = Expect<
- Equal
- >;
-
- expect(key).toStrictEqual([
- ElizaService.typeName,
- ElizaService.methods.say.name,
- ]);
- });
- });
-});
diff --git a/packages/connect-query/src/create-unary-functions.ts b/packages/connect-query/src/create-unary-functions.ts
deleted file mode 100644
index e94c2e40..00000000
--- a/packages/connect-query/src/create-unary-functions.ts
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type {
- Message,
- MethodInfoUnary,
- PartialMessage,
- ServiceType,
-} from "@bufbuild/protobuf";
-import type { CallOptions, ConnectError, Transport } from "@connectrpc/connect";
-import type {
- GetNextPageParamFunction,
- QueryFunction,
- QueryFunctionContext,
-} from "@tanstack/react-query";
-
-import type {
- ConnectPartialQueryKey,
- ConnectQueryKey,
-} from "./connect-query-key.js";
-import { makeConnectQueryKeyGetter } from "./connect-query-key.js";
-import type { DisableQuery } from "./utils.js";
-import {
- assert,
- disableQuery,
- isUnaryMethod,
- protobufSafeUpdater,
- unreachableCase,
-} from "./utils.js";
-
-type RequireExactlyOne = {
- [K in Keys]-?: Partial, undefined>> &
- Required>;
-}[Keys] &
- Pick>;
-
-interface BaseInfiniteQueryOptions<
- I extends Message,
- O extends Message,
- ParamKey extends keyof PartialMessage,
-> {
- getNextPageParam: GetNextPageParamFunction[ParamKey], O>;
- /**
- * The option allows you to remove fields or otherwise customize the input used to generate the query key.
- * By default, we will remove the pageParamKey from the input. If this is provided, we will use this result instead.
- */
- sanitizeInputKey?: (input: PartialMessage) => unknown;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
-}
-
-/**
- * The set of data and hooks that a unary method supports.
- */
-export interface UnaryFunctions, O extends Message> {
- /**
- * Use this to create a data object that can be used as `placeholderData` or `initialData`.
- */
- createData: (data: PartialMessage) => O;
-
- /**
- * createUseQueryOptions is intended to be used with `useQuery`, but is not a hook. Since hooks cannot be called conditionally (or in loops), it can sometimes be helpful to use `createUseQueryOptions` to prepare an input to TanStack's `useQuery` API.
- *
- * The caveat being that if you go the route of using `createUseQueryOptions` you must provide transport. You can get transport from the `useTransport` export, or make sure these functions were generated with createQueryService() with a transport provided.
- */
- createUseQueryOptions: (
- input?: DisableQuery | PartialMessage | undefined,
- options?: {
- getPlaceholderData?: (enabled: boolean) => PartialMessage | undefined;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- },
- ) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: (context?: QueryFunctionContext>) => Promise;
- placeholderData?: () => O | undefined;
- /**
- * We never actually return a throwOnError, we just use this to allow
- * @tanstack/query to infer the error types we CAN throw.
- */
- throwOnError?: (error: ConnectError) => boolean;
- };
-
- /**
- * This helper is useful for getting query keys matching a wider set of queries associated to this Connect `Service`, per TanStack Query's partial matching mechanism.
- */
- getPartialQueryKey: () => ConnectPartialQueryKey;
-
- /**
- * This helper is useful to manually compute the `queryKey` sent to TanStack Query. Otherwise, this has no side effects.
- */
- getQueryKey: (input?: DisableQuery | PartialMessage) => ConnectQueryKey;
-
- /**
- * This is the metadata associated with this method.
- */
- methodInfo: MethodInfoUnary;
-
- /**
- *
- * This helper is intended to be used with `QueryClient`s `setQueryData` function.
- */
- setQueryData: (
- updater: PartialMessage | ((prev?: O) => PartialMessage),
- input?: PartialMessage,
- ) => [ConnectQueryKey, (prev?: O) => O | undefined];
-
- /**
- * This helper is intended to be used with `QueryClient`s `setQueriesData` function.
- */
- setQueriesData: (
- updater: PartialMessage | ((prev?: O) => PartialMessage),
- ) => [{ queryKey: ConnectPartialQueryKey }, (prev?: O) => O | undefined];
-
- /**
- * This helper is intended to be used with `QueryClient`s `useInfiniteQuery` function.
- */
- createUseInfiniteQueryOptions: <
- ParamKey extends keyof PartialMessage,
- Input extends PartialMessage &
- Required, ParamKey>>,
- >(
- input: DisableQuery | Input,
- options: BaseInfiniteQueryOptions &
- RequireExactlyOne<{
- applyPageParam: (options: {
- pageParam: PartialMessage[ParamKey] | undefined;
- input: PartialMessage;
- }) => PartialMessage;
- pageParamKey: ParamKey;
- }>,
- ) => {
- enabled: boolean;
- queryKey: ConnectQueryKey;
- queryFn: QueryFunction, PartialMessage[ParamKey]>;
- getNextPageParam: GetNextPageParamFunction[ParamKey], O>;
- initialPageParam: Input[ParamKey] | undefined;
- /**
- * We never actually return a throwOnError, we just use this to allow
- * @tanstack/query to infer the error types we CAN throw.
- */
- throwOnError?: (error: ConnectError) => boolean;
- };
-
- /**
- * This function is intended to be used with TanStack Query's `useMutation` API.
- */
- createUseMutationOptions: (options?: {
- onError?: (error: ConnectError) => void;
- transport?: Transport | undefined;
- callOptions?: CallOptions | undefined;
- }) => {
- mutationFn: (
- input: PartialMessage,
- context?: QueryFunctionContext>,
- ) => Promise;
- onError?: (error: ConnectError) => void;
- };
-}
-
-/**
- * A helper function that will configure the set of hooks a Unary method supports.
- */
-export const createUnaryFunctions = <
- I extends Message,
- O extends Message,
->({
- methodInfo,
- typeName,
- transport: topLevelCustomTransport,
-}: {
- methodInfo: MethodInfoUnary;
- typeName: ServiceType["typeName"];
- transport?: Transport | undefined;
-}): UnaryFunctions => {
- if (!isUnaryMethod(methodInfo)) {
- throw unreachableCase(
- methodInfo,
- `createUnaryFunctions was passed a non unary method, ${
- (methodInfo as { name: string }).name
- }`,
- );
- }
-
- const getQueryKey = makeConnectQueryKeyGetter(typeName, methodInfo.name);
-
- const createUseQueryOptions: UnaryFunctions["createUseQueryOptions"] = (
- input,
- // istanbul ignore next
- { callOptions, getPlaceholderData, transport } = {},
- ) => {
- const enabled = input !== disableQuery;
-
- assert(
- transport !== undefined,
- "createUseQueryOptions requires you to provide a Transport.",
- );
-
- return {
- enabled,
-
- ...(getPlaceholderData
- ? {
- placeholderData: () => {
- const placeholderData = getPlaceholderData(enabled);
- if (placeholderData === undefined) {
- return undefined;
- }
- return new methodInfo.O(placeholderData);
- },
- }
- : {}),
-
- queryFn: async (context) => {
- assert(enabled, "queryFn does not accept a disabled query");
- const result = await transport.unary(
- { typeName, methods: {} },
- methodInfo,
- (callOptions ?? context)?.signal,
- callOptions?.timeoutMs,
- callOptions?.headers,
- input ?? {},
- );
- return result.message;
- },
-
- queryKey: getQueryKey(input),
- };
- };
-
- return {
- createData: (input) => new methodInfo.O(input),
-
- createUseQueryOptions,
-
- getPartialQueryKey: () => [typeName, methodInfo.name],
-
- getQueryKey,
-
- methodInfo,
-
- setQueriesData: (updater) => [
- {
- queryKey: [typeName, methodInfo.name],
- },
- protobufSafeUpdater(updater, methodInfo.O),
- ],
-
- setQueryData: (updater, input) => [
- getQueryKey(input),
- protobufSafeUpdater(updater, methodInfo.O),
- ],
-
- createUseInfiniteQueryOptions: (
- input,
- {
- transport: optionsTransport,
- getNextPageParam,
- callOptions,
- sanitizeInputKey,
- ...otherOptions
- },
- ) => {
- const transport = optionsTransport ?? topLevelCustomTransport;
-
- assert(
- transport !== undefined,
- "createUseInfiniteQueryOptions requires you to provide a Transport.",
- );
-
- const enabled = input !== disableQuery;
- let sanitizedInput: PartialMessage | typeof disableQuery = input;
-
- if (enabled) {
- sanitizedInput =
- "pageParamKey" in otherOptions &&
- otherOptions.pageParamKey !== undefined
- ? {
- ...input,
- [otherOptions.pageParamKey]: undefined,
- }
- : sanitizeInputKey?.(input) ?? input;
- }
-
- return {
- enabled,
-
- getNextPageParam,
-
- initialPageParam: enabled
- ? "pageParamKey" in otherOptions &&
- otherOptions.pageParamKey !== undefined
- ? input[otherOptions.pageParamKey]
- : undefined
- : undefined,
-
- queryFn: async (context) => {
- assert(
- input !== disableQuery,
- "queryFn does not accept a disabled query",
- );
-
- assert("pageParam" in context, "pageParam must be part of context");
-
- const inputCombinedWithPageParam =
- "applyPageParam" in otherOptions &&
- otherOptions.applyPageParam !== undefined
- ? otherOptions.applyPageParam({
- pageParam: context.pageParam,
- input,
- })
- : {
- ...input,
- [otherOptions.pageParamKey]: context.pageParam,
- };
-
- const result = await transport.unary(
- { typeName, methods: {} },
- methodInfo,
- (callOptions ?? context).signal,
- callOptions?.timeoutMs,
- callOptions?.headers,
- inputCombinedWithPageParam,
- );
- return result.message;
- },
-
- queryKey: getQueryKey(sanitizedInput),
- };
- },
-
- createUseMutationOptions: ({
- transport: optionsTransport,
- callOptions,
- onError,
- } = {}) => {
- const transport = optionsTransport ?? topLevelCustomTransport;
-
- assert(
- transport !== undefined,
- "createUseMutationOptions requires you to provide a Transport.",
- );
-
- return {
- mutationFn: async (input, context) => {
- const result = await transport.unary(
- { typeName, methods: {} },
- methodInfo,
- (callOptions ?? context)?.signal,
- callOptions?.timeoutMs,
- callOptions?.headers,
- input,
- );
- return result.message;
- },
- ...(onError ? { onError } : {}),
- };
- },
- };
-};
diff --git a/packages/connect-query/src/create-unary-hooks.ts b/packages/connect-query/src/create-unary-hooks.ts
deleted file mode 100644
index 01318564..00000000
--- a/packages/connect-query/src/create-unary-hooks.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2021-2023 The Connect Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import type { Message } from "@bufbuild/protobuf";
-
-import type { UnaryFunctions } from "./create-unary-functions.js";
-import { useTransport } from "./use-transport.js";
-
-/**
- * All the additional hooks that are unique to React.
- */
-export interface UnaryHooks<
- I extends Message,
- O extends Message,
- M extends UnaryFunctions,
-> {
- /** The hook version, including transport, of createUseQueryOptions. */
- useQuery: M["createUseQueryOptions"];
- /** The hook version, including transport, of createUseMutationOptions. */
- useMutation: M["createUseMutationOptions"];
- /** The hook version, including transport, of createUseInfiniteQueryOptions. */
- useInfiniteQuery: M["createUseInfiniteQueryOptions"];
-}
-
-/**
- * Creates the hooks for a given set of unary methods.
- */
-export function createUnaryHooks, O extends Message>(
- unaryMethods: UnaryFunctions,
-): UnaryHooks> {
- return {
- useQuery: (input, options) => {
- const transport = useTransport();
- return unaryMethods.createUseQueryOptions(input, {
- transport,
- ...options,
- });
- },
- useInfiniteQuery: (input, options) => {
- const transport = useTransport();
- return unaryMethods.createUseInfiniteQueryOptions(input, {
- transport,
- ...options,
- });
- },
- useMutation: (options) => {
- const transport = useTransport();
- return unaryMethods.createUseMutationOptions({
- transport,
- ...options,
- });
- },
- };
-}
diff --git a/packages/connect-query/src/create-use-infinite-query-options.ts b/packages/connect-query/src/create-use-infinite-query-options.ts
new file mode 100644
index 00000000..cad5622b
--- /dev/null
+++ b/packages/connect-query/src/create-use-infinite-query-options.ts
@@ -0,0 +1,180 @@
+// Copyright 2021-2023 The Connect Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import type { Message, PartialMessage } from "@bufbuild/protobuf";
+import type { CallOptions, ConnectError, Transport } from "@connectrpc/connect";
+import type {
+ GetNextPageParamFunction,
+ InfiniteData,
+ QueryFunction,
+ UseInfiniteQueryOptions,
+ UseSuspenseInfiniteQueryOptions,
+} from "@tanstack/react-query";
+
+import { callUnaryMethod } from "./call-unary-method";
+import {
+ type ConnectQueryKey,
+ createConnectQueryKey,
+} from "./connect-query-key";
+import type { MethodUnaryDescriptor } from "./method-unary-descriptor";
+import { assert, type DisableQuery, disableQuery } from "./utils";
+
+/**
+ * Options specific to connect-query
+ */
+export interface ConnectInfiniteQueryOptions<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage,
+> {
+ /** Defines which part of the input should be considered the page param */
+ pageParamKey: ParamKey;
+ /** Transport can be overridden here.*/
+ transport: Transport;
+ /** Additional call options */
+ callOptions?: Omit | undefined;
+ /** Determines the next page. */
+ getNextPageParam: GetNextPageParamFunction[ParamKey], O>;
+}
+
+/**
+ * Options for useInfiniteQuery
+ */
+export type CreateInfiniteQueryOptions<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage,
+> = ConnectInfiniteQueryOptions &
+ Omit<
+ UseInfiniteQueryOptions<
+ O,
+ ConnectError,
+ InfiniteData,
+ O,
+ ConnectQueryKey,
+ PartialMessage[ParamKey]
+ >,
+ "getNextPageParam" | "initialPageParam" | "queryFn" | "queryKey"
+ >;
+
+/**
+ * Options for useSuspenseInfiniteQuery
+ */
+export type CreateSuspenseInfiniteQueryOptions<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage,
+> = ConnectInfiniteQueryOptions &
+ Omit<
+ UseSuspenseInfiniteQueryOptions<
+ O,
+ ConnectError,
+ InfiniteData,
+ O,
+ ConnectQueryKey,
+ PartialMessage[ParamKey]
+ >,
+ "getNextPageParam" | "initialPageParam" | "queryFn" | "queryKey"
+ >;
+
+function createUnaryInfiniteQueryFn<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage,
+>(
+ methodType: MethodUnaryDescriptor,
+ input: DisableQuery | PartialMessage,
+ {
+ callOptions,
+ transport,
+ pageParamKey,
+ }: {
+ transport: Transport;
+ callOptions?: CallOptions | undefined;
+ pageParamKey: ParamKey;
+ },
+): QueryFunction, PartialMessage[ParamKey]> {
+ return async (context) => {
+ assert(input !== disableQuery, "Disabled query cannot be fetched");
+ assert("pageParam" in context, "pageParam must be part of context");
+
+ const inputCombinedWithPageParam = {
+ ...input,
+ [pageParamKey]: context.pageParam,
+ };
+ return callUnaryMethod(methodType, inputCombinedWithPageParam, {
+ callOptions: {
+ ...callOptions,
+ signal: callOptions?.signal ?? context.signal,
+ },
+ transport,
+ });
+ };
+}
+
+/**
+ * Query the method provided. Maps to useInfiniteQuery on tanstack/react-query
+ *
+ * @param methodSig
+ * @returns
+ */
+export function createUseInfiniteQueryOptions<
+ I extends Message,
+ O extends Message,
+ ParamKey extends keyof PartialMessage