diff --git a/waspc/data/Generator/templates/sdk/wasp/auth/useAuth.ts b/waspc/data/Generator/templates/sdk/wasp/auth/useAuth.ts index 04b6200290..edc251530a 100644 --- a/waspc/data/Generator/templates/sdk/wasp/auth/useAuth.ts +++ b/waspc/data/Generator/templates/sdk/wasp/auth/useAuth.ts @@ -1,23 +1,24 @@ {{={= =}=}} import { deserialize as superjsonDeserialize } from 'superjson' -import { useQuery, addMetadataToQuery } from 'wasp/client/operations' +import { useQuery, buildAndRegisterQuery } from 'wasp/client/operations' +import type { QueryFunction, Query } from 'wasp/client/operations/rpc' import { api, handleApiError } from 'wasp/client/api' import { HttpMethod } from 'wasp/client' import type { AuthUser } from '../server/auth/user.js' import { UseQueryResult } from '@tanstack/react-query' // PUBLIC API -export const getMe: () => Promise = createUserGetter() +export const getMe: Query = createUserGetter() // PUBLIC API -export default function useAuth(queryFnArgs?: unknown, config?: any): UseQueryResult { - return useQuery(getMe, queryFnArgs, config) -} +export default function useAuth(): UseQueryResult { + return useQuery(getMe) +} -function createUserGetter() { +function createUserGetter(): Query { const getMeRelativePath = 'auth/me' const getMeRoute = { method: HttpMethod.Get, path: `/${getMeRelativePath}` } - async function getMe(): Promise { + const getMe: QueryFunction = async () => { try { const response = await api.get(getMeRoute.path) return superjsonDeserialize(response.data) @@ -30,11 +31,9 @@ function createUserGetter() { } } - addMetadataToQuery(getMe, { - relativeQueryPath: getMeRelativePath, + return buildAndRegisterQuery(getMe, { + queryCacheKey: [getMeRelativePath], queryRoute: getMeRoute, entitiesUsed: {=& entitiesGetMeDependsOn =}, }) - - return getMe } diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/actions/core.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/core.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/hooks.ts similarity index 88% rename from waspc/data/Generator/templates/sdk/wasp/client/operations/core.ts rename to waspc/data/Generator/templates/sdk/wasp/client/operations/hooks.ts index 282c4698a7..fa913a44c1 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/operations/core.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/hooks.ts @@ -7,91 +7,31 @@ import { useQuery as rqUseQuery, UseQueryResult, } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - // PUBLIC API export function useQuery( - queryFn: Query, + query: Query, queryFnArgs?: Input, options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') } - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), ...options, - }); + }) } -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - // PUBLIC API /** * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). @@ -133,6 +73,48 @@ export function useAction( return (args) => mutation.mutateAsync(args); } +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + /** * An internal (undocumented, private, desugared) way of defining optimistic updates. */ diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/index.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/operations/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/core.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/index.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/index.ts index ba14725469..8df916377d 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/queries/index.ts @@ -12,5 +12,5 @@ export const {= operationName =}: QueryFor<{= operationTypeName =}> = createQuer ) {=/ queries =} -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/data/Generator/templates/sdk/wasp/client/operations/rpc.ts b/waspc/data/Generator/templates/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/data/Generator/templates/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/data/Generator/templates/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/data/Generator/templates/sdk/wasp/package.json b/waspc/data/Generator/templates/sdk/wasp/package.json index 2d62c7db0e..96a84c044e 100644 --- a/waspc/data/Generator/templates/sdk/wasp/package.json +++ b/waspc/data/Generator/templates/sdk/wasp/package.json @@ -110,7 +110,7 @@ {=! todo(filip): Fixes below are for type errors in 0.13.1, remove ASAP =} {=! Used by our code (SDK for full-stack type safety), uncodumented (but accessible) for users. =} - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./server/crud/Tasks": "./dist/server/crud/Tasks.js", "./server/operations/actions": "./dist/server/operations/actions/index.js", "./server/operations/queries": "./dist/server/operations/queries/index.js", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest index 218aa3083a..c08ca59fe3 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/files.manifest @@ -10,7 +10,7 @@ waspBuild/.wasp/build/sdk/wasp/client/config.ts waspBuild/.wasp/build/sdk/wasp/client/index.ts waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts waspBuild/.wasp/build/sdk/wasp/client/operations/actions/index.ts -waspBuild/.wasp/build/sdk/wasp/client/operations/core.ts +waspBuild/.wasp/build/sdk/wasp/client/operations/hooks.ts waspBuild/.wasp/build/sdk/wasp/client/operations/index.ts waspBuild/.wasp/build/sdk/wasp/client/operations/internal/index.ts waspBuild/.wasp/build/sdk/wasp/client/operations/internal/resources.js @@ -18,6 +18,7 @@ waspBuild/.wasp/build/sdk/wasp/client/operations/internal/updateHandlersMap.js waspBuild/.wasp/build/sdk/wasp/client/operations/queries/core.ts waspBuild/.wasp/build/sdk/wasp/client/operations/queries/index.ts waspBuild/.wasp/build/sdk/wasp/client/operations/queryClient.ts +waspBuild/.wasp/build/sdk/wasp/client/operations/rpc.ts waspBuild/.wasp/build/sdk/wasp/client/router/Link.tsx waspBuild/.wasp/build/sdk/wasp/client/router/index.ts waspBuild/.wasp/build/sdk/wasp/client/router/linkHelpers.ts @@ -44,9 +45,9 @@ waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/core.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/index.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/index.js waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/index.js.map -waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.d.ts -waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js -waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js.map +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js.map @@ -68,6 +69,9 @@ waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queryClient.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queryClient.js waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queryClient.js.map +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.d.ts +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js +waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js.map waspBuild/.wasp/build/sdk/wasp/dist/client/router/Link.d.ts waspBuild/.wasp/build/sdk/wasp/dist/client/router/Link.jsx waspBuild/.wasp/build/sdk/wasp/dist/client/router/Link.jsx.map @@ -228,7 +232,7 @@ waspBuild/.wasp/out/sdk/wasp/client/config.ts waspBuild/.wasp/out/sdk/wasp/client/index.ts waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspBuild/.wasp/out/sdk/wasp/client/operations/actions/index.ts -waspBuild/.wasp/out/sdk/wasp/client/operations/core.ts +waspBuild/.wasp/out/sdk/wasp/client/operations/hooks.ts waspBuild/.wasp/out/sdk/wasp/client/operations/index.ts waspBuild/.wasp/out/sdk/wasp/client/operations/internal/index.ts waspBuild/.wasp/out/sdk/wasp/client/operations/internal/resources.js @@ -236,6 +240,7 @@ waspBuild/.wasp/out/sdk/wasp/client/operations/internal/updateHandlersMap.js waspBuild/.wasp/out/sdk/wasp/client/operations/queries/core.ts waspBuild/.wasp/out/sdk/wasp/client/operations/queries/index.ts waspBuild/.wasp/out/sdk/wasp/client/operations/queryClient.ts +waspBuild/.wasp/out/sdk/wasp/client/operations/rpc.ts waspBuild/.wasp/out/sdk/wasp/client/router/Link.tsx waspBuild/.wasp/out/sdk/wasp/client/router/index.ts waspBuild/.wasp/out/sdk/wasp/client/router/linkHelpers.ts @@ -262,9 +267,9 @@ waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/core.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/index.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js.map -waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts -waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js -waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -286,6 +291,9 @@ waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queryClient.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js.map +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js +waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map waspBuild/.wasp/out/sdk/wasp/dist/client/router/Link.d.ts waspBuild/.wasp/out/sdk/wasp/dist/client/router/Link.jsx waspBuild/.wasp/out/sdk/wasp/dist/client/router/Link.jsx.map diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums index 2ce8ecff03..a399ac6756 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/.waspchecksums @@ -32,7 +32,7 @@ "file", "../out/sdk/wasp/client/operations/actions/core.ts" ], - "016623c0ebdc1b88d746fa1345015b3cb9653429abdc13a9147a952bff83a927" + "d19dba04947e4015af69d90dd160ec5f4ed020bfa905bc37bcd5d980517097ae" ], [ [ @@ -44,16 +44,16 @@ [ [ "file", - "../out/sdk/wasp/client/operations/core.ts" + "../out/sdk/wasp/client/operations/hooks.ts" ], - "bad860771b16a0d4830fab22de8b85e63c840d47dc3728728a23b8ada91061ef" + "eb4362162aad4b605781e2e33facef83935a7df4101cc0a77097ceaff3a0b360" ], [ [ "file", "../out/sdk/wasp/client/operations/index.ts" ], - "4a66c90319dd7ef0d3a8e6c1f571037c7dfd9778b9def6e951813dbc4b4dcef3" + "edcdc3798590e62b778115b3818df8cc69fa5a8bb3a7fe9fa6d6d5ea706d14c4" ], [ [ @@ -81,14 +81,14 @@ "file", "../out/sdk/wasp/client/operations/queries/core.ts" ], - "5f30328d93582f9c8444720e99f45c19c8647f052b7fbcf5f71b578b9241ac96" + "cce982751b463494c048efd683dddb8d4e617d2f354722a79961199990c7201a" ], [ [ "file", "../out/sdk/wasp/client/operations/queries/index.ts" ], - "882410504b909cc421d50a27c39952f664ba3457438ce746a95d9cab3431944c" + "c92c64425986a38f835211c2693380a8b13904cb78bbf6f6ae880e56ade51686" ], [ [ @@ -97,6 +97,13 @@ ], "5c1d87ac10513788bcde7ebc7c10601b9ad0854cddff355e8fb7e2d4685ecdef" ], + [ + [ + "file", + "../out/sdk/wasp/client/operations/rpc.ts" + ], + "5ab471422e7916c33a0931ce8d499d7d40191802ce2c6f3343b45c623a963566" + ], [ [ "file", @@ -137,7 +144,7 @@ "file", "../out/sdk/wasp/client/test/vitest/helpers.tsx" ], - "b2362e8f80134137fda2f8bb43ef7c0d7ae8aadf8a7adfa472d42d6699e9d918" + "b44ff591a2eebfff4de9fa9e9e1b89f1f22f523f03ab0febc19ff3999721b39a" ], [ [ @@ -193,7 +200,7 @@ "file", "../out/sdk/wasp/package.json" ], - "3422dd87e9e5f4aeb3922ee152ad691399d91ad71172f2b76f6235eb28955fa2" + "8df2ebcc130b484aa956ddf0109b23c83fe2221cb41ea35ed5e99817013f36a9" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/hooks.ts similarity index 88% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/core.ts rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/hooks.ts index 282c4698a7..fa913a44c1 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/hooks.ts @@ -7,91 +7,31 @@ import { useQuery as rqUseQuery, UseQueryResult, } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - // PUBLIC API export function useQuery( - queryFn: Query, + query: Query, queryFnArgs?: Input, options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') } - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), ...options, - }); + }) } -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - // PUBLIC API /** * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). @@ -133,6 +73,48 @@ export function useAction( return (args) => mutation.mutateAsync(args); } +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + /** * An internal (undocumented, private, desugared) way of defining optimistic updates. */ diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/index.ts index eeb2cf5a11..af63c67c63 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/queries/index.ts @@ -1,4 +1,4 @@ import { type QueryFor, createQuery } from './core' -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/rpc.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.d.ts similarity index 65% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.d.ts index a23b24ebd6..36a5e0b65b 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.d.ts @@ -1,18 +1,15 @@ import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; /** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; /** * A documented (public) way to define optimistic updates. */ @@ -20,25 +17,25 @@ export type OptimisticUpdateDefinition = { getQuerySpecifier: GetQuerySpecifier; updateQuery: UpdateQuery; }; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; /** * A function that takes an item and returns a Wasp Query specifier. */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; /** * A function that takes an item and the previous state of the cache, and returns * the desired (new) state of the cache. */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; /** * A public query specifier used for addressing Wasp queries. See our docs for details: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js similarity index 93% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js index 2c94cb01fd..95ab379515 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js @@ -1,17 +1,15 @@ import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; // PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); } // PUBLIC API /** @@ -168,4 +166,4 @@ function getRqQueryKeyFromSpecifier(querySpecifier) { const [queryFn, ...otherKeys] = querySpecifier; return [...queryFn.queryCacheKey, ...otherKeys]; } -//# sourceMappingURL=core.js.map \ No newline at end of file +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.d.ts index 575c502be1..2d9dbafe81 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1 +1 @@ -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js index 1c28e8d0d3..56d2e15238 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js @@ -1,3 +1,3 @@ -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js.map index 5c83611fa3..44416efcfa 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json index e3a3bb5440..73e355a456 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/package.json @@ -41,7 +41,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/hooks.ts similarity index 88% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/core.ts rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/hooks.ts index 282c4698a7..fa913a44c1 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/hooks.ts @@ -7,91 +7,31 @@ import { useQuery as rqUseQuery, UseQueryResult, } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - // PUBLIC API export function useQuery( - queryFn: Query, + query: Query, queryFnArgs?: Input, options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') } - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), ...options, - }); + }) } -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - // PUBLIC API /** * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). @@ -133,6 +73,48 @@ export function useAction( return (args) => mutation.mutateAsync(args); } +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + /** * An internal (undocumented, private, desugared) way of defining optimistic updates. */ diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/index.ts index eeb2cf5a11..af63c67c63 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/queries/index.ts @@ -1,4 +1,4 @@ import { type QueryFor, createQuery } from './core' -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/rpc.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts similarity index 65% rename from waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts index a23b24ebd6..36a5e0b65b 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts @@ -1,18 +1,15 @@ import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; /** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; /** * A documented (public) way to define optimistic updates. */ @@ -20,25 +17,25 @@ export type OptimisticUpdateDefinition = { getQuerySpecifier: GetQuerySpecifier; updateQuery: UpdateQuery; }; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; /** * A function that takes an item and returns a Wasp Query specifier. */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; /** * A function that takes an item and the previous state of the cache, and returns * the desired (new) state of the cache. */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; /** * A public query specifier used for addressing Wasp queries. See our docs for details: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js similarity index 93% rename from waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js rename to waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js index 2c94cb01fd..95ab379515 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js @@ -1,17 +1,15 @@ import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; // PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); } // PUBLIC API /** @@ -168,4 +166,4 @@ function getRqQueryKeyFromSpecifier(querySpecifier) { const [queryFn, ...otherKeys] = querySpecifier; return [...queryFn.queryCacheKey, ...otherKeys]; } -//# sourceMappingURL=core.js.map \ No newline at end of file +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts index 575c502be1..2d9dbafe81 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1 +1 @@ -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js index 1c28e8d0d3..56d2e15238 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js @@ -1,3 +1,3 @@ -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map index 5c83611fa3..44416efcfa 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json index e3a3bb5440..73e355a456 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/out/sdk/wasp/package.json @@ -41,7 +41,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest index 598f844e80..63cd761005 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/files.manifest @@ -9,7 +9,7 @@ waspCompile/.wasp/out/sdk/wasp/client/config.ts waspCompile/.wasp/out/sdk/wasp/client/index.ts waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspCompile/.wasp/out/sdk/wasp/client/operations/actions/index.ts -waspCompile/.wasp/out/sdk/wasp/client/operations/core.ts +waspCompile/.wasp/out/sdk/wasp/client/operations/hooks.ts waspCompile/.wasp/out/sdk/wasp/client/operations/index.ts waspCompile/.wasp/out/sdk/wasp/client/operations/internal/index.ts waspCompile/.wasp/out/sdk/wasp/client/operations/internal/resources.js @@ -17,6 +17,7 @@ waspCompile/.wasp/out/sdk/wasp/client/operations/internal/updateHandlersMap.js waspCompile/.wasp/out/sdk/wasp/client/operations/queries/core.ts waspCompile/.wasp/out/sdk/wasp/client/operations/queries/index.ts waspCompile/.wasp/out/sdk/wasp/client/operations/queryClient.ts +waspCompile/.wasp/out/sdk/wasp/client/operations/rpc.ts waspCompile/.wasp/out/sdk/wasp/client/router/Link.tsx waspCompile/.wasp/out/sdk/wasp/client/router/index.ts waspCompile/.wasp/out/sdk/wasp/client/router/linkHelpers.ts @@ -43,9 +44,9 @@ waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/core.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/index.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js.map -waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts -waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js -waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -67,6 +68,9 @@ waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queryClient.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js.map +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js +waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map waspCompile/.wasp/out/sdk/wasp/dist/client/router/Link.d.ts waspCompile/.wasp/out/sdk/wasp/dist/client/router/Link.jsx waspCompile/.wasp/out/sdk/wasp/dist/client/router/Link.jsx.map diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums index c116f58449..34d741b056 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/.waspchecksums @@ -32,7 +32,7 @@ "file", "../out/sdk/wasp/client/operations/actions/core.ts" ], - "016623c0ebdc1b88d746fa1345015b3cb9653429abdc13a9147a952bff83a927" + "d19dba04947e4015af69d90dd160ec5f4ed020bfa905bc37bcd5d980517097ae" ], [ [ @@ -44,16 +44,16 @@ [ [ "file", - "../out/sdk/wasp/client/operations/core.ts" + "../out/sdk/wasp/client/operations/hooks.ts" ], - "bad860771b16a0d4830fab22de8b85e63c840d47dc3728728a23b8ada91061ef" + "eb4362162aad4b605781e2e33facef83935a7df4101cc0a77097ceaff3a0b360" ], [ [ "file", "../out/sdk/wasp/client/operations/index.ts" ], - "4a66c90319dd7ef0d3a8e6c1f571037c7dfd9778b9def6e951813dbc4b4dcef3" + "edcdc3798590e62b778115b3818df8cc69fa5a8bb3a7fe9fa6d6d5ea706d14c4" ], [ [ @@ -81,14 +81,14 @@ "file", "../out/sdk/wasp/client/operations/queries/core.ts" ], - "5f30328d93582f9c8444720e99f45c19c8647f052b7fbcf5f71b578b9241ac96" + "cce982751b463494c048efd683dddb8d4e617d2f354722a79961199990c7201a" ], [ [ "file", "../out/sdk/wasp/client/operations/queries/index.ts" ], - "882410504b909cc421d50a27c39952f664ba3457438ce746a95d9cab3431944c" + "c92c64425986a38f835211c2693380a8b13904cb78bbf6f6ae880e56ade51686" ], [ [ @@ -97,6 +97,13 @@ ], "5c1d87ac10513788bcde7ebc7c10601b9ad0854cddff355e8fb7e2d4685ecdef" ], + [ + [ + "file", + "../out/sdk/wasp/client/operations/rpc.ts" + ], + "5ab471422e7916c33a0931ce8d499d7d40191802ce2c6f3343b45c623a963566" + ], [ [ "file", @@ -137,7 +144,7 @@ "file", "../out/sdk/wasp/client/test/vitest/helpers.tsx" ], - "b2362e8f80134137fda2f8bb43ef7c0d7ae8aadf8a7adfa472d42d6699e9d918" + "b44ff591a2eebfff4de9fa9e9e1b89f1f22f523f03ab0febc19ff3999721b39a" ], [ [ @@ -193,7 +200,7 @@ "file", "../out/sdk/wasp/package.json" ], - "3422dd87e9e5f4aeb3922ee152ad691399d91ad71172f2b76f6235eb28955fa2" + "8df2ebcc130b484aa956ddf0109b23c83fe2221cb41ea35ed5e99817013f36a9" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/hooks.ts similarity index 88% rename from waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/core.ts rename to waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/hooks.ts index 282c4698a7..fa913a44c1 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/core.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/hooks.ts @@ -7,91 +7,31 @@ import { useQuery as rqUseQuery, UseQueryResult, } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - // PUBLIC API export function useQuery( - queryFn: Query, + query: Query, queryFnArgs?: Input, options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') } - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), ...options, - }); + }) } -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - // PUBLIC API /** * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). @@ -133,6 +73,48 @@ export function useAction( return (args) => mutation.mutateAsync(args); } +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + /** * An internal (undocumented, private, desugared) way of defining optimistic updates. */ diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/index.ts index eeb2cf5a11..af63c67c63 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/queries/index.ts @@ -1,4 +1,4 @@ import { type QueryFor, createQuery } from './core' -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/rpc.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts similarity index 65% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.d.ts rename to waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts index a23b24ebd6..36a5e0b65b 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts @@ -1,18 +1,15 @@ import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; /** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; /** * A documented (public) way to define optimistic updates. */ @@ -20,25 +17,25 @@ export type OptimisticUpdateDefinition = { getQuerySpecifier: GetQuerySpecifier; updateQuery: UpdateQuery; }; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; /** * A function that takes an item and returns a Wasp Query specifier. */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; /** * A function that takes an item and the previous state of the cache, and returns * the desired (new) state of the cache. */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; /** * A public query specifier used for addressing Wasp queries. See our docs for details: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js similarity index 93% rename from waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js rename to waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js index 2c94cb01fd..95ab379515 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js @@ -1,17 +1,15 @@ import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; // PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); } // PUBLIC API /** @@ -168,4 +166,4 @@ function getRqQueryKeyFromSpecifier(querySpecifier) { const [queryFn, ...otherKeys] = querySpecifier; return [...queryFn.queryCacheKey, ...otherKeys]; } -//# sourceMappingURL=core.js.map \ No newline at end of file +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts index 575c502be1..2d9dbafe81 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1 +1 @@ -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js index 1c28e8d0d3..56d2e15238 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js @@ -1,3 +1,3 @@ -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map index 5c83611fa3..44416efcfa 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json index e3a3bb5440..73e355a456 100644 --- a/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspCompile-golden/waspCompile/.wasp/out/sdk/wasp/package.json @@ -41,7 +41,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest index 1f31292084..df00bf3b88 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/files.manifest @@ -39,7 +39,7 @@ waspComplexTest/.wasp/out/sdk/wasp/client/crud/tasks.ts waspComplexTest/.wasp/out/sdk/wasp/client/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/index.ts -waspComplexTest/.wasp/out/sdk/wasp/client/operations/core.ts +waspComplexTest/.wasp/out/sdk/wasp/client/operations/hooks.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/internal/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/internal/resources.js @@ -47,6 +47,7 @@ waspComplexTest/.wasp/out/sdk/wasp/client/operations/internal/updateHandlersMap. waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/core.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/operations/queryClient.ts +waspComplexTest/.wasp/out/sdk/wasp/client/operations/rpc.ts waspComplexTest/.wasp/out/sdk/wasp/client/router/Link.tsx waspComplexTest/.wasp/out/sdk/wasp/client/router/index.ts waspComplexTest/.wasp/out/sdk/wasp/client/router/linkHelpers.ts @@ -159,9 +160,9 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/core.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js.map -waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts -waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js -waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -183,6 +184,9 @@ waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queryClient.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js.map +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js +waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map waspComplexTest/.wasp/out/sdk/wasp/dist/client/router/Link.d.ts waspComplexTest/.wasp/out/sdk/wasp/dist/client/router/Link.jsx waspComplexTest/.wasp/out/sdk/wasp/dist/client/router/Link.jsx.map diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums index 6c8e64dac0..2322c62419 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/.waspchecksums @@ -151,7 +151,7 @@ "file", "../out/sdk/wasp/auth/useAuth.ts" ], - "614d24fc6d2b87d0913f832d7b0069190c96cbf7301c3bc541230044854bfef0" + "a465fececdb235d98c23ce25215c1942bc186c2131e3bb4595d45d9ef219fbd5" ], [ [ @@ -228,7 +228,7 @@ "file", "../out/sdk/wasp/client/operations/actions/core.ts" ], - "016623c0ebdc1b88d746fa1345015b3cb9653429abdc13a9147a952bff83a927" + "d19dba04947e4015af69d90dd160ec5f4ed020bfa905bc37bcd5d980517097ae" ], [ [ @@ -240,16 +240,16 @@ [ [ "file", - "../out/sdk/wasp/client/operations/core.ts" + "../out/sdk/wasp/client/operations/hooks.ts" ], - "bad860771b16a0d4830fab22de8b85e63c840d47dc3728728a23b8ada91061ef" + "eb4362162aad4b605781e2e33facef83935a7df4101cc0a77097ceaff3a0b360" ], [ [ "file", "../out/sdk/wasp/client/operations/index.ts" ], - "4a66c90319dd7ef0d3a8e6c1f571037c7dfd9778b9def6e951813dbc4b4dcef3" + "edcdc3798590e62b778115b3818df8cc69fa5a8bb3a7fe9fa6d6d5ea706d14c4" ], [ [ @@ -277,14 +277,14 @@ "file", "../out/sdk/wasp/client/operations/queries/core.ts" ], - "5f30328d93582f9c8444720e99f45c19c8647f052b7fbcf5f71b578b9241ac96" + "cce982751b463494c048efd683dddb8d4e617d2f354722a79961199990c7201a" ], [ [ "file", "../out/sdk/wasp/client/operations/queries/index.ts" ], - "0f9a37061deb021b9532f4601be54e244067dcf1c39b0d47db92f2d1e1c59c78" + "f086e3b7f43d3b4fdd7b8a4e32907d245b4cddf837c7f777b07fc1d3577c067c" ], [ [ @@ -293,6 +293,13 @@ ], "5c1d87ac10513788bcde7ebc7c10601b9ad0854cddff355e8fb7e2d4685ecdef" ], + [ + [ + "file", + "../out/sdk/wasp/client/operations/rpc.ts" + ], + "5ab471422e7916c33a0931ce8d499d7d40191802ce2c6f3343b45c623a963566" + ], [ [ "file", @@ -333,7 +340,7 @@ "file", "../out/sdk/wasp/client/test/vitest/helpers.tsx" ], - "b2362e8f80134137fda2f8bb43ef7c0d7ae8aadf8a7adfa472d42d6699e9d918" + "b44ff591a2eebfff4de9fa9e9e1b89f1f22f523f03ab0febc19ff3999721b39a" ], [ [ @@ -466,7 +473,7 @@ "file", "../out/sdk/wasp/package.json" ], - "0f0265981290d0585a53f8fb24e8e37f41e0e89c8379efce5334a51197548c93" + "75c4ac6e306ef6d0e017dc778ba2c5febe9fea168c3ecf03f80c5086c4e35054" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/auth/useAuth.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/auth/useAuth.ts index 03327c9802..5c83924355 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/auth/useAuth.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/auth/useAuth.ts @@ -1,22 +1,23 @@ import { deserialize as superjsonDeserialize } from 'superjson' -import { useQuery, addMetadataToQuery } from 'wasp/client/operations' +import { useQuery, buildAndRegisterQuery } from 'wasp/client/operations' +import type { QueryFunction, Query } from 'wasp/client/operations/rpc' import { api, handleApiError } from 'wasp/client/api' import { HttpMethod } from 'wasp/client' import type { AuthUser } from '../server/auth/user.js' import { UseQueryResult } from '@tanstack/react-query' // PUBLIC API -export const getMe: () => Promise = createUserGetter() +export const getMe: Query = createUserGetter() // PUBLIC API -export default function useAuth(queryFnArgs?: unknown, config?: any): UseQueryResult { - return useQuery(getMe, queryFnArgs, config) -} +export default function useAuth(): UseQueryResult { + return useQuery(getMe) +} -function createUserGetter() { +function createUserGetter(): Query { const getMeRelativePath = 'auth/me' const getMeRoute = { method: HttpMethod.Get, path: `/${getMeRelativePath}` } - async function getMe(): Promise { + const getMe: QueryFunction = async () => { try { const response = await api.get(getMeRoute.path) return superjsonDeserialize(response.data) @@ -29,11 +30,9 @@ function createUserGetter() { } } - addMetadataToQuery(getMe, { - relativeQueryPath: getMeRelativePath, + return buildAndRegisterQuery(getMe, { + queryCacheKey: [getMeRelativePath], queryRoute: getMeRoute, entitiesUsed: ['User'], }) - - return getMe } diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/core.ts deleted file mode 100644 index 282c4698a7..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/core.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { - QueryClient, - QueryKey, - useMutation, - UseMutationOptions, - useQueryClient, - useQuery as rqUseQuery, - UseQueryResult, -} from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - -// PUBLIC API -export function useQuery( - queryFn: Query, - queryFnArgs?: Input, - options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); - } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); - } - - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), - ...options, - }); -} - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - -// PUBLIC API -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export function useAction( - actionFn: Action, - actionOptions?: ActionOptions -): typeof actionFn { - const queryClient = useQueryClient(); - - let mutationFn = actionFn; - let options = {}; - if (actionOptions?.optimisticUpdates) { - const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( - translateToInternalDefinition - ); - mutationFn = makeOptimisticUpdateMutationFn( - actionFn, - optimisticUpdatesDefinitions - ); - options = makeRqOptimisticUpdateOptions( - queryClient, - optimisticUpdatesDefinitions - ); - } - - // NOTE: We decided to hide React Query's extra mutation features (e.g., - // isLoading, onSuccess and onError callbacks, synchronous mutate) and only - // expose a simple async function whose API matches the original Action. - // We did this to avoid cluttering the API with stuff we're not sure we need - // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to - // clearly separate our opinionated API from React Query's lower-level - // advanced API (which users can also use) - const mutation = useMutation(mutationFn, options); - return (args) => mutation.mutateAsync(args); -} - -/** - * An internal (undocumented, private, desugared) way of defining optimistic updates. - */ -type InternalOptimisticUpdateDefinition = { - getQueryKey: (item: ActionInput) => QueryKey; - updateQuery: UpdateQuery; -}; - -/** - * An UpdateQuery function "instantiated" with a specific item. It only takes - * the current state of the cache and returns the desired (new) state of the - * cache. - */ -type SpecificUpdateQuery = (oldData: CachedData) => CachedData; - -/** - * A specific, "instantiated" optimistic update definition which contains a - * fully-constructed query key and a specific update function. - */ -type SpecificOptimisticUpdateDefinition = { - queryKey: QueryKey; - updateQuery: SpecificUpdateQuery; -}; - -type InternalAction = Action & { - internal( - item: Input, - optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] - ): Promise; -}; - -/** - * Translates/Desugars a public optimistic update definition object into a - * definition object our system uses internally. - * - * @param publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns An internally-used optimistic update definition object. - */ -function translateToInternalDefinition( - publicOptimisticUpdateDefinition: OptimisticUpdateDefinition -): InternalOptimisticUpdateDefinition { - const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; - - const definitionErrors = []; - if (typeof getQuerySpecifier !== "function") { - definitionErrors.push("`getQuerySpecifier` is not a function."); - } - if (typeof updateQuery !== "function") { - definitionErrors.push("`updateQuery` is not a function."); - } - if (definitionErrors.length) { - throw new TypeError( - `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` - ); - } - - return { - getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), - updateQuery, - }; -} - -/** - * Creates a function that performs an action while telling it about the - * optimistic updates it caused. - * - * @param actionFn The Wasp Action. - * @param optimisticUpdateDefinitions The optimisitc updates the action causes. - * @returns An decorated action which performs optimistic updates. - */ -function makeOptimisticUpdateMutationFn( - actionFn: Action, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - Input, - CachedData - >[] -): typeof actionFn { - return function performActionWithOptimisticUpdates(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - return (actionFn as InternalAction).internal( - item, - specificOptimisticUpdateDefinitions - ); - }; -} - -/** - * Given a ReactQuery query client and our internal definition of optimistic - * updates, this function constructs an object describing those same optimistic - * updates in a format we can pass into React Query's useMutation hook. In other - * words, it translates our optimistic updates definition into React Query's - * optimistic updates definition. Check their docs for details: - * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates - * - * @param queryClient The QueryClient instance used by React Query. - * @param optimisticUpdateDefinitions A list containing internal optimistic - * updates definition objects (i.e., a list where each object carries the - * instructions for performing particular optimistic update). - * @returns An object containing 'onMutate' and 'onError' functions - * corresponding to the given optimistic update definitions (check the docs - * linked above for details). - */ -function makeRqOptimisticUpdateOptions( - queryClient: QueryClient, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >[] -): Pick { - async function onMutate(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - - // Cancel any outgoing refetches (so they don't overwrite our optimistic update). - // Theoretically, we can be a bit faster. Instead of awaiting the - // cancellation of all queries, we could cancel and update them in parallel. - // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. - await Promise.all( - specificOptimisticUpdateDefinitions.map(({ queryKey }) => - queryClient.cancelQueries(queryKey) - ) - ); - - // We're using a Map to correctly serialize query keys that contain objects. - const previousData = new Map(); - specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { - // Snapshot the currently cached value. - const previousDataForQuery: CachedData = - queryClient.getQueryData(queryKey); - - // Attempt to optimistically update the cache using the new value. - try { - queryClient.setQueryData(queryKey, updateQuery); - } catch (e) { - console.error( - "The `updateQuery` function threw an exception, skipping optimistic update:" - ); - console.error(e); - } - - // Remember the snapshotted value to restore in case of an error. - previousData.set(queryKey, previousDataForQuery); - }); - - return { previousData }; - } - - function onError(_err, _item, context) { - // All we do in case of an error is roll back all optimistic updates. We ensure - // not to do anything else because React Query rethrows the error. This allows - // the programmer to handle the error as they usually would (i.e., we want the - // error handling to work as it would if the programmer wasn't using optimistic - // updates). - context.previousData.forEach(async (data, queryKey) => { - await queryClient.cancelQueries(queryKey); - queryClient.setQueryData(queryKey, data); - }); - } - - return { - onMutate, - onError, - }; -} - -/** - * Constructs the definition for optimistically updating a specific item. It - * uses a closure over the updated item to construct an item-specific query key - * (e.g., useful when the query key depends on an ID). - * - * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic - * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the - * argument passed to the Action). - * @returns A specific optimistic update definition which corresponds to the - * provided definition and closes over the provided item. - */ -function getOptimisticUpdateDefinitionForSpecificItem( - optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >, - item: ActionInput -): SpecificOptimisticUpdateDefinition { - const { getQueryKey, updateQuery } = optimisticUpdateDefinition; - return { - queryKey: getQueryKey(item), - updateQuery: (old) => updateQuery(item, old), - }; -} - -/** - * Translates a Wasp query specifier to a query cache key used by React Query. - * - * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns A cache key React Query internally uses for addressing queries. - */ -function getRqQueryKeyFromSpecifier( - querySpecifier: QuerySpecifier -): QueryKey { - const [queryFn, ...otherKeys] = querySpecifier; - return [...(queryFn as any).queryCacheKey, ...otherKeys]; -} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/hooks.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/hooks.ts new file mode 100644 index 0000000000..fa913a44c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/hooks.ts @@ -0,0 +1,328 @@ +import { + QueryClient, + QueryKey, + useMutation, + UseMutationOptions, + useQueryClient, + useQuery as rqUseQuery, + UseQueryResult, +} from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; +export { configureQueryClient } from "./queryClient"; + +// PUBLIC API +export function useQuery( + query: Query, + queryFnArgs?: Input, + options?: any +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') + } + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') + } + + return rqUseQuery({ + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), + ...options, + }) +} + +// PUBLIC API +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export function useAction( + actionFn: Action, + actionOptions?: ActionOptions +): typeof actionFn { + const queryClient = useQueryClient(); + + let mutationFn = actionFn; + let options = {}; + if (actionOptions?.optimisticUpdates) { + const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( + translateToInternalDefinition + ); + mutationFn = makeOptimisticUpdateMutationFn( + actionFn, + optimisticUpdatesDefinitions + ); + options = makeRqOptimisticUpdateOptions( + queryClient, + optimisticUpdatesDefinitions + ); + } + + // NOTE: We decided to hide React Query's extra mutation features (e.g., + // isLoading, onSuccess and onError callbacks, synchronous mutate) and only + // expose a simple async function whose API matches the original Action. + // We did this to avoid cluttering the API with stuff we're not sure we need + // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to + // clearly separate our opinionated API from React Query's lower-level + // advanced API (which users can also use) + const mutation = useMutation(mutationFn, options); + return (args) => mutation.mutateAsync(args); +} + +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + +/** + * An internal (undocumented, private, desugared) way of defining optimistic updates. + */ +type InternalOptimisticUpdateDefinition = { + getQueryKey: (item: ActionInput) => QueryKey; + updateQuery: UpdateQuery; +}; + +/** + * An UpdateQuery function "instantiated" with a specific item. It only takes + * the current state of the cache and returns the desired (new) state of the + * cache. + */ +type SpecificUpdateQuery = (oldData: CachedData) => CachedData; + +/** + * A specific, "instantiated" optimistic update definition which contains a + * fully-constructed query key and a specific update function. + */ +type SpecificOptimisticUpdateDefinition = { + queryKey: QueryKey; + updateQuery: SpecificUpdateQuery; +}; + +type InternalAction = Action & { + internal( + item: Input, + optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] + ): Promise; +}; + +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition( + publicOptimisticUpdateDefinition: OptimisticUpdateDefinition +): InternalOptimisticUpdateDefinition { + const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; + + const definitionErrors = []; + if (typeof getQuerySpecifier !== "function") { + definitionErrors.push("`getQuerySpecifier` is not a function."); + } + if (typeof updateQuery !== "function") { + definitionErrors.push("`updateQuery` is not a function."); + } + if (definitionErrors.length) { + throw new TypeError( + `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` + ); + } + + return { + getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), + updateQuery, + }; +} + +/** + * Creates a function that performs an action while telling it about the + * optimistic updates it caused. + * + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. + */ +function makeOptimisticUpdateMutationFn( + actionFn: Action, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + Input, + CachedData + >[] +): typeof actionFn { + return function performActionWithOptimisticUpdates(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + return (actionFn as InternalAction).internal( + item, + specificOptimisticUpdateDefinitions + ); + }; +} + +/** + * Given a ReactQuery query client and our internal definition of optimistic + * updates, this function constructs an object describing those same optimistic + * updates in a format we can pass into React Query's useMutation hook. In other + * words, it translates our optimistic updates definition into React Query's + * optimistic updates definition. Check their docs for details: + * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates + * + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). + */ +function makeRqOptimisticUpdateOptions( + queryClient: QueryClient, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >[] +): Pick { + async function onMutate(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + + // Cancel any outgoing refetches (so they don't overwrite our optimistic update). + // Theoretically, we can be a bit faster. Instead of awaiting the + // cancellation of all queries, we could cancel and update them in parallel. + // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. + await Promise.all( + specificOptimisticUpdateDefinitions.map(({ queryKey }) => + queryClient.cancelQueries(queryKey) + ) + ); + + // We're using a Map to correctly serialize query keys that contain objects. + const previousData = new Map(); + specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { + // Snapshot the currently cached value. + const previousDataForQuery: CachedData = + queryClient.getQueryData(queryKey); + + // Attempt to optimistically update the cache using the new value. + try { + queryClient.setQueryData(queryKey, updateQuery); + } catch (e) { + console.error( + "The `updateQuery` function threw an exception, skipping optimistic update:" + ); + console.error(e); + } + + // Remember the snapshotted value to restore in case of an error. + previousData.set(queryKey, previousDataForQuery); + }); + + return { previousData }; + } + + function onError(_err, _item, context) { + // All we do in case of an error is roll back all optimistic updates. We ensure + // not to do anything else because React Query rethrows the error. This allows + // the programmer to handle the error as they usually would (i.e., we want the + // error handling to work as it would if the programmer wasn't using optimistic + // updates). + context.previousData.forEach(async (data, queryKey) => { + await queryClient.cancelQueries(queryKey); + queryClient.setQueryData(queryKey, data); + }); + } + + return { + onMutate, + onError, + }; +} + +/** + * Constructs the definition for optimistically updating a specific item. It + * uses a closure over the updated item to construct an item-specific query key + * (e.g., useful when the query key depends on an ID). + * + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic + * update definition with a function for constructing the query key. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. + */ +function getOptimisticUpdateDefinitionForSpecificItem( + optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >, + item: ActionInput +): SpecificOptimisticUpdateDefinition { + const { getQueryKey, updateQuery } = optimisticUpdateDefinition; + return { + queryKey: getQueryKey(item), + updateQuery: (old) => updateQuery(item, old), + }; +} + +/** + * Translates a Wasp query specifier to a query cache key used by React Query. + * + * @param querySpecifier A query specifier that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns A cache key React Query internally uses for addressing queries. + */ +function getRqQueryKeyFromSpecifier( + querySpecifier: QuerySpecifier +): QueryKey { + const [queryFn, ...otherKeys] = querySpecifier; + return [...(queryFn as any).queryCacheKey, ...otherKeys]; +} diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/index.ts index 7a7ad3e2e3..13dd534ed5 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/operations/queries/index.ts @@ -7,5 +7,5 @@ export const mySpecialQuery: QueryFor = createQuery = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.d.ts index 8cb868b58a..c1607701c3 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.d.ts @@ -1,4 +1,5 @@ +import type { Query } from 'wasp/client/operations/rpc'; import type { AuthUser } from '../server/auth/user.js'; import { UseQueryResult } from '@tanstack/react-query'; -export declare const getMe: () => Promise; -export default function useAuth(queryFnArgs?: unknown, config?: any): UseQueryResult; +export declare const getMe: Query; +export default function useAuth(): UseQueryResult; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js index 9029f7877f..f5789a25b0 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js @@ -1,17 +1,17 @@ import { deserialize as superjsonDeserialize } from 'superjson'; -import { useQuery, addMetadataToQuery } from 'wasp/client/operations'; +import { useQuery, buildAndRegisterQuery } from 'wasp/client/operations'; import { api, handleApiError } from 'wasp/client/api'; import { HttpMethod } from 'wasp/client'; // PUBLIC API export const getMe = createUserGetter(); // PUBLIC API -export default function useAuth(queryFnArgs, config) { - return useQuery(getMe, queryFnArgs, config); +export default function useAuth() { + return useQuery(getMe); } function createUserGetter() { const getMeRelativePath = 'auth/me'; const getMeRoute = { method: HttpMethod.Get, path: `/${getMeRelativePath}` }; - async function getMe() { + const getMe = async () => { var _a; try { const response = await api.get(getMeRoute.path); @@ -25,12 +25,11 @@ function createUserGetter() { handleApiError(error); } } - } - addMetadataToQuery(getMe, { - relativeQueryPath: getMeRelativePath, + }; + return buildAndRegisterQuery(getMe, { + queryCacheKey: [getMeRelativePath], queryRoute: getMeRoute, entitiesUsed: ['User'], }); - return getMe; } //# sourceMappingURL=useAuth.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js.map index 3c7774d78b..252ed53938 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/auth/useAuth.js.map @@ -1 +1 @@ -{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../../auth/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAC/D,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACrE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,aAAa;AACb,MAAM,CAAC,MAAM,KAAK,GAAmC,gBAAgB,EAAE,CAAA;AAEvE,aAAa;AACb,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,WAAqB,EAAE,MAAY;IACjE,OAAO,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,CAAA;AAC7C,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,iBAAiB,GAAG,SAAS,CAAA;IACnC,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,iBAAiB,EAAE,EAAE,CAAA;IAC5E,KAAK,UAAU,KAAK;;QAClB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YAC/C,OAAO,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAA,MAAA,KAAK,CAAC,QAAQ,0CAAE,MAAM,MAAK,GAAG,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAA;YACb,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,KAAK,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE;QACxB,iBAAiB,EAAE,iBAAiB;QACpC,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,CAAC,MAAM,CAAC;KACvB,CAAC,CAAA;IAEF,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file +{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../../auth/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAC/D,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AAExE,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAIxC,aAAa;AACb,MAAM,CAAC,MAAM,KAAK,GAAiC,gBAAgB,EAAE,CAAA;AAErE,aAAa;AACb,MAAM,CAAC,OAAO,UAAU,OAAO;IAC7B,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,gBAAgB;IACvB,MAAM,iBAAiB,GAAG,SAAS,CAAA;IACnC,MAAM,UAAU,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,iBAAiB,EAAE,EAAE,CAAA;IAC5E,MAAM,KAAK,GAAyC,KAAK,IAAI,EAAE;;QAC7D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;YAC/C,OAAO,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAA,MAAA,KAAK,CAAC,QAAQ,0CAAE,MAAM,MAAK,GAAG,EAAE,CAAC;gBACnC,OAAO,IAAI,CAAA;YACb,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,KAAK,CAAC,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAAC,KAAK,EAAE;QAClC,aAAa,EAAE,CAAC,iBAAiB,CAAC;QAClC,UAAU,EAAE,UAAU;QACtB,YAAY,EAAE,CAAC,MAAM,CAAC;KACvB,CAAC,CAAA;AACJ,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts similarity index 65% rename from waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts rename to waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts index a23b24ebd6..36a5e0b65b 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts @@ -1,18 +1,15 @@ import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; /** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; /** * A documented (public) way to define optimistic updates. */ @@ -20,25 +17,25 @@ export type OptimisticUpdateDefinition = { getQuerySpecifier: GetQuerySpecifier; updateQuery: UpdateQuery; }; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; /** * A function that takes an item and returns a Wasp Query specifier. */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; /** * A function that takes an item and the previous state of the cache, and returns * the desired (new) state of the cache. */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; /** * A public query specifier used for addressing Wasp queries. See our docs for details: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js similarity index 93% rename from waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js rename to waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js index 2c94cb01fd..95ab379515 100644 --- a/waspc/e2e-test/test-outputs/waspBuild-golden/waspBuild/.wasp/build/sdk/wasp/dist/client/operations/core.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js @@ -1,17 +1,15 @@ import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; export { configureQueryClient } from "./queryClient"; // PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); } // PUBLIC API /** @@ -168,4 +166,4 @@ function getRqQueryKeyFromSpecifier(querySpecifier) { const [queryFn, ...otherKeys] = querySpecifier; return [...queryFn.queryCacheKey, ...otherKeys]; } -//# sourceMappingURL=core.js.map \ No newline at end of file +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts index a66c6c9feb..9584df69e2 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1,4 +1,4 @@ import { type QueryFor } from './core'; import { MySpecialQuery } from 'wasp/server/operations/queries'; export declare const mySpecialQuery: QueryFor; -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js index d0238ee4f5..4ec9ff9ab0 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js @@ -1,6 +1,6 @@ import { createQuery } from './core'; // PUBLIC API export const mySpecialQuery = createQuery('operations/my-special-query', ['User']); -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map index 57f48b3df9..3d0a9da757 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,QAAQ,CAAA;AAGnD,aAAa;AACb,MAAM,CAAC,MAAM,cAAc,GAA6B,WAAW,CACjE,6BAA6B,EAC7B,CAAC,MAAM,CAAC,CACT,CAAA;AAED,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,WAAW,EAAE,MAAM,QAAQ,CAAA;AAGnD,aAAa;AACb,MAAM,CAAC,MAAM,cAAc,GAA6B,WAAW,CACjE,6BAA6B,EAC7B,CAAC,MAAM,CAAC,CACT,CAAA;AAED,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json index 351de46d9a..e51e71e7a1 100644 --- a/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspComplexTest-golden/waspComplexTest/.wasp/out/sdk/wasp/package.json @@ -47,7 +47,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest index 271e4c97cc..f2247703e0 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspJob-golden/files.manifest @@ -10,7 +10,7 @@ waspJob/.wasp/out/sdk/wasp/client/config.ts waspJob/.wasp/out/sdk/wasp/client/index.ts waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspJob/.wasp/out/sdk/wasp/client/operations/actions/index.ts -waspJob/.wasp/out/sdk/wasp/client/operations/core.ts +waspJob/.wasp/out/sdk/wasp/client/operations/hooks.ts waspJob/.wasp/out/sdk/wasp/client/operations/index.ts waspJob/.wasp/out/sdk/wasp/client/operations/internal/index.ts waspJob/.wasp/out/sdk/wasp/client/operations/internal/resources.js @@ -18,6 +18,7 @@ waspJob/.wasp/out/sdk/wasp/client/operations/internal/updateHandlersMap.js waspJob/.wasp/out/sdk/wasp/client/operations/queries/core.ts waspJob/.wasp/out/sdk/wasp/client/operations/queries/index.ts waspJob/.wasp/out/sdk/wasp/client/operations/queryClient.ts +waspJob/.wasp/out/sdk/wasp/client/operations/rpc.ts waspJob/.wasp/out/sdk/wasp/client/router/Link.tsx waspJob/.wasp/out/sdk/wasp/client/router/index.ts waspJob/.wasp/out/sdk/wasp/client/router/linkHelpers.ts @@ -44,9 +45,9 @@ waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/core.js.map waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/index.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js.map -waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts -waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js -waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts +waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js +waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -68,6 +69,9 @@ waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map waspJob/.wasp/out/sdk/wasp/dist/client/operations/queryClient.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js waspJob/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js.map +waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts +waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js +waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map waspJob/.wasp/out/sdk/wasp/dist/client/router/Link.d.ts waspJob/.wasp/out/sdk/wasp/dist/client/router/Link.jsx waspJob/.wasp/out/sdk/wasp/dist/client/router/Link.jsx.map diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums index 34e311a71f..8886bece85 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/.waspchecksums @@ -32,7 +32,7 @@ "file", "../out/sdk/wasp/client/operations/actions/core.ts" ], - "016623c0ebdc1b88d746fa1345015b3cb9653429abdc13a9147a952bff83a927" + "d19dba04947e4015af69d90dd160ec5f4ed020bfa905bc37bcd5d980517097ae" ], [ [ @@ -44,16 +44,16 @@ [ [ "file", - "../out/sdk/wasp/client/operations/core.ts" + "../out/sdk/wasp/client/operations/hooks.ts" ], - "bad860771b16a0d4830fab22de8b85e63c840d47dc3728728a23b8ada91061ef" + "eb4362162aad4b605781e2e33facef83935a7df4101cc0a77097ceaff3a0b360" ], [ [ "file", "../out/sdk/wasp/client/operations/index.ts" ], - "4a66c90319dd7ef0d3a8e6c1f571037c7dfd9778b9def6e951813dbc4b4dcef3" + "edcdc3798590e62b778115b3818df8cc69fa5a8bb3a7fe9fa6d6d5ea706d14c4" ], [ [ @@ -81,14 +81,14 @@ "file", "../out/sdk/wasp/client/operations/queries/core.ts" ], - "5f30328d93582f9c8444720e99f45c19c8647f052b7fbcf5f71b578b9241ac96" + "cce982751b463494c048efd683dddb8d4e617d2f354722a79961199990c7201a" ], [ [ "file", "../out/sdk/wasp/client/operations/queries/index.ts" ], - "882410504b909cc421d50a27c39952f664ba3457438ce746a95d9cab3431944c" + "c92c64425986a38f835211c2693380a8b13904cb78bbf6f6ae880e56ade51686" ], [ [ @@ -97,6 +97,13 @@ ], "5c1d87ac10513788bcde7ebc7c10601b9ad0854cddff355e8fb7e2d4685ecdef" ], + [ + [ + "file", + "../out/sdk/wasp/client/operations/rpc.ts" + ], + "5ab471422e7916c33a0931ce8d499d7d40191802ce2c6f3343b45c623a963566" + ], [ [ "file", @@ -137,7 +144,7 @@ "file", "../out/sdk/wasp/client/test/vitest/helpers.tsx" ], - "b2362e8f80134137fda2f8bb43ef7c0d7ae8aadf8a7adfa472d42d6699e9d918" + "b44ff591a2eebfff4de9fa9e9e1b89f1f22f523f03ab0febc19ff3999721b39a" ], [ [ @@ -200,7 +207,7 @@ "file", "../out/sdk/wasp/package.json" ], - "3b782f30f90af25108f881b4e7d28cd6a8c2894d169dd78c076cf47d76598440" + "a675b7fce46675fe7fc2f5a4edabe8abca4db9cd90a03340d9cf9c54c0e7539f" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/core.ts deleted file mode 100644 index 282c4698a7..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/core.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { - QueryClient, - QueryKey, - useMutation, - UseMutationOptions, - useQueryClient, - useQuery as rqUseQuery, - UseQueryResult, -} from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - -// PUBLIC API -export function useQuery( - queryFn: Query, - queryFnArgs?: Input, - options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); - } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); - } - - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), - ...options, - }); -} - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - -// PUBLIC API -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export function useAction( - actionFn: Action, - actionOptions?: ActionOptions -): typeof actionFn { - const queryClient = useQueryClient(); - - let mutationFn = actionFn; - let options = {}; - if (actionOptions?.optimisticUpdates) { - const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( - translateToInternalDefinition - ); - mutationFn = makeOptimisticUpdateMutationFn( - actionFn, - optimisticUpdatesDefinitions - ); - options = makeRqOptimisticUpdateOptions( - queryClient, - optimisticUpdatesDefinitions - ); - } - - // NOTE: We decided to hide React Query's extra mutation features (e.g., - // isLoading, onSuccess and onError callbacks, synchronous mutate) and only - // expose a simple async function whose API matches the original Action. - // We did this to avoid cluttering the API with stuff we're not sure we need - // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to - // clearly separate our opinionated API from React Query's lower-level - // advanced API (which users can also use) - const mutation = useMutation(mutationFn, options); - return (args) => mutation.mutateAsync(args); -} - -/** - * An internal (undocumented, private, desugared) way of defining optimistic updates. - */ -type InternalOptimisticUpdateDefinition = { - getQueryKey: (item: ActionInput) => QueryKey; - updateQuery: UpdateQuery; -}; - -/** - * An UpdateQuery function "instantiated" with a specific item. It only takes - * the current state of the cache and returns the desired (new) state of the - * cache. - */ -type SpecificUpdateQuery = (oldData: CachedData) => CachedData; - -/** - * A specific, "instantiated" optimistic update definition which contains a - * fully-constructed query key and a specific update function. - */ -type SpecificOptimisticUpdateDefinition = { - queryKey: QueryKey; - updateQuery: SpecificUpdateQuery; -}; - -type InternalAction = Action & { - internal( - item: Input, - optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] - ): Promise; -}; - -/** - * Translates/Desugars a public optimistic update definition object into a - * definition object our system uses internally. - * - * @param publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns An internally-used optimistic update definition object. - */ -function translateToInternalDefinition( - publicOptimisticUpdateDefinition: OptimisticUpdateDefinition -): InternalOptimisticUpdateDefinition { - const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; - - const definitionErrors = []; - if (typeof getQuerySpecifier !== "function") { - definitionErrors.push("`getQuerySpecifier` is not a function."); - } - if (typeof updateQuery !== "function") { - definitionErrors.push("`updateQuery` is not a function."); - } - if (definitionErrors.length) { - throw new TypeError( - `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` - ); - } - - return { - getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), - updateQuery, - }; -} - -/** - * Creates a function that performs an action while telling it about the - * optimistic updates it caused. - * - * @param actionFn The Wasp Action. - * @param optimisticUpdateDefinitions The optimisitc updates the action causes. - * @returns An decorated action which performs optimistic updates. - */ -function makeOptimisticUpdateMutationFn( - actionFn: Action, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - Input, - CachedData - >[] -): typeof actionFn { - return function performActionWithOptimisticUpdates(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - return (actionFn as InternalAction).internal( - item, - specificOptimisticUpdateDefinitions - ); - }; -} - -/** - * Given a ReactQuery query client and our internal definition of optimistic - * updates, this function constructs an object describing those same optimistic - * updates in a format we can pass into React Query's useMutation hook. In other - * words, it translates our optimistic updates definition into React Query's - * optimistic updates definition. Check their docs for details: - * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates - * - * @param queryClient The QueryClient instance used by React Query. - * @param optimisticUpdateDefinitions A list containing internal optimistic - * updates definition objects (i.e., a list where each object carries the - * instructions for performing particular optimistic update). - * @returns An object containing 'onMutate' and 'onError' functions - * corresponding to the given optimistic update definitions (check the docs - * linked above for details). - */ -function makeRqOptimisticUpdateOptions( - queryClient: QueryClient, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >[] -): Pick { - async function onMutate(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - - // Cancel any outgoing refetches (so they don't overwrite our optimistic update). - // Theoretically, we can be a bit faster. Instead of awaiting the - // cancellation of all queries, we could cancel and update them in parallel. - // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. - await Promise.all( - specificOptimisticUpdateDefinitions.map(({ queryKey }) => - queryClient.cancelQueries(queryKey) - ) - ); - - // We're using a Map to correctly serialize query keys that contain objects. - const previousData = new Map(); - specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { - // Snapshot the currently cached value. - const previousDataForQuery: CachedData = - queryClient.getQueryData(queryKey); - - // Attempt to optimistically update the cache using the new value. - try { - queryClient.setQueryData(queryKey, updateQuery); - } catch (e) { - console.error( - "The `updateQuery` function threw an exception, skipping optimistic update:" - ); - console.error(e); - } - - // Remember the snapshotted value to restore in case of an error. - previousData.set(queryKey, previousDataForQuery); - }); - - return { previousData }; - } - - function onError(_err, _item, context) { - // All we do in case of an error is roll back all optimistic updates. We ensure - // not to do anything else because React Query rethrows the error. This allows - // the programmer to handle the error as they usually would (i.e., we want the - // error handling to work as it would if the programmer wasn't using optimistic - // updates). - context.previousData.forEach(async (data, queryKey) => { - await queryClient.cancelQueries(queryKey); - queryClient.setQueryData(queryKey, data); - }); - } - - return { - onMutate, - onError, - }; -} - -/** - * Constructs the definition for optimistically updating a specific item. It - * uses a closure over the updated item to construct an item-specific query key - * (e.g., useful when the query key depends on an ID). - * - * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic - * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the - * argument passed to the Action). - * @returns A specific optimistic update definition which corresponds to the - * provided definition and closes over the provided item. - */ -function getOptimisticUpdateDefinitionForSpecificItem( - optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >, - item: ActionInput -): SpecificOptimisticUpdateDefinition { - const { getQueryKey, updateQuery } = optimisticUpdateDefinition; - return { - queryKey: getQueryKey(item), - updateQuery: (old) => updateQuery(item, old), - }; -} - -/** - * Translates a Wasp query specifier to a query cache key used by React Query. - * - * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns A cache key React Query internally uses for addressing queries. - */ -function getRqQueryKeyFromSpecifier( - querySpecifier: QuerySpecifier -): QueryKey { - const [queryFn, ...otherKeys] = querySpecifier; - return [...(queryFn as any).queryCacheKey, ...otherKeys]; -} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/hooks.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/hooks.ts new file mode 100644 index 0000000000..fa913a44c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/hooks.ts @@ -0,0 +1,328 @@ +import { + QueryClient, + QueryKey, + useMutation, + UseMutationOptions, + useQueryClient, + useQuery as rqUseQuery, + UseQueryResult, +} from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; +export { configureQueryClient } from "./queryClient"; + +// PUBLIC API +export function useQuery( + query: Query, + queryFnArgs?: Input, + options?: any +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') + } + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') + } + + return rqUseQuery({ + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), + ...options, + }) +} + +// PUBLIC API +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export function useAction( + actionFn: Action, + actionOptions?: ActionOptions +): typeof actionFn { + const queryClient = useQueryClient(); + + let mutationFn = actionFn; + let options = {}; + if (actionOptions?.optimisticUpdates) { + const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( + translateToInternalDefinition + ); + mutationFn = makeOptimisticUpdateMutationFn( + actionFn, + optimisticUpdatesDefinitions + ); + options = makeRqOptimisticUpdateOptions( + queryClient, + optimisticUpdatesDefinitions + ); + } + + // NOTE: We decided to hide React Query's extra mutation features (e.g., + // isLoading, onSuccess and onError callbacks, synchronous mutate) and only + // expose a simple async function whose API matches the original Action. + // We did this to avoid cluttering the API with stuff we're not sure we need + // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to + // clearly separate our opinionated API from React Query's lower-level + // advanced API (which users can also use) + const mutation = useMutation(mutationFn, options); + return (args) => mutation.mutateAsync(args); +} + +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + +/** + * An internal (undocumented, private, desugared) way of defining optimistic updates. + */ +type InternalOptimisticUpdateDefinition = { + getQueryKey: (item: ActionInput) => QueryKey; + updateQuery: UpdateQuery; +}; + +/** + * An UpdateQuery function "instantiated" with a specific item. It only takes + * the current state of the cache and returns the desired (new) state of the + * cache. + */ +type SpecificUpdateQuery = (oldData: CachedData) => CachedData; + +/** + * A specific, "instantiated" optimistic update definition which contains a + * fully-constructed query key and a specific update function. + */ +type SpecificOptimisticUpdateDefinition = { + queryKey: QueryKey; + updateQuery: SpecificUpdateQuery; +}; + +type InternalAction = Action & { + internal( + item: Input, + optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] + ): Promise; +}; + +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition( + publicOptimisticUpdateDefinition: OptimisticUpdateDefinition +): InternalOptimisticUpdateDefinition { + const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; + + const definitionErrors = []; + if (typeof getQuerySpecifier !== "function") { + definitionErrors.push("`getQuerySpecifier` is not a function."); + } + if (typeof updateQuery !== "function") { + definitionErrors.push("`updateQuery` is not a function."); + } + if (definitionErrors.length) { + throw new TypeError( + `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` + ); + } + + return { + getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), + updateQuery, + }; +} + +/** + * Creates a function that performs an action while telling it about the + * optimistic updates it caused. + * + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. + */ +function makeOptimisticUpdateMutationFn( + actionFn: Action, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + Input, + CachedData + >[] +): typeof actionFn { + return function performActionWithOptimisticUpdates(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + return (actionFn as InternalAction).internal( + item, + specificOptimisticUpdateDefinitions + ); + }; +} + +/** + * Given a ReactQuery query client and our internal definition of optimistic + * updates, this function constructs an object describing those same optimistic + * updates in a format we can pass into React Query's useMutation hook. In other + * words, it translates our optimistic updates definition into React Query's + * optimistic updates definition. Check their docs for details: + * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates + * + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). + */ +function makeRqOptimisticUpdateOptions( + queryClient: QueryClient, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >[] +): Pick { + async function onMutate(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + + // Cancel any outgoing refetches (so they don't overwrite our optimistic update). + // Theoretically, we can be a bit faster. Instead of awaiting the + // cancellation of all queries, we could cancel and update them in parallel. + // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. + await Promise.all( + specificOptimisticUpdateDefinitions.map(({ queryKey }) => + queryClient.cancelQueries(queryKey) + ) + ); + + // We're using a Map to correctly serialize query keys that contain objects. + const previousData = new Map(); + specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { + // Snapshot the currently cached value. + const previousDataForQuery: CachedData = + queryClient.getQueryData(queryKey); + + // Attempt to optimistically update the cache using the new value. + try { + queryClient.setQueryData(queryKey, updateQuery); + } catch (e) { + console.error( + "The `updateQuery` function threw an exception, skipping optimistic update:" + ); + console.error(e); + } + + // Remember the snapshotted value to restore in case of an error. + previousData.set(queryKey, previousDataForQuery); + }); + + return { previousData }; + } + + function onError(_err, _item, context) { + // All we do in case of an error is roll back all optimistic updates. We ensure + // not to do anything else because React Query rethrows the error. This allows + // the programmer to handle the error as they usually would (i.e., we want the + // error handling to work as it would if the programmer wasn't using optimistic + // updates). + context.previousData.forEach(async (data, queryKey) => { + await queryClient.cancelQueries(queryKey); + queryClient.setQueryData(queryKey, data); + }); + } + + return { + onMutate, + onError, + }; +} + +/** + * Constructs the definition for optimistically updating a specific item. It + * uses a closure over the updated item to construct an item-specific query key + * (e.g., useful when the query key depends on an ID). + * + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic + * update definition with a function for constructing the query key. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. + */ +function getOptimisticUpdateDefinitionForSpecificItem( + optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >, + item: ActionInput +): SpecificOptimisticUpdateDefinition { + const { getQueryKey, updateQuery } = optimisticUpdateDefinition; + return { + queryKey: getQueryKey(item), + updateQuery: (old) => updateQuery(item, old), + }; +} + +/** + * Translates a Wasp query specifier to a query cache key used by React Query. + * + * @param querySpecifier A query specifier that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns A cache key React Query internally uses for addressing queries. + */ +function getRqQueryKeyFromSpecifier( + querySpecifier: QuerySpecifier +): QueryKey { + const [queryFn, ...otherKeys] = querySpecifier; + return [...(queryFn as any).queryCacheKey, ...otherKeys]; +} diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/index.ts index eeb2cf5a11..af63c67c63 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/queries/index.ts @@ -1,4 +1,4 @@ import { type QueryFor, createQuery } from './core' -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/rpc.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts deleted file mode 100644 index a23b24ebd6..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UseQueryResult } from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js deleted file mode 100644 index 2c94cb01fd..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js +++ /dev/null @@ -1,171 +0,0 @@ -import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); - } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); - } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); -} -// PUBLIC API -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export function useAction(actionFn, actionOptions) { - const queryClient = useQueryClient(); - let mutationFn = actionFn; - let options = {}; - if (actionOptions === null || actionOptions === void 0 ? void 0 : actionOptions.optimisticUpdates) { - const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map(translateToInternalDefinition); - mutationFn = makeOptimisticUpdateMutationFn(actionFn, optimisticUpdatesDefinitions); - options = makeRqOptimisticUpdateOptions(queryClient, optimisticUpdatesDefinitions); - } - // NOTE: We decided to hide React Query's extra mutation features (e.g., - // isLoading, onSuccess and onError callbacks, synchronous mutate) and only - // expose a simple async function whose API matches the original Action. - // We did this to avoid cluttering the API with stuff we're not sure we need - // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to - // clearly separate our opinionated API from React Query's lower-level - // advanced API (which users can also use) - const mutation = useMutation(mutationFn, options); - return (args) => mutation.mutateAsync(args); -} -/** - * Translates/Desugars a public optimistic update definition object into a - * definition object our system uses internally. - * - * @param publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns An internally-used optimistic update definition object. - */ -function translateToInternalDefinition(publicOptimisticUpdateDefinition) { - const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; - const definitionErrors = []; - if (typeof getQuerySpecifier !== "function") { - definitionErrors.push("`getQuerySpecifier` is not a function."); - } - if (typeof updateQuery !== "function") { - definitionErrors.push("`updateQuery` is not a function."); - } - if (definitionErrors.length) { - throw new TypeError(`Invalid optimistic update definition: ${definitionErrors.join(", ")}.`); - } - return { - getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), - updateQuery, - }; -} -/** - * Creates a function that performs an action while telling it about the - * optimistic updates it caused. - * - * @param actionFn The Wasp Action. - * @param optimisticUpdateDefinitions The optimisitc updates the action causes. - * @returns An decorated action which performs optimistic updates. - */ -function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { - return function performActionWithOptimisticUpdates(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); - return actionFn.internal(item, specificOptimisticUpdateDefinitions); - }; -} -/** - * Given a ReactQuery query client and our internal definition of optimistic - * updates, this function constructs an object describing those same optimistic - * updates in a format we can pass into React Query's useMutation hook. In other - * words, it translates our optimistic updates definition into React Query's - * optimistic updates definition. Check their docs for details: - * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates - * - * @param queryClient The QueryClient instance used by React Query. - * @param optimisticUpdateDefinitions A list containing internal optimistic - * updates definition objects (i.e., a list where each object carries the - * instructions for performing particular optimistic update). - * @returns An object containing 'onMutate' and 'onError' functions - * corresponding to the given optimistic update definitions (check the docs - * linked above for details). - */ -function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) { - async function onMutate(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); - // Cancel any outgoing refetches (so they don't overwrite our optimistic update). - // Theoretically, we can be a bit faster. Instead of awaiting the - // cancellation of all queries, we could cancel and update them in parallel. - // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. - await Promise.all(specificOptimisticUpdateDefinitions.map(({ queryKey }) => queryClient.cancelQueries(queryKey))); - // We're using a Map to correctly serialize query keys that contain objects. - const previousData = new Map(); - specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { - // Snapshot the currently cached value. - const previousDataForQuery = queryClient.getQueryData(queryKey); - // Attempt to optimistically update the cache using the new value. - try { - queryClient.setQueryData(queryKey, updateQuery); - } - catch (e) { - console.error("The `updateQuery` function threw an exception, skipping optimistic update:"); - console.error(e); - } - // Remember the snapshotted value to restore in case of an error. - previousData.set(queryKey, previousDataForQuery); - }); - return { previousData }; - } - function onError(_err, _item, context) { - // All we do in case of an error is roll back all optimistic updates. We ensure - // not to do anything else because React Query rethrows the error. This allows - // the programmer to handle the error as they usually would (i.e., we want the - // error handling to work as it would if the programmer wasn't using optimistic - // updates). - context.previousData.forEach(async (data, queryKey) => { - await queryClient.cancelQueries(queryKey); - queryClient.setQueryData(queryKey, data); - }); - } - return { - onMutate, - onError, - }; -} -/** - * Constructs the definition for optimistically updating a specific item. It - * uses a closure over the updated item to construct an item-specific query key - * (e.g., useful when the query key depends on an ID). - * - * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic - * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the - * argument passed to the Action). - * @returns A specific optimistic update definition which corresponds to the - * provided definition and closes over the provided item. - */ -function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition, item) { - const { getQueryKey, updateQuery } = optimisticUpdateDefinition; - return { - queryKey: getQueryKey(item), - updateQuery: (old) => updateQuery(item, old), - }; -} -/** - * Translates a Wasp query specifier to a query cache key used by React Query. - * - * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns A cache key React Query internally uses for addressing queries. - */ -function getRqQueryKeyFromSpecifier(querySpecifier) { - const [queryFn, ...otherKeys] = querySpecifier; - return [...queryFn.queryCacheKey, ...otherKeys]; -} -//# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts new file mode 100644 index 0000000000..36a5e0b65b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts @@ -0,0 +1,41 @@ +import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +export { configureQueryClient } from "./queryClient"; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js new file mode 100644 index 0000000000..95ab379515 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js @@ -0,0 +1,169 @@ +import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; +export { configureQueryClient } from "./queryClient"; +// PUBLIC API +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); + } + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); + } + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); +} +// PUBLIC API +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export function useAction(actionFn, actionOptions) { + const queryClient = useQueryClient(); + let mutationFn = actionFn; + let options = {}; + if (actionOptions === null || actionOptions === void 0 ? void 0 : actionOptions.optimisticUpdates) { + const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map(translateToInternalDefinition); + mutationFn = makeOptimisticUpdateMutationFn(actionFn, optimisticUpdatesDefinitions); + options = makeRqOptimisticUpdateOptions(queryClient, optimisticUpdatesDefinitions); + } + // NOTE: We decided to hide React Query's extra mutation features (e.g., + // isLoading, onSuccess and onError callbacks, synchronous mutate) and only + // expose a simple async function whose API matches the original Action. + // We did this to avoid cluttering the API with stuff we're not sure we need + // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to + // clearly separate our opinionated API from React Query's lower-level + // advanced API (which users can also use) + const mutation = useMutation(mutationFn, options); + return (args) => mutation.mutateAsync(args); +} +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition(publicOptimisticUpdateDefinition) { + const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; + const definitionErrors = []; + if (typeof getQuerySpecifier !== "function") { + definitionErrors.push("`getQuerySpecifier` is not a function."); + } + if (typeof updateQuery !== "function") { + definitionErrors.push("`updateQuery` is not a function."); + } + if (definitionErrors.length) { + throw new TypeError(`Invalid optimistic update definition: ${definitionErrors.join(", ")}.`); + } + return { + getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), + updateQuery, + }; +} +/** + * Creates a function that performs an action while telling it about the + * optimistic updates it caused. + * + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. + */ +function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { + return function performActionWithOptimisticUpdates(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); + return actionFn.internal(item, specificOptimisticUpdateDefinitions); + }; +} +/** + * Given a ReactQuery query client and our internal definition of optimistic + * updates, this function constructs an object describing those same optimistic + * updates in a format we can pass into React Query's useMutation hook. In other + * words, it translates our optimistic updates definition into React Query's + * optimistic updates definition. Check their docs for details: + * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates + * + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). + */ +function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) { + async function onMutate(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); + // Cancel any outgoing refetches (so they don't overwrite our optimistic update). + // Theoretically, we can be a bit faster. Instead of awaiting the + // cancellation of all queries, we could cancel and update them in parallel. + // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. + await Promise.all(specificOptimisticUpdateDefinitions.map(({ queryKey }) => queryClient.cancelQueries(queryKey))); + // We're using a Map to correctly serialize query keys that contain objects. + const previousData = new Map(); + specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { + // Snapshot the currently cached value. + const previousDataForQuery = queryClient.getQueryData(queryKey); + // Attempt to optimistically update the cache using the new value. + try { + queryClient.setQueryData(queryKey, updateQuery); + } + catch (e) { + console.error("The `updateQuery` function threw an exception, skipping optimistic update:"); + console.error(e); + } + // Remember the snapshotted value to restore in case of an error. + previousData.set(queryKey, previousDataForQuery); + }); + return { previousData }; + } + function onError(_err, _item, context) { + // All we do in case of an error is roll back all optimistic updates. We ensure + // not to do anything else because React Query rethrows the error. This allows + // the programmer to handle the error as they usually would (i.e., we want the + // error handling to work as it would if the programmer wasn't using optimistic + // updates). + context.previousData.forEach(async (data, queryKey) => { + await queryClient.cancelQueries(queryKey); + queryClient.setQueryData(queryKey, data); + }); + } + return { + onMutate, + onError, + }; +} +/** + * Constructs the definition for optimistically updating a specific item. It + * uses a closure over the updated item to construct an item-specific query key + * (e.g., useful when the query key depends on an ID). + * + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic + * update definition with a function for constructing the query key. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. + */ +function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition, item) { + const { getQueryKey, updateQuery } = optimisticUpdateDefinition; + return { + queryKey: getQueryKey(item), + updateQuery: (old) => updateQuery(item, old), + }; +} +/** + * Translates a Wasp query specifier to a query cache key used by React Query. + * + * @param querySpecifier A query specifier that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns A cache key React Query internally uses for addressing queries. + */ +function getRqQueryKeyFromSpecifier(querySpecifier) { + const [queryFn, ...otherKeys] = querySpecifier; + return [...queryFn.queryCacheKey, ...otherKeys]; +} +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts index 575c502be1..2d9dbafe81 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1 +1 @@ -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js index 1c28e8d0d3..56d2e15238 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js @@ -1,3 +1,3 @@ -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map index 5c83611fa3..44416efcfa 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json index b46249ed12..2a7a874c5c 100644 --- a/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspJob-golden/waspJob/.wasp/out/sdk/wasp/package.json @@ -42,7 +42,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest index 5602a4b139..6d5eae8a38 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/files.manifest @@ -13,7 +13,7 @@ waspMigrate/.wasp/out/sdk/wasp/client/config.ts waspMigrate/.wasp/out/sdk/wasp/client/index.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/index.ts -waspMigrate/.wasp/out/sdk/wasp/client/operations/core.ts +waspMigrate/.wasp/out/sdk/wasp/client/operations/hooks.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/index.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/internal/index.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/internal/resources.js @@ -21,6 +21,7 @@ waspMigrate/.wasp/out/sdk/wasp/client/operations/internal/updateHandlersMap.js waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/core.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/index.ts waspMigrate/.wasp/out/sdk/wasp/client/operations/queryClient.ts +waspMigrate/.wasp/out/sdk/wasp/client/operations/rpc.ts waspMigrate/.wasp/out/sdk/wasp/client/router/Link.tsx waspMigrate/.wasp/out/sdk/wasp/client/router/index.ts waspMigrate/.wasp/out/sdk/wasp/client/router/linkHelpers.ts @@ -47,9 +48,9 @@ waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/core.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/index.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/index.js.map -waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts -waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js -waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -71,6 +72,9 @@ waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queryClient.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queryClient.js.map +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js +waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map waspMigrate/.wasp/out/sdk/wasp/dist/client/router/Link.d.ts waspMigrate/.wasp/out/sdk/wasp/dist/client/router/Link.jsx waspMigrate/.wasp/out/sdk/wasp/dist/client/router/Link.jsx.map diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums index 9785061b75..e93786b405 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/.waspchecksums @@ -32,7 +32,7 @@ "file", "../out/sdk/wasp/client/operations/actions/core.ts" ], - "016623c0ebdc1b88d746fa1345015b3cb9653429abdc13a9147a952bff83a927" + "d19dba04947e4015af69d90dd160ec5f4ed020bfa905bc37bcd5d980517097ae" ], [ [ @@ -44,16 +44,16 @@ [ [ "file", - "../out/sdk/wasp/client/operations/core.ts" + "../out/sdk/wasp/client/operations/hooks.ts" ], - "bad860771b16a0d4830fab22de8b85e63c840d47dc3728728a23b8ada91061ef" + "eb4362162aad4b605781e2e33facef83935a7df4101cc0a77097ceaff3a0b360" ], [ [ "file", "../out/sdk/wasp/client/operations/index.ts" ], - "4a66c90319dd7ef0d3a8e6c1f571037c7dfd9778b9def6e951813dbc4b4dcef3" + "edcdc3798590e62b778115b3818df8cc69fa5a8bb3a7fe9fa6d6d5ea706d14c4" ], [ [ @@ -81,14 +81,14 @@ "file", "../out/sdk/wasp/client/operations/queries/core.ts" ], - "5f30328d93582f9c8444720e99f45c19c8647f052b7fbcf5f71b578b9241ac96" + "cce982751b463494c048efd683dddb8d4e617d2f354722a79961199990c7201a" ], [ [ "file", "../out/sdk/wasp/client/operations/queries/index.ts" ], - "882410504b909cc421d50a27c39952f664ba3457438ce746a95d9cab3431944c" + "c92c64425986a38f835211c2693380a8b13904cb78bbf6f6ae880e56ade51686" ], [ [ @@ -97,6 +97,13 @@ ], "5c1d87ac10513788bcde7ebc7c10601b9ad0854cddff355e8fb7e2d4685ecdef" ], + [ + [ + "file", + "../out/sdk/wasp/client/operations/rpc.ts" + ], + "5ab471422e7916c33a0931ce8d499d7d40191802ce2c6f3343b45c623a963566" + ], [ [ "file", @@ -137,7 +144,7 @@ "file", "../out/sdk/wasp/client/test/vitest/helpers.tsx" ], - "b2362e8f80134137fda2f8bb43ef7c0d7ae8aadf8a7adfa472d42d6699e9d918" + "b44ff591a2eebfff4de9fa9e9e1b89f1f22f523f03ab0febc19ff3999721b39a" ], [ [ @@ -193,7 +200,7 @@ "file", "../out/sdk/wasp/package.json" ], - "3422dd87e9e5f4aeb3922ee152ad691399d91ad71172f2b76f6235eb28955fa2" + "8df2ebcc130b484aa956ddf0109b23c83fe2221cb41ea35ed5e99817013f36a9" ], [ [ diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts index f5db25aff2..c24f726ef3 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/actions/core.ts @@ -1,5 +1,5 @@ -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Action } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { registerActionInProgress, @@ -7,7 +7,7 @@ import { } from '../internal/resources.js' // PRIVATE API -export function createAction( +export function createAction( relativeActionRoute: string, entitiesUsed: unknown[] ): ActionFor { @@ -41,8 +41,5 @@ export function createAction( } // PRIVATE API -export type ActionFor = - Action[0], _Awaited<_ReturnType>> - - -type GenericBackendAction = (args: never, context: any) => unknown +export type ActionFor = + OperationRpcFor diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/core.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/core.ts deleted file mode 100644 index 282c4698a7..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/core.ts +++ /dev/null @@ -1,346 +0,0 @@ -import { - QueryClient, - QueryKey, - useMutation, - UseMutationOptions, - useQueryClient, - useQuery as rqUseQuery, - UseQueryResult, -} from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; - -// PUBLIC API -export function useQuery( - queryFn: Query, - queryFnArgs?: Input, - options?: any -): UseQueryResult; - -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); - } - if (!queryFn.queryCacheKey) { - throw new TypeError( - "queryFn needs to have queryCacheKey property defined." - ); - } - - const queryKey = - queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery({ - queryKey, - queryFn: () => queryFn(queryKey, queryFnArgs), - ...options, - }); -} - -// PRIVATE API (but should maybe be public, users use values of this type) -export type Action = [Input] extends [never] - ? (args?: unknown) => Promise - : (args: Input) => Promise; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; - -// PUBLIC API -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = ( - item: ActionInput -) => QuerySpecifier; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = ( - item: ActionInput, - oldData: CachedData | undefined -) => CachedData; - -// PRIVATE API (but should maybe be public, users define values of this type) -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; - -// PUBLIC API -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export function useAction( - actionFn: Action, - actionOptions?: ActionOptions -): typeof actionFn { - const queryClient = useQueryClient(); - - let mutationFn = actionFn; - let options = {}; - if (actionOptions?.optimisticUpdates) { - const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( - translateToInternalDefinition - ); - mutationFn = makeOptimisticUpdateMutationFn( - actionFn, - optimisticUpdatesDefinitions - ); - options = makeRqOptimisticUpdateOptions( - queryClient, - optimisticUpdatesDefinitions - ); - } - - // NOTE: We decided to hide React Query's extra mutation features (e.g., - // isLoading, onSuccess and onError callbacks, synchronous mutate) and only - // expose a simple async function whose API matches the original Action. - // We did this to avoid cluttering the API with stuff we're not sure we need - // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to - // clearly separate our opinionated API from React Query's lower-level - // advanced API (which users can also use) - const mutation = useMutation(mutationFn, options); - return (args) => mutation.mutateAsync(args); -} - -/** - * An internal (undocumented, private, desugared) way of defining optimistic updates. - */ -type InternalOptimisticUpdateDefinition = { - getQueryKey: (item: ActionInput) => QueryKey; - updateQuery: UpdateQuery; -}; - -/** - * An UpdateQuery function "instantiated" with a specific item. It only takes - * the current state of the cache and returns the desired (new) state of the - * cache. - */ -type SpecificUpdateQuery = (oldData: CachedData) => CachedData; - -/** - * A specific, "instantiated" optimistic update definition which contains a - * fully-constructed query key and a specific update function. - */ -type SpecificOptimisticUpdateDefinition = { - queryKey: QueryKey; - updateQuery: SpecificUpdateQuery; -}; - -type InternalAction = Action & { - internal( - item: Input, - optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] - ): Promise; -}; - -/** - * Translates/Desugars a public optimistic update definition object into a - * definition object our system uses internally. - * - * @param publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns An internally-used optimistic update definition object. - */ -function translateToInternalDefinition( - publicOptimisticUpdateDefinition: OptimisticUpdateDefinition -): InternalOptimisticUpdateDefinition { - const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; - - const definitionErrors = []; - if (typeof getQuerySpecifier !== "function") { - definitionErrors.push("`getQuerySpecifier` is not a function."); - } - if (typeof updateQuery !== "function") { - definitionErrors.push("`updateQuery` is not a function."); - } - if (definitionErrors.length) { - throw new TypeError( - `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` - ); - } - - return { - getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), - updateQuery, - }; -} - -/** - * Creates a function that performs an action while telling it about the - * optimistic updates it caused. - * - * @param actionFn The Wasp Action. - * @param optimisticUpdateDefinitions The optimisitc updates the action causes. - * @returns An decorated action which performs optimistic updates. - */ -function makeOptimisticUpdateMutationFn( - actionFn: Action, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - Input, - CachedData - >[] -): typeof actionFn { - return function performActionWithOptimisticUpdates(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - return (actionFn as InternalAction).internal( - item, - specificOptimisticUpdateDefinitions - ); - }; -} - -/** - * Given a ReactQuery query client and our internal definition of optimistic - * updates, this function constructs an object describing those same optimistic - * updates in a format we can pass into React Query's useMutation hook. In other - * words, it translates our optimistic updates definition into React Query's - * optimistic updates definition. Check their docs for details: - * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates - * - * @param queryClient The QueryClient instance used by React Query. - * @param optimisticUpdateDefinitions A list containing internal optimistic - * updates definition objects (i.e., a list where each object carries the - * instructions for performing particular optimistic update). - * @returns An object containing 'onMutate' and 'onError' functions - * corresponding to the given optimistic update definitions (check the docs - * linked above for details). - */ -function makeRqOptimisticUpdateOptions( - queryClient: QueryClient, - optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >[] -): Pick { - async function onMutate(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( - (generalDefinition) => - getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) - ); - - // Cancel any outgoing refetches (so they don't overwrite our optimistic update). - // Theoretically, we can be a bit faster. Instead of awaiting the - // cancellation of all queries, we could cancel and update them in parallel. - // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. - await Promise.all( - specificOptimisticUpdateDefinitions.map(({ queryKey }) => - queryClient.cancelQueries(queryKey) - ) - ); - - // We're using a Map to correctly serialize query keys that contain objects. - const previousData = new Map(); - specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { - // Snapshot the currently cached value. - const previousDataForQuery: CachedData = - queryClient.getQueryData(queryKey); - - // Attempt to optimistically update the cache using the new value. - try { - queryClient.setQueryData(queryKey, updateQuery); - } catch (e) { - console.error( - "The `updateQuery` function threw an exception, skipping optimistic update:" - ); - console.error(e); - } - - // Remember the snapshotted value to restore in case of an error. - previousData.set(queryKey, previousDataForQuery); - }); - - return { previousData }; - } - - function onError(_err, _item, context) { - // All we do in case of an error is roll back all optimistic updates. We ensure - // not to do anything else because React Query rethrows the error. This allows - // the programmer to handle the error as they usually would (i.e., we want the - // error handling to work as it would if the programmer wasn't using optimistic - // updates). - context.previousData.forEach(async (data, queryKey) => { - await queryClient.cancelQueries(queryKey); - queryClient.setQueryData(queryKey, data); - }); - } - - return { - onMutate, - onError, - }; -} - -/** - * Constructs the definition for optimistically updating a specific item. It - * uses a closure over the updated item to construct an item-specific query key - * (e.g., useful when the query key depends on an ID). - * - * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic - * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the - * argument passed to the Action). - * @returns A specific optimistic update definition which corresponds to the - * provided definition and closes over the provided item. - */ -function getOptimisticUpdateDefinitionForSpecificItem( - optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< - ActionInput, - CachedData - >, - item: ActionInput -): SpecificOptimisticUpdateDefinition { - const { getQueryKey, updateQuery } = optimisticUpdateDefinition; - return { - queryKey: getQueryKey(item), - updateQuery: (old) => updateQuery(item, old), - }; -} - -/** - * Translates a Wasp query specifier to a query cache key used by React Query. - * - * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns A cache key React Query internally uses for addressing queries. - */ -function getRqQueryKeyFromSpecifier( - querySpecifier: QuerySpecifier -): QueryKey { - const [queryFn, ...otherKeys] = querySpecifier; - return [...(queryFn as any).queryCacheKey, ...otherKeys]; -} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/hooks.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/hooks.ts new file mode 100644 index 0000000000..fa913a44c1 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/hooks.ts @@ -0,0 +1,328 @@ +import { + QueryClient, + QueryKey, + useMutation, + UseMutationOptions, + useQueryClient, + useQuery as rqUseQuery, + UseQueryResult, +} from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +import { makeQueryCacheKey } from "./queries/core"; +export { configureQueryClient } from "./queryClient"; + +// PUBLIC API +export function useQuery( + query: Query, + queryFnArgs?: Input, + options?: any +): UseQueryResult { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.') + } + + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.') + } + + return rqUseQuery({ + queryKey: makeQueryCacheKey(query, queryFnArgs), + queryFn: () => query(queryFnArgs), + ...options, + }) +} + +// PUBLIC API +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export function useAction( + actionFn: Action, + actionOptions?: ActionOptions +): typeof actionFn { + const queryClient = useQueryClient(); + + let mutationFn = actionFn; + let options = {}; + if (actionOptions?.optimisticUpdates) { + const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map( + translateToInternalDefinition + ); + mutationFn = makeOptimisticUpdateMutationFn( + actionFn, + optimisticUpdatesDefinitions + ); + options = makeRqOptimisticUpdateOptions( + queryClient, + optimisticUpdatesDefinitions + ); + } + + // NOTE: We decided to hide React Query's extra mutation features (e.g., + // isLoading, onSuccess and onError callbacks, synchronous mutate) and only + // expose a simple async function whose API matches the original Action. + // We did this to avoid cluttering the API with stuff we're not sure we need + // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to + // clearly separate our opinionated API from React Query's lower-level + // advanced API (which users can also use) + const mutation = useMutation(mutationFn, options); + return (args) => mutation.mutateAsync(args); +} + +// PUBLIC API +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; + +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; + +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = ( + item: ActionInput +) => QuerySpecifier; + +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = ( + item: ActionInput, + oldData: CachedData | undefined +) => CachedData; + +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; + + +/** + * An internal (undocumented, private, desugared) way of defining optimistic updates. + */ +type InternalOptimisticUpdateDefinition = { + getQueryKey: (item: ActionInput) => QueryKey; + updateQuery: UpdateQuery; +}; + +/** + * An UpdateQuery function "instantiated" with a specific item. It only takes + * the current state of the cache and returns the desired (new) state of the + * cache. + */ +type SpecificUpdateQuery = (oldData: CachedData) => CachedData; + +/** + * A specific, "instantiated" optimistic update definition which contains a + * fully-constructed query key and a specific update function. + */ +type SpecificOptimisticUpdateDefinition = { + queryKey: QueryKey; + updateQuery: SpecificUpdateQuery; +}; + +type InternalAction = Action & { + internal( + item: Input, + optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] + ): Promise; +}; + +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition( + publicOptimisticUpdateDefinition: OptimisticUpdateDefinition +): InternalOptimisticUpdateDefinition { + const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; + + const definitionErrors = []; + if (typeof getQuerySpecifier !== "function") { + definitionErrors.push("`getQuerySpecifier` is not a function."); + } + if (typeof updateQuery !== "function") { + definitionErrors.push("`updateQuery` is not a function."); + } + if (definitionErrors.length) { + throw new TypeError( + `Invalid optimistic update definition: ${definitionErrors.join(", ")}.` + ); + } + + return { + getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), + updateQuery, + }; +} + +/** + * Creates a function that performs an action while telling it about the + * optimistic updates it caused. + * + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. + */ +function makeOptimisticUpdateMutationFn( + actionFn: Action, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + Input, + CachedData + >[] +): typeof actionFn { + return function performActionWithOptimisticUpdates(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + return (actionFn as InternalAction).internal( + item, + specificOptimisticUpdateDefinitions + ); + }; +} + +/** + * Given a ReactQuery query client and our internal definition of optimistic + * updates, this function constructs an object describing those same optimistic + * updates in a format we can pass into React Query's useMutation hook. In other + * words, it translates our optimistic updates definition into React Query's + * optimistic updates definition. Check their docs for details: + * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates + * + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). + */ +function makeRqOptimisticUpdateOptions( + queryClient: QueryClient, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >[] +): Pick { + async function onMutate(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( + (generalDefinition) => + getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) + ); + + // Cancel any outgoing refetches (so they don't overwrite our optimistic update). + // Theoretically, we can be a bit faster. Instead of awaiting the + // cancellation of all queries, we could cancel and update them in parallel. + // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. + await Promise.all( + specificOptimisticUpdateDefinitions.map(({ queryKey }) => + queryClient.cancelQueries(queryKey) + ) + ); + + // We're using a Map to correctly serialize query keys that contain objects. + const previousData = new Map(); + specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { + // Snapshot the currently cached value. + const previousDataForQuery: CachedData = + queryClient.getQueryData(queryKey); + + // Attempt to optimistically update the cache using the new value. + try { + queryClient.setQueryData(queryKey, updateQuery); + } catch (e) { + console.error( + "The `updateQuery` function threw an exception, skipping optimistic update:" + ); + console.error(e); + } + + // Remember the snapshotted value to restore in case of an error. + previousData.set(queryKey, previousDataForQuery); + }); + + return { previousData }; + } + + function onError(_err, _item, context) { + // All we do in case of an error is roll back all optimistic updates. We ensure + // not to do anything else because React Query rethrows the error. This allows + // the programmer to handle the error as they usually would (i.e., we want the + // error handling to work as it would if the programmer wasn't using optimistic + // updates). + context.previousData.forEach(async (data, queryKey) => { + await queryClient.cancelQueries(queryKey); + queryClient.setQueryData(queryKey, data); + }); + } + + return { + onMutate, + onError, + }; +} + +/** + * Constructs the definition for optimistically updating a specific item. It + * uses a closure over the updated item to construct an item-specific query key + * (e.g., useful when the query key depends on an ID). + * + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic + * update definition with a function for constructing the query key. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. + */ +function getOptimisticUpdateDefinitionForSpecificItem( + optimisticUpdateDefinition: InternalOptimisticUpdateDefinition< + ActionInput, + CachedData + >, + item: ActionInput +): SpecificOptimisticUpdateDefinition { + const { getQueryKey, updateQuery } = optimisticUpdateDefinition; + return { + queryKey: getQueryKey(item), + updateQuery: (old) => updateQuery(item, old), + }; +} + +/** + * Translates a Wasp query specifier to a query cache key used by React Query. + * + * @param querySpecifier A query specifier that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns A cache key React Query internally uses for addressing queries. + */ +function getRqQueryKeyFromSpecifier( + querySpecifier: QuerySpecifier +): QueryKey { + const [queryFn, ...otherKeys] = querySpecifier; + return [...(queryFn as any).queryCacheKey, ...otherKeys]; +} diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/index.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/index.ts index ec9ca9f689..4dc2691035 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/index.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/index.ts @@ -10,7 +10,7 @@ export { useQuery, // PUBLIC API type OptimisticUpdateDefinition, -} from './core' +} from './hooks' export { // PUBLIC API diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/core.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/core.ts index bdc0a9db75..36cd6ad067 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/core.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/core.ts @@ -1,53 +1,83 @@ import { Route } from 'wasp/client' -import type { Expand, _Awaited, _ReturnType } from 'wasp/universal/types' -import { type Query } from '../core.js' +import type { _Awaited, _ReturnType } from 'wasp/universal/types' +import type { + GenericBackendOperation, + GenericOperationRpc, + OperationRpcFor, + Query, + QueryMetadata, +} from '../rpc.js' import { callOperation, makeOperationRoute } from '../internal/index.js' import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources' -export function createQuery( +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey( + query: Query, + payload: Input +): (string | Input)[] { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey +} + +// PRIVATE API (unsed in SDK) +export function createQuery( relativeQueryPath: string, entitiesUsed: string[] ): QueryFor { const queryRoute = makeOperationRoute(relativeQueryPath) + const queryCacheKey = [relativeQueryPath] - async function query(queryKey, queryArgs) { + const queryFn: QueryFunctionFor = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs) - return getActiveOptimisticUpdates(queryKey).reduce( + const queryCacheKey = makeQueryCacheKey(queryFn as QueryFor, queryArgs) + return getActiveOptimisticUpdates(queryCacheKey).reduce( (result, update) => update(result), serverResult, ) } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) - - return query + return buildAndRegisterQuery( + queryFn, + { queryCacheKey, queryRoute, entitiesUsed }, + ) } -// PRIVATE API -export function addMetadataToQuery( - query: (...args: any[]) => Promise, - metadata: { - relativeQueryPath: string - queryRoute: Route - entitiesUsed: string[] - } -): void - -// PRIVATE API -export function addMetadataToQuery( - query, - { relativeQueryPath, queryRoute, entitiesUsed } -) { - query.queryCacheKey = [relativeQueryPath] +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery( + queryFn: QF, + { queryCacheKey, queryRoute, entitiesUsed }: + { queryCacheKey: string[], queryRoute: Route, entitiesUsed: string[] } +): QueryForFunction { + const query = queryFn as QueryForFunction + + query.queryCacheKey = queryCacheKey query.route = queryRoute addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed) + + return query } -export type QueryFor = - Query[0], _Awaited<_ReturnType>> +// PRIVATE API (but should maybe be public, users define values of this type) +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = + QueryForFunction> +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = + OperationRpcFor -type GenericBackendQuery = (args: never, context: any) => unknown \ No newline at end of file +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/index.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/index.ts index eeb2cf5a11..af63c67c63 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/index.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/queries/index.ts @@ -1,4 +1,4 @@ import { type QueryFor, createQuery } from './core' -// PRIVATE API -export { addMetadataToQuery } from './core' +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core' diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/rpc.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/rpc.ts new file mode 100644 index 0000000000..45a8dbbf47 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/operations/rpc.ts @@ -0,0 +1,77 @@ +import { type Route } from "wasp/client"; +import type { + _Awaited, + _ReturnType, +} from "wasp/universal/types" + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type). +// +// Frontend queries are functions with some extra properties (metadata). +// +// To simplify working with the type (i.e., referencing the type's two different +// components), we've defined it as an intersection of two distinct types: +// one representing the function (QueryFunction), and the other representing the +// metadata (QueryMetadata) . +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata + +// PRIVATE API (for SDK, should maybe be public, users define values of this +// type) +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation + +// PRIVATE API (for SDK) +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation + +// PRIVATE API (for SDK) +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[] + route: Route +} + +// PRIVATE API (needed in SDK) +// Explanation: +// - Custom `_Awaited` and `_ReturnType` - Read the comments above their +// definitions. +// - `Parameters extends []` - See here: +// https://github.com/wasp-lang/wasp/pull/1992/files#r1583040080 +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = + Parameters extends [] + ? ClientOperation>> + : ClientOperation< + Parameters[0], + _Awaited<_ReturnType> + > + +// PRIVATE API (needed in SDK) +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown + +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise + +// Read this to understand the type: https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +type ClientOperation = [Input] extends [never] + ? (args?: unknown) => Promise + : (args: Input) => Promise; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx index 8e6085f34c..96ff5e90e8 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/client/test/vitest/helpers.tsx @@ -6,7 +6,7 @@ import { BrowserRouter as Router } from 'react-router-dom' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' -import { Query } from 'wasp/client/operations/core' +import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts index e1708451f8..4b8c455fd4 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/actions/core.d.ts @@ -1,6 +1,3 @@ -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Action } from '../core.js'; -export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; -export type ActionFor = Action[0], _Awaited<_ReturnType>>; -type GenericBackendAction = (args: never, context: any) => unknown; -export {}; +import type { OperationRpcFor, GenericBackendOperation } from '../rpc.js'; +export declare function createAction(relativeActionRoute: string, entitiesUsed: unknown[]): ActionFor; +export type ActionFor = OperationRpcFor; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts deleted file mode 100644 index a23b24ebd6..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { UseQueryResult } from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; -export type Query = { - (queryCacheKey: string[], args: Input): Promise; -}; -export declare function useQuery(queryFn: Query, queryFnArgs?: Input, options?: any): UseQueryResult; -export type Action = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; -/** - * An options object passed into the `useAction` hook and used to enhance the - * action with extra options. - * - */ -export type ActionOptions = { - optimisticUpdates: OptimisticUpdateDefinition[]; -}; -/** - * A documented (public) way to define optimistic updates. - */ -export type OptimisticUpdateDefinition = { - getQuerySpecifier: GetQuerySpecifier; - updateQuery: UpdateQuery; -}; -/** - * A function that takes an item and returns a Wasp Query specifier. - */ -export type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; -/** - * A function that takes an item and the previous state of the cache, and returns - * the desired (new) state of the cache. - */ -export type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; -/** - * A public query specifier used for addressing Wasp queries. See our docs for details: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - */ -export type QuerySpecifier = [Query, ...any[]]; -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js deleted file mode 100644 index 2c94cb01fd..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js +++ /dev/null @@ -1,171 +0,0 @@ -import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; -export { configureQueryClient } from "./queryClient"; -// PUBLIC API -export function useQuery(queryFn, queryFnArgs, options) { - if (typeof queryFn !== "function") { - throw new TypeError("useQuery requires queryFn to be a function."); - } - if (!queryFn.queryCacheKey) { - throw new TypeError("queryFn needs to have queryCacheKey property defined."); - } - const queryKey = queryFnArgs !== undefined - ? [...queryFn.queryCacheKey, queryFnArgs] - : queryFn.queryCacheKey; - return rqUseQuery(Object.assign({ queryKey, queryFn: () => queryFn(queryKey, queryFnArgs) }, options)); -} -// PUBLIC API -/** - * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). - * - * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param actionOptions An options object for enhancing/decorating the given Action. - * @returns A decorated Action with added behavior but an unchanged API. - */ -export function useAction(actionFn, actionOptions) { - const queryClient = useQueryClient(); - let mutationFn = actionFn; - let options = {}; - if (actionOptions === null || actionOptions === void 0 ? void 0 : actionOptions.optimisticUpdates) { - const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map(translateToInternalDefinition); - mutationFn = makeOptimisticUpdateMutationFn(actionFn, optimisticUpdatesDefinitions); - options = makeRqOptimisticUpdateOptions(queryClient, optimisticUpdatesDefinitions); - } - // NOTE: We decided to hide React Query's extra mutation features (e.g., - // isLoading, onSuccess and onError callbacks, synchronous mutate) and only - // expose a simple async function whose API matches the original Action. - // We did this to avoid cluttering the API with stuff we're not sure we need - // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to - // clearly separate our opinionated API from React Query's lower-level - // advanced API (which users can also use) - const mutation = useMutation(mutationFn, options); - return (args) => mutation.mutateAsync(args); -} -/** - * Translates/Desugars a public optimistic update definition object into a - * definition object our system uses internally. - * - * @param publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns An internally-used optimistic update definition object. - */ -function translateToInternalDefinition(publicOptimisticUpdateDefinition) { - const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; - const definitionErrors = []; - if (typeof getQuerySpecifier !== "function") { - definitionErrors.push("`getQuerySpecifier` is not a function."); - } - if (typeof updateQuery !== "function") { - definitionErrors.push("`updateQuery` is not a function."); - } - if (definitionErrors.length) { - throw new TypeError(`Invalid optimistic update definition: ${definitionErrors.join(", ")}.`); - } - return { - getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), - updateQuery, - }; -} -/** - * Creates a function that performs an action while telling it about the - * optimistic updates it caused. - * - * @param actionFn The Wasp Action. - * @param optimisticUpdateDefinitions The optimisitc updates the action causes. - * @returns An decorated action which performs optimistic updates. - */ -function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { - return function performActionWithOptimisticUpdates(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); - return actionFn.internal(item, specificOptimisticUpdateDefinitions); - }; -} -/** - * Given a ReactQuery query client and our internal definition of optimistic - * updates, this function constructs an object describing those same optimistic - * updates in a format we can pass into React Query's useMutation hook. In other - * words, it translates our optimistic updates definition into React Query's - * optimistic updates definition. Check their docs for details: - * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates - * - * @param queryClient The QueryClient instance used by React Query. - * @param optimisticUpdateDefinitions A list containing internal optimistic - * updates definition objects (i.e., a list where each object carries the - * instructions for performing particular optimistic update). - * @returns An object containing 'onMutate' and 'onError' functions - * corresponding to the given optimistic update definitions (check the docs - * linked above for details). - */ -function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) { - async function onMutate(item) { - const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); - // Cancel any outgoing refetches (so they don't overwrite our optimistic update). - // Theoretically, we can be a bit faster. Instead of awaiting the - // cancellation of all queries, we could cancel and update them in parallel. - // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. - await Promise.all(specificOptimisticUpdateDefinitions.map(({ queryKey }) => queryClient.cancelQueries(queryKey))); - // We're using a Map to correctly serialize query keys that contain objects. - const previousData = new Map(); - specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { - // Snapshot the currently cached value. - const previousDataForQuery = queryClient.getQueryData(queryKey); - // Attempt to optimistically update the cache using the new value. - try { - queryClient.setQueryData(queryKey, updateQuery); - } - catch (e) { - console.error("The `updateQuery` function threw an exception, skipping optimistic update:"); - console.error(e); - } - // Remember the snapshotted value to restore in case of an error. - previousData.set(queryKey, previousDataForQuery); - }); - return { previousData }; - } - function onError(_err, _item, context) { - // All we do in case of an error is roll back all optimistic updates. We ensure - // not to do anything else because React Query rethrows the error. This allows - // the programmer to handle the error as they usually would (i.e., we want the - // error handling to work as it would if the programmer wasn't using optimistic - // updates). - context.previousData.forEach(async (data, queryKey) => { - await queryClient.cancelQueries(queryKey); - queryClient.setQueryData(queryKey, data); - }); - } - return { - onMutate, - onError, - }; -} -/** - * Constructs the definition for optimistically updating a specific item. It - * uses a closure over the updated item to construct an item-specific query key - * (e.g., useful when the query key depends on an ID). - * - * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic - * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the - * argument passed to the Action). - * @returns A specific optimistic update definition which corresponds to the - * provided definition and closes over the provided item. - */ -function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition, item) { - const { getQueryKey, updateQuery } = optimisticUpdateDefinition; - return { - queryKey: getQueryKey(item), - updateQuery: (old) => updateQuery(item, old), - }; -} -/** - * Translates a Wasp query specifier to a query cache key used by React Query. - * - * @param querySpecifier A query specifier that's a part of the public API: - * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns A cache key React Query internally uses for addressing queries. - */ -function getRqQueryKeyFromSpecifier(querySpecifier) { - const [queryFn, ...otherKeys] = querySpecifier; - return [...queryFn.queryCacheKey, ...otherKeys]; -} -//# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js.map deleted file mode 100644 index 20d72dc15e..0000000000 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../client/operations/core.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts new file mode 100644 index 0000000000..36a5e0b65b --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.d.ts @@ -0,0 +1,41 @@ +import { UseQueryResult } from "@tanstack/react-query"; +import { Action, Query } from "./rpc"; +export { configureQueryClient } from "./queryClient"; +export declare function useQuery(query: Query, queryFnArgs?: Input, options?: any): UseQueryResult; +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export declare function useAction(actionFn: Action, actionOptions?: ActionOptions): typeof actionFn; +/** + * A documented (public) way to define optimistic updates. + */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +}; +/** + * An options object passed into the `useAction` hook and used to enhance the + * action with extra options. + * + */ +type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +}; +/** + * A function that takes an item and returns a Wasp Query specifier. + */ +type GetQuerySpecifier = (item: ActionInput) => QuerySpecifier; +/** + * A function that takes an item and the previous state of the cache, and returns + * the desired (new) state of the cache. + */ +type UpdateQuery = (item: ActionInput, oldData: CachedData | undefined) => CachedData; +/** + * A public query specifier used for addressing Wasp queries. See our docs for details: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + */ +type QuerySpecifier = [Query, ...any[]]; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js new file mode 100644 index 0000000000..95ab379515 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js @@ -0,0 +1,169 @@ +import { useMutation, useQueryClient, useQuery as rqUseQuery, } from "@tanstack/react-query"; +import { makeQueryCacheKey } from "./queries/core"; +export { configureQueryClient } from "./queryClient"; +// PUBLIC API +export function useQuery(query, queryFnArgs, options) { + if (typeof query !== 'function') { + throw new TypeError('useQuery requires queryFn to be a function.'); + } + if (!query.queryCacheKey) { + throw new TypeError('queryFn needs to have queryCacheKey property defined.'); + } + return rqUseQuery(Object.assign({ queryKey: makeQueryCacheKey(query, queryFnArgs), queryFn: () => query(queryFnArgs) }, options)); +} +// PUBLIC API +/** + * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). + * + * @param actionFn The Wasp Action you wish to enhance/decorate. + * @param actionOptions An options object for enhancing/decorating the given Action. + * @returns A decorated Action with added behavior but an unchanged API. + */ +export function useAction(actionFn, actionOptions) { + const queryClient = useQueryClient(); + let mutationFn = actionFn; + let options = {}; + if (actionOptions === null || actionOptions === void 0 ? void 0 : actionOptions.optimisticUpdates) { + const optimisticUpdatesDefinitions = actionOptions.optimisticUpdates.map(translateToInternalDefinition); + mutationFn = makeOptimisticUpdateMutationFn(actionFn, optimisticUpdatesDefinitions); + options = makeRqOptimisticUpdateOptions(queryClient, optimisticUpdatesDefinitions); + } + // NOTE: We decided to hide React Query's extra mutation features (e.g., + // isLoading, onSuccess and onError callbacks, synchronous mutate) and only + // expose a simple async function whose API matches the original Action. + // We did this to avoid cluttering the API with stuff we're not sure we need + // yet (e.g., isLoading), to postpone the action vs mutation dilemma, and to + // clearly separate our opinionated API from React Query's lower-level + // advanced API (which users can also use) + const mutation = useMutation(mutationFn, options); + return (args) => mutation.mutateAsync(args); +} +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition(publicOptimisticUpdateDefinition) { + const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition; + const definitionErrors = []; + if (typeof getQuerySpecifier !== "function") { + definitionErrors.push("`getQuerySpecifier` is not a function."); + } + if (typeof updateQuery !== "function") { + definitionErrors.push("`updateQuery` is not a function."); + } + if (definitionErrors.length) { + throw new TypeError(`Invalid optimistic update definition: ${definitionErrors.join(", ")}.`); + } + return { + getQueryKey: (item) => getRqQueryKeyFromSpecifier(getQuerySpecifier(item)), + updateQuery, + }; +} +/** + * Creates a function that performs an action while telling it about the + * optimistic updates it caused. + * + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. + */ +function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { + return function performActionWithOptimisticUpdates(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); + return actionFn.internal(item, specificOptimisticUpdateDefinitions); + }; +} +/** + * Given a ReactQuery query client and our internal definition of optimistic + * updates, this function constructs an object describing those same optimistic + * updates in a format we can pass into React Query's useMutation hook. In other + * words, it translates our optimistic updates definition into React Query's + * optimistic updates definition. Check their docs for details: + * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates + * + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). + */ +function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) { + async function onMutate(item) { + const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map((generalDefinition) => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item)); + // Cancel any outgoing refetches (so they don't overwrite our optimistic update). + // Theoretically, we can be a bit faster. Instead of awaiting the + // cancellation of all queries, we could cancel and update them in parallel. + // However, awaiting cancellation hasn't yet proven to be a performance bottleneck. + await Promise.all(specificOptimisticUpdateDefinitions.map(({ queryKey }) => queryClient.cancelQueries(queryKey))); + // We're using a Map to correctly serialize query keys that contain objects. + const previousData = new Map(); + specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { + // Snapshot the currently cached value. + const previousDataForQuery = queryClient.getQueryData(queryKey); + // Attempt to optimistically update the cache using the new value. + try { + queryClient.setQueryData(queryKey, updateQuery); + } + catch (e) { + console.error("The `updateQuery` function threw an exception, skipping optimistic update:"); + console.error(e); + } + // Remember the snapshotted value to restore in case of an error. + previousData.set(queryKey, previousDataForQuery); + }); + return { previousData }; + } + function onError(_err, _item, context) { + // All we do in case of an error is roll back all optimistic updates. We ensure + // not to do anything else because React Query rethrows the error. This allows + // the programmer to handle the error as they usually would (i.e., we want the + // error handling to work as it would if the programmer wasn't using optimistic + // updates). + context.previousData.forEach(async (data, queryKey) => { + await queryClient.cancelQueries(queryKey); + queryClient.setQueryData(queryKey, data); + }); + } + return { + onMutate, + onError, + }; +} +/** + * Constructs the definition for optimistically updating a specific item. It + * uses a closure over the updated item to construct an item-specific query key + * (e.g., useful when the query key depends on an ID). + * + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic + * update definition with a function for constructing the query key. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. + */ +function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition, item) { + const { getQueryKey, updateQuery } = optimisticUpdateDefinition; + return { + queryKey: getQueryKey(item), + updateQuery: (old) => updateQuery(item, old), + }; +} +/** + * Translates a Wasp query specifier to a query cache key used by React Query. + * + * @param querySpecifier A query specifier that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns A cache key React Query internally uses for addressing queries. + */ +function getRqQueryKeyFromSpecifier(querySpecifier) { + const [queryFn, ...otherKeys] = querySpecifier; + return [...queryFn.queryCacheKey, ...otherKeys]; +} +//# sourceMappingURL=hooks.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map new file mode 100644 index 0000000000..9048c71ee2 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/hooks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../client/operations/hooks.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts index 301165fa8e..76ad094f0d 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.d.ts @@ -1,4 +1,4 @@ export * from './actions'; export * from './queries'; -export { useAction, useQuery, type OptimisticUpdateDefinition, } from './core'; +export { useAction, useQuery, type OptimisticUpdateDefinition, } from './hooks'; export { configureQueryClient, initializeQueryClient, queryClientInitialized } from './queryClient'; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js index f83307c7b1..fd2c6b29b0 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js @@ -6,7 +6,7 @@ export { // PUBLIC API useAction, // PUBLIC API -useQuery, } from './core'; +useQuery, } from './hooks'; export { // PUBLIC API configureQueryClient, diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js.map index 982c957a9a..a6cf9161ab 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,QAAQ,CAAA;AAEf,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../client/operations/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,cAAc,WAAW,CAAA;AACzB,+CAA+C;AAC/C,cAAc,WAAW,CAAA;AAEzB,OAAO;AACH,aAAa;AACb,SAAS;AACT,aAAa;AACb,QAAQ,GAGX,MAAM,SAAS,CAAA;AAEhB,OAAO;AACH,aAAa;AACb,oBAAoB;AACpB,+BAA+B;AAC/B,qBAAqB;AACrB,+BAA+B;AAC/B,sBAAsB,EACzB,MAAM,eAAe,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts index 93bbd1ddaa..c70de80ba6 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.d.ts @@ -1,12 +1,25 @@ import { Route } from 'wasp/client'; -import type { _Awaited, _ReturnType } from 'wasp/universal/types'; -import { type Query } from '../core.js'; -export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; -export declare function addMetadataToQuery(query: (...args: any[]) => Promise, metadata: { - relativeQueryPath: string; +import type { GenericBackendOperation, GenericOperationRpc, OperationRpcFor, Query, QueryMetadata } from '../rpc.js'; +export declare function makeQueryCacheKey(query: Query, payload: Input): (string | Input)[]; +export declare function createQuery(relativeQueryPath: string, entitiesUsed: string[]): QueryFor; +export declare function buildAndRegisterQuery(queryFn: QF, { queryCacheKey, queryRoute, entitiesUsed }: { + queryCacheKey: string[]; queryRoute: Route; entitiesUsed: string[]; -}): void; -export type QueryFor = Query[0], _Awaited<_ReturnType>>; -type GenericBackendQuery = (args: never, context: any) => unknown; +}): QueryForFunction; +/** + * Constructs the client Query object type from the type of the Query's definition + * on the backend. + */ +export type QueryFor = QueryForFunction>; +/** + * Constructs the client Query function type from the type of the Query's + * definition on the backend. + */ +type QueryFunctionFor = OperationRpcFor; +/** + * Returns the appropriate client Query object type for the provided client + * Query function type. + */ +type QueryForFunction = QF & QueryMetadata; export {}; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js index c03f172443..fefdfc58fc 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js @@ -1,18 +1,28 @@ import { callOperation, makeOperationRoute } from '../internal/index.js'; import { addResourcesUsedByQuery, getActiveOptimisticUpdates, } from '../internal/resources'; +// PRIVATE API (used in the SDK) +export function makeQueryCacheKey(query, payload) { + return payload !== undefined ? + [...query.queryCacheKey, payload] + : query.queryCacheKey; +} +// PRIVATE API (unsed in SDK) export function createQuery(relativeQueryPath, entitiesUsed) { const queryRoute = makeOperationRoute(relativeQueryPath); - async function query(queryKey, queryArgs) { + const queryCacheKey = [relativeQueryPath]; + const queryFn = async (queryArgs) => { const serverResult = await callOperation(queryRoute, queryArgs); - return getActiveOptimisticUpdates(queryKey).reduce((result, update) => update(result), serverResult); - } - addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }); - return query; + const queryCacheKey = makeQueryCacheKey(queryFn, queryArgs); + return getActiveOptimisticUpdates(queryCacheKey).reduce((result, update) => update(result), serverResult); + }; + return buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }); } -// PRIVATE API -export function addMetadataToQuery(query, { relativeQueryPath, queryRoute, entitiesUsed }) { - query.queryCacheKey = [relativeQueryPath]; +// PRIVATE API (used in SDK) +export function buildAndRegisterQuery(queryFn, { queryCacheKey, queryRoute, entitiesUsed }) { + const query = queryFn; + query.queryCacheKey = queryCacheKey; query.route = queryRoute; addResourcesUsedByQuery(query.queryCacheKey, entitiesUsed); + return query; } //# sourceMappingURL=core.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map index dcc40d2693..0ef0547c44 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/core.js.map @@ -1 +1 @@ -{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IAExD,KAAK,UAAU,KAAK,CAAC,QAAQ,EAAE,SAAS;QACtC,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,OAAO,0BAA0B,CAAC,QAAQ,CAAC,CAAC,MAAM,CAChD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC;IAED,kBAAkB,CAAC,KAAK,EAAE,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAA;IAE1E,OAAO,KAAK,CAAA;AACd,CAAC;AAYD,cAAc;AACd,MAAM,UAAU,kBAAkB,CAChC,KAAK,EACL,EAAE,iBAAiB,EAAE,UAAU,EAAE,YAAY,EAAE;IAE/C,KAAK,CAAC,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IACzC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;AAC5D,CAAC"} \ No newline at end of file +{"version":3,"file":"core.js","sourceRoot":"","sources":["../../../../client/operations/queries/core.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACxE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,uBAAuB,CAAA;AAE9B,gCAAgC;AAChC,MAAM,UAAU,iBAAiB,CAC/B,KAA2B,EAC3B,OAAc;IAEd,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;QAC5B,CAAC,GAAG,KAAK,CAAC,aAAa,EAAE,OAAO,CAAC;QAC/B,CAAC,CAAC,KAAK,CAAC,aAAa,CAAA;AAC3B,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,WAAW,CACzB,iBAAyB,EACzB,YAAsB;IAEtB,MAAM,UAAU,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;IACxD,MAAM,aAAa,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAEzC,MAAM,OAAO,GAAmC,KAAK,EAAE,SAAS,EAAE,EAAE;QAClE,MAAM,YAAY,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAC/D,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAiC,EAAE,SAAS,CAAC,CAAA;QACrF,OAAO,0BAA0B,CAAC,aAAa,CAAC,CAAC,MAAM,CACrD,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,EAClC,YAAY,CACb,CAAA;IACH,CAAC,CAAA;IAED,OAAO,qBAAqB,CAC1B,OAAO,EACP,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAAE,CAC5C,CAAA;AACH,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,qBAAqB,CACnC,OAAW,EACX,EAAE,aAAa,EAAE,UAAU,EAAE,YAAY,EAC6B;IAEtE,MAAM,KAAK,GAAG,OAA+B,CAAA;IAE7C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAA;IACnC,KAAK,CAAC,KAAK,GAAG,UAAU,CAAA;IACxB,uBAAuB,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAA;IAE1D,OAAO,KAAK,CAAA;AACd,CAAC"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts index 575c502be1..2d9dbafe81 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.d.ts @@ -1 +1 @@ -export { addMetadataToQuery } from './core'; +export { buildAndRegisterQuery } from './core'; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js index 1c28e8d0d3..56d2e15238 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js @@ -1,3 +1,3 @@ -// PRIVATE API -export { addMetadataToQuery } from './core'; +// PRIVATE API (used in SDK) +export { buildAndRegisterQuery } from './core'; //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map index 5c83611fa3..44416efcfa 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/queries/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../client/operations/queries/index.ts"],"names":[],"mappings":"AAEA,4BAA4B;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,QAAQ,CAAA"} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts new file mode 100644 index 0000000000..ee52ec65b4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.d.ts @@ -0,0 +1,38 @@ +import { type Route } from "wasp/client"; +import type { _Awaited, _ReturnType } from "wasp/universal/types"; +/** + * The client Query object type. It's a callable Query function with some extra + * properties (metadata). + */ +export type Query = QueryFunction & QueryMetadata; +/** + * The client Action object type (unlike a Query, it's just a normal function). + */ +export type Action = ClientOperation; +/** + * The client Query function type. + */ +export type QueryFunction = ClientOperation; +/** + * All extra properties (metadata) found on a Query object type. + */ +export type QueryMetadata = { + queryCacheKey: string[]; + route: Route; +}; +/** + * Constructs the client RPC function type from the type of the operation's + * definition on the backend. + */ +export type OperationRpcFor = Parameters extends [] ? ClientOperation>> : ClientOperation[0], _Awaited<_ReturnType>>; +/** + * A supertype of all possible backend operation definitions (i.e., Queries and + * Actions) + */ +export type GenericBackendOperation = (args: never, context: any) => unknown; +/** + * A supertype of all possible frontend RPC function types. + */ +export type GenericOperationRpc = (args: never) => Promise; +type ClientOperation = [Input] extends [never] ? (args?: unknown) => Promise : (args: Input) => Promise; +export {}; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js new file mode 100644 index 0000000000..f3a500abe4 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=rpc.js.map \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map new file mode 100644 index 0000000000..a51bf309e5 --- /dev/null +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/operations/rpc.js.map @@ -0,0 +1 @@ +{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../../client/operations/rpc.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts index 645112861a..89fd0d7587 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/dist/client/test/vitest/helpers.d.ts @@ -1,7 +1,7 @@ import { ReactElement } from 'react'; import { type SetupServer } from 'msw/node'; import { RenderResult } from '@testing-library/react'; -import { Query } from 'wasp/client/operations/core'; +import { Query } from 'wasp/client/operations/rpc'; import { Route } from 'wasp/client'; export type MockQuery = (query: Query, resJson: MockOutput) => void; export type MockApi = (route: Route, resJson: unknown) => void; diff --git a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json index e3a3bb5440..73e355a456 100644 --- a/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json +++ b/waspc/e2e-test/test-outputs/waspMigrate-golden/waspMigrate/.wasp/out/sdk/wasp/package.json @@ -41,7 +41,7 @@ "./client/auth": "./dist/client/auth/index.js", "./client/crud": "./dist/client/crud/index.js", "./client/operations": "./dist/client/operations/index.js", - "./client/operations/core": "./dist/client/operations/core.js", + "./client/operations/rpc": "./dist/client/operations/rpc.js", "./client/router": "./dist/client/router/index.js", "./client/test": "./dist/client/test/index.js", "./client/test/*": "./dist/client/test/*.js", diff --git a/waspc/examples/todoApp/main.wasp b/waspc/examples/todoApp/main.wasp index 46cb5ce3c2..1df9847ed5 100644 --- a/waspc/examples/todoApp/main.wasp +++ b/waspc/examples/todoApp/main.wasp @@ -5,18 +5,18 @@ app todoApp { title: "ToDo App", // head: [], webSocket: { - fn: import { webSocketFn } from "@src/webSocket.js", + fn: import { webSocketFn } from "@src/webSocket", // autoConnect: false }, auth: { userEntity: User, methods: { // usernameAndPassword: { - // userSignupFields: import { userSignupFields } from "@src/auth/github.js", + // userSignupFields: import { userSignupFields } from "@src/auth/github", // }, google: { - configFn: import { config } from "@src/auth/google.js", - userSignupFields: import { userSignupFields } from "@src/auth/google.js" + configFn: import { config } from "@src/auth/google", + userSignupFields: import { userSignupFields } from "@src/auth/google" }, gitHub: { configFn: import { config } from "@src/auth/github.js", @@ -24,17 +24,17 @@ app todoApp { }, // keycloak: {}, email: { - userSignupFields: import { userSignupFields } from "@src/auth/email.js", + userSignupFields: import { userSignupFields } from "@src/auth/email", fromField: { name: "ToDO App", email: "mihovil@ilakovac.com" }, emailVerification: { - getEmailContentFn: import { getVerificationEmailContent } from "@src/auth/email.js", + getEmailContentFn: import { getVerificationEmailContent } from "@src/auth/email", clientRoute: EmailVerificationRoute, }, passwordReset: { - getEmailContentFn: import { getPasswordResetEmailContent } from "@src/auth/email.js", + getEmailContentFn: import { getPasswordResetEmailContent } from "@src/auth/email", clientRoute: PasswordResetRoute }, }, @@ -43,18 +43,18 @@ app todoApp { onAuthSucceededRedirectTo: "/profile" }, server: { - setupFn: import setup from "@src/serverSetup.js", - middlewareConfigFn: import { serverMiddlewareFn } from "@src/serverSetup.js", + setupFn: import setup from "@src/serverSetup", + middlewareConfigFn: import { serverMiddlewareFn } from "@src/serverSetup", }, client: { - rootComponent: import { App } from "@src/App.tsx", - setupFn: import setup from "@src/clientSetup.js" + rootComponent: import { App } from "@src/App", + setupFn: import setup from "@src/clientSetup" }, db: { system: PostgreSQL, seeds: [ - import { devSeedSimple } from "@src/dbSeeds.js", - import { prodSeed } from "@src/dbSeeds.js" + import { devSeedSimple } from "@src/dbSeeds", + import { prodSeed } from "@src/dbSeeds" ] }, emailSender: { @@ -82,44 +82,44 @@ psl=} route SignupRoute { path: "/signup", to: SignupPage } page SignupPage { - component: import Signup from "@src/pages/auth/Signup.tsx" + component: import Signup from "@src/pages/auth/Signup" } route LoginRoute { path: "/login", to: LoginPage } page LoginPage { - component: import Login from "@src/pages/auth/Login.tsx" + component: import Login from "@src/pages/auth/Login" } route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage } page PasswordResetPage { - component: import { PasswordReset } from "@src/pages/auth/PasswordReset.tsx", + component: import { PasswordReset } from "@src/pages/auth/PasswordReset", } route EmailVerificationRoute { path: "/email-verification-", to: EmailVerificationPage } page EmailVerificationPage { - component: import { EmailVerification } from "@src/pages/auth/EmailVerification.tsx", + component: import { EmailVerification } from "@src/pages/auth/EmailVerification", } route RequestPasswordResetRoute { path: "/request-password-reset", to: RequestPasswordResetPage } page RequestPasswordResetPage { - component: import { RequestPasswordReset } from "@src/pages/auth/RequestPasswordReset.tsx", + component: import { RequestPasswordReset } from "@src/pages/auth/RequestPasswordReset", } route HomeRoute { path: "/", to: MainPage } page MainPage { authRequired: true, - component: import Main from "@src/pages/Main.jsx" + component: import Main from "@src/pages/Main" } route AboutRoute { path: "/about", to: AboutPage } page AboutPage { - component: import About from "@src/pages/About.jsx" + component: import About from "@src/pages/About" } route ProfileRoute { path: "/profile", to: ProfilePage } page ProfilePage { authRequired: true, - component: import { ProfilePage } from "@src/pages/ProfilePage.tsx" + component: import { ProfilePage } from "@src/pages/ProfilePage" } // Page for viewing a specific task @@ -127,84 +127,95 @@ page ProfilePage { route TaskRoute { path: "/task/:id", to: TaskPage } page TaskPage { authRequired: true, - component: import Task from "@src/pages/Task.tsx" + component: import Task from "@src/pages/Task" } // --------- Queries --------- // query getTasks { - fn: import { getTasks } from "@src/queries.js", + fn: import { getTasks } from "@src/queries", entities: [Task] } api fooBar { - fn: import { fooBar } from "@src/apis.js", - middlewareConfigFn: import { fooBarMiddlewareFn } from "@src/apis.js", + fn: import { fooBar } from "@src/apis", + middlewareConfigFn: import { fooBarMiddlewareFn } from "@src/apis", entities: [Task], // ALL here let's our CORS work. If we did GET, we would need an apiNamespace over it with CORS. httpRoute: (ALL, "/foo/bar") } apiNamespace bar { - middlewareConfigFn: import { barNamespaceMiddlewareFn } from "@src/apis.js", + middlewareConfigFn: import { barNamespaceMiddlewareFn } from "@src/apis", path: "/bar" } api barBaz { - fn: import { barBaz } from "@src/apis.js", + fn: import { barBaz } from "@src/apis", auth: false, entities: [Task], httpRoute: (GET, "/bar/baz") } api webhookCallback { - fn: import { webhookCallback } from "@src/apis.js", - middlewareConfigFn: import { webhookCallbackMiddlewareFn } from "@src/apis.js", + fn: import { webhookCallback } from "@src/apis", + middlewareConfigFn: import { webhookCallbackMiddlewareFn } from "@src/apis", httpRoute: (POST, "/webhook/callback"), auth: false } query getNumTasks { - fn: import { getNumTasks } from "@src/queries.js", + fn: import { getNumTasks } from "@src/queries", entities: [Task], auth: false } query getTask { - fn: import { getTask } from "@src/queries.js", + fn: import { getTask } from "@src/queries", entities: [Task] } query getDate { - fn: import { getDate } from "@src/queries.js" + fn: import { getDate } from "@src/queries" } +query getAnything { + fn: import { getAnything } from "@src/queries", + entities: [] +} + +query getTrueVoid { + fn: import { getTrueVoid } from "@src/queries", + entities: [] +} + + // --------- Actions --------- // action createTask { - fn: import { createTask } from "@src/actions.js", + fn: import { createTask } from "@src/actions", entities: [Task] } action updateTaskIsDone { - fn: import { updateTaskIsDone } from "@src/actions.js", + fn: import { updateTaskIsDone } from "@src/actions", entities: [Task] } action deleteCompletedTasks { - fn: import { deleteCompletedTasks } from "@src/actions.js", + fn: import { deleteCompletedTasks } from "@src/actions", entities: [Task] } action toggleAllTasks { - fn: import { toggleAllTasks } from "@src/actions.js", + fn: import { toggleAllTasks } from "@src/actions", entities: [Task] } job mySpecialJob { executor: PgBoss, perform: { - fn: import { foo } from "@src/jobs/bar.js", + fn: import { foo } from "@src/jobs/bar", executorOptions: { pgBoss: {=json { "retryLimit": 1 } json=} } @@ -215,7 +226,7 @@ job mySpecialJob { job mySpecialScheduledJob { executor: PgBoss, perform: { - fn: import { foo } from "@src/jobs/bar.js" + fn: import { foo } from "@src/jobs/bar" }, schedule: { cron: "0 * * * *", diff --git a/waspc/examples/todoApp/src/App.tsx b/waspc/examples/todoApp/src/App.tsx index 3efa385f17..4ea746fca3 100644 --- a/waspc/examples/todoApp/src/App.tsx +++ b/waspc/examples/todoApp/src/App.tsx @@ -1,10 +1,12 @@ -import { useSocket } from "wasp/client/webSocket"; -import { Link } from "wasp/client/router"; -import { logout, useAuth } from "wasp/client/auth"; -import { useQuery, getDate } from "wasp/client/operations"; +import { useSocket } from 'wasp/client/webSocket' +import { Link } from 'wasp/client/router' +import { logout, useAuth } from 'wasp/client/auth' +import { useQuery, getDate } from 'wasp/client/operations' import './Main.css' import { getName } from './user' +// Necessary to trigger type tests. +import './testTypes' export function App({ children }: any) { const { data: user } = useAuth() diff --git a/waspc/examples/todoApp/src/Todo.tsx b/waspc/examples/todoApp/src/Todo.tsx index 3ac64a280e..b2f9ca6bd1 100644 --- a/waspc/examples/todoApp/src/Todo.tsx +++ b/waspc/examples/todoApp/src/Todo.tsx @@ -1,5 +1,5 @@ -import { Link } from "wasp/client/router"; -import { type Task } from "wasp/entities"; +import { Link } from 'wasp/client/router' +import { type Task } from 'wasp/entities' import { useAction, @@ -10,12 +10,34 @@ import { toggleAllTasks, useQuery, getTasks, -} from "wasp/client/operations"; + getTask, + getDate, + getAnything, +} from 'wasp/client/operations' import React, { useState, FormEventHandler, ChangeEventHandler } from 'react' type NonEmptyArray = [T, ...T[]] +async function logAll() { + const tasks = await getTasks() + console.info('Got tasks:', tasks) + + const someId = tasks.map((task) => task.id).find((id) => id) + if (!someId) { + console.info('No tasks found') + } else { + const task = await getTask({ id: someId }) + console.info(`Got task with id ${someId}`, task) + } + + const date = await getDate() + console.info('Got date:', date) + + const anything = await getAnything() + console.info('Got anything:', anything) +} + export function areThereAnyTasks( tasks: Task[] | undefined ): tasks is NonEmptyArray { @@ -23,6 +45,7 @@ export function areThereAnyTasks( } const Todo = () => { + logAll() const { data: tasks, isError, error: tasksError } = useQuery(getTasks) const TasksError = () => { diff --git a/waspc/examples/todoApp/src/actions.ts b/waspc/examples/todoApp/src/actions.ts index 995916333f..7d1e569208 100644 --- a/waspc/examples/todoApp/src/actions.ts +++ b/waspc/examples/todoApp/src/actions.ts @@ -1,14 +1,14 @@ -import { type Task } from "wasp/entities"; -import { HttpError } from "wasp/server"; +import { type Task } from 'wasp/entities' +import { HttpError } from 'wasp/server' import { type CreateTask, type DeleteCompletedTasks, type ToggleAllTasks, type UpdateTaskIsDone, -} from "wasp/server/operations"; +} from 'wasp/server/operations' import { getSomeResource } from './serverSetup.js' -export const createTask: CreateTask> = async ( +export const createTask: CreateTask, Task> = async ( task, context ) => { @@ -29,7 +29,7 @@ export const createTask: CreateTask> = async ( console.log( 'New task created! Btw, current value of someResource is: ' + - getSomeResource() + getSomeResource() ) return newTask @@ -43,8 +43,8 @@ export const updateTaskIsDone: UpdateTaskIsDone< } // Uncomment to test optimistic updates - // const sleep = (ms) => new Promise(res => setTimeout(res, ms)) - // await sleep(3000); + const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)) + await sleep(5000) const Task = context.entities.Task const updateResult = await Task.updateMany({ diff --git a/waspc/examples/todoApp/src/pages/Main.jsx b/waspc/examples/todoApp/src/pages/Main.tsx similarity index 100% rename from waspc/examples/todoApp/src/pages/Main.jsx rename to waspc/examples/todoApp/src/pages/Main.tsx diff --git a/waspc/examples/todoApp/src/pages/Task.tsx b/waspc/examples/todoApp/src/pages/Task.tsx index a3e4bbc147..303aee59fb 100644 --- a/waspc/examples/todoApp/src/pages/Task.tsx +++ b/waspc/examples/todoApp/src/pages/Task.tsx @@ -1,5 +1,5 @@ -import { Link } from "wasp/client/router"; -import { type Task } from "wasp/entities"; +import { Link } from 'wasp/client/router' +import { type Task } from 'wasp/entities' import { useAction, @@ -8,7 +8,7 @@ import { useQuery, getTask, getTasks, -} from "wasp/client/operations"; +} from 'wasp/client/operations' import React from 'react' @@ -17,7 +17,12 @@ type TaskPayload = Pick const Todo = (props: any) => { const taskId = parseInt(props.match.params.id) - const { data: task, isFetching, error, isError } = useQuery(getTask, { id: taskId }) + const { + data: task, + isFetching, + error, + isError, + } = useQuery(getTask, { id: taskId }) const updateTaskIsDoneOptimistically = useAction(updateTaskIsDone, { optimisticUpdates: [ @@ -37,8 +42,8 @@ const Todo = (props: any) => { ], }) - if (!task) return
Task with id {taskId} does not exist.
; - if (isError) return
Error occurred! {error.message}
; + if (!task) return
Task with id {taskId} does not exist.
+ if (isError) return
Error occurred! {error.message}
async function toggleIsDone({ id, isDone }: Task) { try { diff --git a/waspc/examples/todoApp/src/queries.ts b/waspc/examples/todoApp/src/queries.ts index d88aedb431..c53a5c40e9 100644 --- a/waspc/examples/todoApp/src/queries.ts +++ b/waspc/examples/todoApp/src/queries.ts @@ -1,6 +1,13 @@ -import { type Task } from "wasp/entities"; -import { HttpError } from "wasp/server"; -import { type GetNumTasks, type GetTask, type GetTasks, type GetDate } from "wasp/server/operations"; +import { type Task } from 'wasp/entities' +import { HttpError } from 'wasp/server' +import type { + GetNumTasks, + GetTask, + GetTasks, + GetDate, + GetAnything, + GetTrueVoid, +} from 'wasp/server/operations' export const getTasks: GetTasks = async (_args, context) => { if (!context.user) { @@ -10,20 +17,24 @@ export const getTasks: GetTasks = async (_args, context) => { console.log('TEST_ENV_VAR', process.env.TEST_ENV_VAR) const Task = context.entities.Task - const tasks = await Task.findMany( - { - where: { user: { id: context.user.id } }, - orderBy: { id: 'asc' }, - } - ) + const tasks = await Task.findMany({ + where: { user: { id: context.user.id } }, + orderBy: { id: 'asc' }, + }) return tasks } -export const getNumTasks: GetNumTasks = async (_args, context) => { +export const getNumTasks: GetNumTasks = async ( + _args, + context +) => { return context.entities.Task.count() } -export const getTask: GetTask, Task> = async (where, context) => { +export const getTask: GetTask, Task> = async ( + where, + context +) => { if (!context.user) { throw new HttpError(401) } @@ -47,3 +58,11 @@ export const getTask: GetTask, Task> = async (where, context) = export const getDate: GetDate = async () => { return new Date() } + +export const getAnything: GetAnything = async () => { + return 'anything' +} + +export const getTrueVoid = (async () => { + return 'anything' +}) satisfies GetTrueVoid diff --git a/waspc/examples/todoApp/src/testTypes/helpers.ts b/waspc/examples/todoApp/src/testTypes/helpers.ts new file mode 100644 index 0000000000..58afbbd328 --- /dev/null +++ b/waspc/examples/todoApp/src/testTypes/helpers.ts @@ -0,0 +1,6 @@ +export type Assert = T +export type AreEqual = T extends Expected + ? Expected extends T + ? true + : false + : false diff --git a/waspc/examples/todoApp/src/testTypes/index.ts b/waspc/examples/todoApp/src/testTypes/index.ts new file mode 100644 index 0000000000..200ef226de --- /dev/null +++ b/waspc/examples/todoApp/src/testTypes/index.ts @@ -0,0 +1 @@ +import './rpc' diff --git a/waspc/examples/todoApp/src/testTypes/rpc.ts b/waspc/examples/todoApp/src/testTypes/rpc.ts new file mode 100644 index 0000000000..4b3724bf4d --- /dev/null +++ b/waspc/examples/todoApp/src/testTypes/rpc.ts @@ -0,0 +1,104 @@ +import { AuthUser } from 'wasp/auth' +import { getMe } from 'wasp/client/auth' +import { + getTask, + getTasks, + createTask, + updateTaskIsDone, + deleteCompletedTasks, + toggleAllTasks, + getNumTasks, + getDate, + getAnything, + getTrueVoid, +} from 'wasp/client/operations' +import { Task } from 'wasp/entities' +import { Payload } from 'wasp/server/_types' +import { Assert, AreEqual } from './helpers' + +// For the details of this specification, see +// https://github.com/wasp-lang/wasp/pull/1090#discussion_r1159732471 +// This should be [], +// but I couldn't get it to work yet: https://github.com/wasp-lang/wasp/issues/2004 +type VoidOperationPayload = [args?: void | undefined] + +// When the user doesn't specify an operation payload, +// we want to be as permissive as possible. +type UnspecifiedOperationPayload = [args?: unknown] + +type TQ1 = Assert< + InputsAndOutputsAre], Promise> +> + +type TQ2 = Assert< + InputsAndOutputsAre> +> + +type TQ3 = Assert< + InputsAndOutputsAre> +> + +type TQ4 = Assert< + InputsAndOutputsAre> +> + +type TQ5 = Assert< + InputsAndOutputsAre< + typeof getAnything, + UnspecifiedOperationPayload, + Promise + > +> + +type TQ6 = Assert< + InputsAndOutputsAre> +> + +type TestGetMe = Assert< + InputsAndOutputsAre< + typeof getMe, + VoidOperationPayload, + Promise + > +> + +type TA1 = Assert< + InputsAndOutputsAre< + typeof createTask, + [Pick], + Promise + > +> + +type TA2 = Assert< + InputsAndOutputsAre< + typeof updateTaskIsDone, + [Pick], + Promise + > +> + +type TA3 = Assert< + InputsAndOutputsAre< + typeof deleteCompletedTasks, + UnspecifiedOperationPayload, + Promise + > +> + +type TA4 = Assert< + InputsAndOutputsAre< + typeof toggleAllTasks, + UnspecifiedOperationPayload, + Promise + > +> + +type InputsAndOutputsAre< + OperationType extends (...args: any[]) => any, + ExpectedParams, + ExpectedReturn +> = { + params: AreEqual, ExpectedParams> + return: AreEqual, ExpectedReturn> +} diff --git a/waspc/src/Wasp/Generator/SdkGenerator/Client/OperationsGenerator.hs b/waspc/src/Wasp/Generator/SdkGenerator/Client/OperationsGenerator.hs index cc1331786a..113668526d 100644 --- a/waspc/src/Wasp/Generator/SdkGenerator/Client/OperationsGenerator.hs +++ b/waspc/src/Wasp/Generator/SdkGenerator/Client/OperationsGenerator.hs @@ -38,7 +38,8 @@ genOperations spec = genClientOpsFileCopy [relfile|internal/index.ts|], -- Not migrated to TS yet genClientOpsFileCopy [relfile|internal/updateHandlersMap.js|], - genClientOpsFileCopy [relfile|core.ts|], + genClientOpsFileCopy [relfile|rpc.ts|], + genClientOpsFileCopy [relfile|hooks.ts|], genClientOpsFileCopy [relfile|index.ts|], genClientOpsFileCopy [relfile|queryClient.ts|] ] diff --git a/web/docs/data-model/operations/actions.md b/web/docs/data-model/operations/actions.md index 365eb32ed4..9c689da382 100644 --- a/web/docs/data-model/operations/actions.md +++ b/web/docs/data-model/operations/actions.md @@ -90,7 +90,7 @@ After declaring a Wasp Action, two important things happen: Wasp will send this object over the network and pass it into the Action's implementation as its first positional argument (more on this when we look at the implementations). Such an abstraction works thanks to an HTTP API route handler Wasp generates on the server, which calls the Action's NodeJS implementation under the hood. -Generating these two functions ensures a uniform calling interface across the entire app (both client and server). +Generating these two functions ensures a similar calling interface across the entire app (both client and server). ### Implementing Actions in Node @@ -216,7 +216,7 @@ For a detailed explanation of the Action definition API (i.e., arguments and ret ### Using Actions -To use an Action, you can import it from `wasp/client/operations` and call it directly. As mentioned, the usage doesn't change depending on whether you're on the server or the client: +To use an Action on the client, you can import it from `wasp/client/operations` and call it directly. diff --git a/web/docs/data-model/operations/queries.md b/web/docs/data-model/operations/queries.md index c41733465f..81c5cacf58 100644 --- a/web/docs/data-model/operations/queries.md +++ b/web/docs/data-model/operations/queries.md @@ -94,7 +94,7 @@ After declaring a Wasp Query, two important things happen: Wasp will send this object over the network and pass it into the Query's implementation as its first positional argument (more on this when we look at the implementations). Such an abstraction works thanks to an HTTP API route handler Wasp generates on the server, which calls the Query's NodeJS implementation under the hood. -Generating these two functions ensures a uniform calling interface across the entire app (both client and server). +Generating these two functions ensures a similar calling interface across the entire app (both client and server). ### Implementing Queries in Node @@ -188,7 +188,7 @@ For a detailed explanation of the Query definition API (i.e., arguments and retu ### Using Queries -To use a Query, you can import it from `wasp/client/operations` and call it directly. As mentioned, the usage doesn't change depending on whether you're on the server or the client: +To call a Query in the client code, you can import it from `wasp/client/operations` and call it directly.