@questpie/tanstack-query 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,15 @@
1
1
  # @questpie/tanstack-query
2
2
 
3
- Type-safe TanStack Query option builders for QUESTPIE — collections, globals, RPC procedures, and realtime streaming.
3
+ Type-safe TanStack Query option builders for QUESTPIE — collections, globals, app routes, and realtime streaming.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Query Options Factory** — Pre-built `queryOptions()` and `mutationOptions()` for all CRUD operations
8
- - **RPC Query Options** — Typed `query()` / `mutation()` builders for standalone RPC procedures with nested router support
8
+ - **Route Query Options** — Typed `query()` / `mutation()` builders for app routes with nested path support
9
9
  - **Realtime Streaming** — `streamedQuery`-based options for SSE live updates on collections and globals
10
10
  - **Batch Operations** — `updateMany` and `deleteMany` mutation builders
11
11
  - **SSR Ready** — Prefetch data on the server, hydrate on the client
12
- - **Full Type Inference** — Types flow from your CMS schema through to query results
12
+ - **Full Type Inference** — Types flow from your app schema through to query results
13
13
 
14
14
  ## Installation
15
15
 
@@ -24,43 +24,45 @@ bun add @questpie/tanstack-query questpie @tanstack/react-query
24
24
  ```ts
25
25
  import { createClient } from "questpie/client";
26
26
  import { createQuestpieQueryOptions } from "@questpie/tanstack-query";
27
- import type { AppCMS, AppRpc } from "@/questpie/server/cms";
27
+ import type { AppConfig } from "#questpie";
28
28
 
29
- const cmsClient = createClient<AppCMS, AppRpc>({
30
- baseURL: "http://localhost:3000",
31
- basePath: "/api/cms",
29
+ const appClient = createClient<AppConfig>({
30
+ baseURL: "http://localhost:3000",
31
+ basePath: "/api",
32
32
  });
33
33
 
34
- export const cmsQueries = createQuestpieQueryOptions(cmsClient);
34
+ export const appQueries = createQuestpieQueryOptions(appClient);
35
35
  ```
36
36
 
37
37
  ### 2. Use in Components
38
38
 
39
39
  ```tsx
40
40
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
41
- import { cmsQueries } from "@/lib/queries";
41
+ import { appQueries } from "@/lib/queries";
42
42
 
43
43
  function PostsList() {
44
- const { data } = useQuery(
45
- cmsQueries.collections.posts.find({ limit: 10 })
46
- );
47
-
48
- const queryClient = useQueryClient();
49
- const createPost = useMutation({
50
- ...cmsQueries.collections.posts.create(),
51
- onSuccess: () => {
52
- queryClient.invalidateQueries({ queryKey: cmsQueries.collections.posts.key() });
53
- },
54
- });
55
-
56
- return (
57
- <div>
58
- {data?.docs.map((post) => <div key={post.id}>{post.title}</div>)}
59
- <button onClick={() => createPost.mutate({ title: "New Post" })}>
60
- Create
61
- </button>
62
- </div>
63
- );
44
+ const { data } = useQuery(appQueries.collections.posts.find({ limit: 10 }));
45
+
46
+ const queryClient = useQueryClient();
47
+ const createPost = useMutation({
48
+ ...appQueries.collections.posts.create(),
49
+ onSuccess: () => {
50
+ queryClient.invalidateQueries({
51
+ queryKey: appQueries.collections.posts.key(),
52
+ });
53
+ },
54
+ });
55
+
56
+ return (
57
+ <div>
58
+ {data?.docs.map((post) => (
59
+ <div key={post.id}>{post.title}</div>
60
+ ))}
61
+ <button onClick={() => createPost.mutate({ title: "New Post" })}>
62
+ Create
63
+ </button>
64
+ </div>
65
+ );
64
66
  }
65
67
  ```
66
68
 
@@ -68,79 +70,77 @@ function PostsList() {
68
70
 
69
71
  ```ts
70
72
  // Find many (paginated)
71
- cmsQueries.collections.posts.find({
72
- where: { published: { eq: true } },
73
- orderBy: { createdAt: "desc" },
74
- limit: 10,
75
- offset: 0,
76
- with: { author: true },
77
- })
73
+ appQueries.collections.posts.find({
74
+ where: { published: { eq: true } },
75
+ orderBy: { createdAt: "desc" },
76
+ limit: 10,
77
+ offset: 0,
78
+ with: { author: true },
79
+ });
78
80
 
79
81
  // Find one
80
- cmsQueries.collections.posts.findOne({
81
- where: { id: "post-id" },
82
- with: { author: true },
83
- })
82
+ appQueries.collections.posts.findOne({
83
+ where: { id: "post-id" },
84
+ with: { author: true },
85
+ });
84
86
 
85
87
  // Count
86
- cmsQueries.collections.posts.count({
87
- where: { published: { eq: true } },
88
- })
88
+ appQueries.collections.posts.count({
89
+ where: { published: { eq: true } },
90
+ });
89
91
 
90
92
  // Create (mutation)
91
- cmsQueries.collections.posts.create()
93
+ appQueries.collections.posts.create();
92
94
 
93
95
  // Update (mutation)
94
- cmsQueries.collections.posts.update()
96
+ appQueries.collections.posts.update();
95
97
 
96
98
  // Delete (mutation)
97
- cmsQueries.collections.posts.delete()
99
+ appQueries.collections.posts.delete();
98
100
 
99
101
  // Update many (mutation)
100
- cmsQueries.collections.posts.updateMany()
102
+ appQueries.collections.posts.updateMany();
101
103
 
102
104
  // Delete many (mutation)
103
- cmsQueries.collections.posts.deleteMany()
105
+ appQueries.collections.posts.deleteMany();
104
106
 
105
107
  // Restore soft-deleted (mutation)
106
- cmsQueries.collections.posts.restore()
108
+ appQueries.collections.posts.restore();
107
109
 
108
110
  // Query key for invalidation
109
- cmsQueries.collections.posts.key()
111
+ appQueries.collections.posts.key();
110
112
  ```
111
113
 
112
114
  ## Globals
113
115
 
114
116
  ```ts
115
117
  // Get global value
116
- cmsQueries.globals.siteSettings.get()
118
+ appQueries.globals.siteSettings.get();
117
119
 
118
120
  // Update global (mutation)
119
- cmsQueries.globals.siteSettings.update()
121
+ appQueries.globals.siteSettings.update();
120
122
 
121
123
  // Query key
122
- cmsQueries.globals.siteSettings.key()
124
+ appQueries.globals.siteSettings.key();
123
125
  ```
124
126
 
125
- ## RPC
127
+ ## Routes
126
128
 
127
- Full type-safe query/mutation options for standalone RPC procedures:
129
+ Full type-safe query/mutation options for app routes:
128
130
 
129
131
  ```ts
130
- // Query options from RPC procedure
132
+ // Query options from route
131
133
  const { data } = useQuery(
132
- cmsQueries.rpc.dashboard.getStats.query({ period: "week" })
134
+ appQueries.routes.dashboard.getStats.query({ period: "week" }),
133
135
  );
134
136
 
135
- // Mutation options from RPC procedure
136
- const publish = useMutation(
137
- cmsQueries.rpc.posts.publish.mutation()
138
- );
137
+ // Mutation options from route
138
+ const publish = useMutation(appQueries.routes.posts.publish.mutation());
139
139
  publish.mutate({ id: "post_123" });
140
140
 
141
141
  // Query key for invalidation/prefetch
142
142
  queryClient.invalidateQueries({
143
- queryKey: cmsQueries.rpc.dashboard.getStats.key({ period: "week" }),
143
+ queryKey: appQueries.routes.dashboard.getStats.key({ period: "week" }),
144
144
  });
145
145
  ```
146
146
 
@@ -151,17 +151,17 @@ Collection queries support SSE-based live updates via `streamedQuery`:
151
151
  ```ts
152
152
  // Enable realtime on a query
153
153
  const { data } = useQuery(
154
- cmsQueries.collections.posts.find(
155
- { limit: 10 },
156
- { realtime: true }
157
- )
154
+ appQueries.collections.posts.find({ limit: 10 }, { realtime: true }),
158
155
  );
159
156
  ```
160
157
 
161
158
  Topic helpers for manual subscription:
162
159
 
163
160
  ```ts
164
- import { buildCollectionTopic, buildGlobalTopic } from "@questpie/tanstack-query";
161
+ import {
162
+ buildCollectionTopic,
163
+ buildGlobalTopic,
164
+ } from "@questpie/tanstack-query";
165
165
 
166
166
  const postsTopic = buildCollectionTopic("posts");
167
167
  const settingsTopic = buildGlobalTopic("siteSettings");
@@ -174,26 +174,28 @@ const settingsTopic = buildGlobalTopic("siteSettings");
174
174
  ```tsx
175
175
  import { createFileRoute } from "@tanstack/react-router";
176
176
  import { useSuspenseQuery } from "@tanstack/react-query";
177
- import { cmsQueries } from "@/lib/queries";
177
+ import { appQueries } from "@/lib/queries";
178
178
 
179
179
  export const Route = createFileRoute("/posts")({
180
- loader: async ({ context }) => {
181
- await context.queryClient.ensureQueryData(
182
- cmsQueries.collections.posts.find({ limit: 10 })
183
- );
184
- },
185
- component: PostsPage,
180
+ loader: async ({ context }) => {
181
+ await context.queryClient.ensureQueryData(
182
+ appQueries.collections.posts.find({ limit: 10 }),
183
+ );
184
+ },
185
+ component: PostsPage,
186
186
  });
187
187
 
188
188
  function PostsPage() {
189
- const { data } = useSuspenseQuery(
190
- cmsQueries.collections.posts.find({ limit: 10 })
191
- );
192
- return (
193
- <ul>
194
- {data.docs.map((post) => <li key={post.id}>{post.title}</li>)}
195
- </ul>
196
- );
189
+ const { data } = useSuspenseQuery(
190
+ appQueries.collections.posts.find({ limit: 10 }),
191
+ );
192
+ return (
193
+ <ul>
194
+ {data.docs.map((post) => (
195
+ <li key={post.id}>{post.title}</li>
196
+ ))}
197
+ </ul>
198
+ );
197
199
  }
198
200
  ```
199
201
 
@@ -202,18 +204,18 @@ function PostsPage() {
202
204
  ```tsx
203
205
  import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
204
206
  import { getQueryClient } from "@/lib/query-client";
205
- import { cmsQueries } from "@/lib/queries";
207
+ import { appQueries } from "@/lib/queries";
206
208
 
207
209
  export default async function PostsPage() {
208
- const queryClient = getQueryClient();
209
- await queryClient.prefetchQuery(
210
- cmsQueries.collections.posts.find({ limit: 10 })
211
- );
212
- return (
213
- <HydrationBoundary state={dehydrate(queryClient)}>
214
- <PostsList />
215
- </HydrationBoundary>
216
- );
210
+ const queryClient = getQueryClient();
211
+ await queryClient.prefetchQuery(
212
+ appQueries.collections.posts.find({ limit: 10 }),
213
+ );
214
+ return (
215
+ <HydrationBoundary state={dehydrate(queryClient)}>
216
+ <PostsList />
217
+ </HydrationBoundary>
218
+ );
217
219
  }
218
220
  ```
219
221
 
@@ -228,9 +230,9 @@ export default async function PostsPage() {
228
230
  // Globals
229
231
  ["globals", globalName, "get"]
230
232
 
231
- // RPC
232
- ["rpc", ...segments, "query", input]
233
- ["rpc", ...segments, "mutation"]
233
+ // Routes
234
+ ["routes", ...segments, "query", input]
235
+ ["routes", ...segments, "mutation"]
234
236
  ```
235
237
 
236
238
  ## Documentation
package/dist/index.d.mts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { DefaultError, QueryKey, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
- import { QuestpieClient, RealtimeAPI, TopicConfig, buildCollectionTopic, buildGlobalTopic } from "questpie/client";
3
- import { Questpie } from "questpie";
2
+ import { QuestpieApp, QuestpieClient, RealtimeAPI, TopicConfig, buildCollectionTopic, buildGlobalTopic } from "questpie/client";
4
3
 
5
4
  //#region src/index.d.ts
6
5
  type QuestpieQueryErrorMap = (error: unknown) => unknown;
@@ -8,6 +7,7 @@ type QuestpieQueryOptionsConfig = {
8
7
  keyPrefix?: QueryKey;
9
8
  errorMap?: QuestpieQueryErrorMap;
10
9
  locale?: string;
10
+ stage?: string;
11
11
  };
12
12
  type AnyAsyncFn = (...args: any[]) => Promise<any>;
13
13
  type QueryData<TFn extends AnyAsyncFn> = Awaited<ReturnType<TFn>>;
@@ -18,40 +18,55 @@ type KeyBuilder<TFn extends AnyAsyncFn> = HasArgs<TFn> extends false ? () => Que
18
18
  type MutationVariables<TFn extends AnyAsyncFn> = HasArgs<TFn> extends false ? void : undefined extends FirstArg<TFn> ? FirstArg<TFn> | void : FirstArg<TFn>;
19
19
  type MutationBuilder<TVariables, TData> = () => UseMutationOptions<TData, DefaultError, TVariables>;
20
20
  /** Extract collection keys from QuestpieClient */
21
- type CollectionKeys<TCMS extends Questpie<any>, TRPC extends Record<string, any>> = Extract<keyof QuestpieClient<TCMS, TRPC>["collections"], string>;
21
+ type CollectionKeys<TApp extends QuestpieApp> = Extract<keyof QuestpieClient<TApp>["collections"], string>;
22
22
  /** Extract global keys from QuestpieClient */
23
- type GlobalKeys<TCMS extends Questpie<any>, TRPC extends Record<string, any>> = Extract<keyof QuestpieClient<TCMS, TRPC>["globals"], string>;
24
- type CollectionFind<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["find"];
25
- type CollectionCount<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["count"];
26
- type CollectionFindOne<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["findOne"];
27
- type CollectionCreate<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["create"];
28
- type CollectionUpdate<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["update"];
29
- type CollectionDelete<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["delete"];
30
- type CollectionRestore<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["collections"][K$1]["restore"];
31
- type GlobalGet<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends GlobalKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["globals"][K$1] extends {
23
+ type GlobalKeys<TApp extends QuestpieApp> = Extract<keyof QuestpieClient<TApp>["globals"], string>;
24
+ type CollectionFind<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["find"];
25
+ type CollectionCount<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["count"];
26
+ type CollectionFindOne<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["findOne"];
27
+ type CollectionCreate<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["create"];
28
+ type CollectionUpdate<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["update"];
29
+ type CollectionDelete<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["delete"];
30
+ type CollectionRestore<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["restore"];
31
+ type CollectionFindVersions<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["findVersions"];
32
+ type CollectionRevertToVersion<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["revertToVersion"];
33
+ type CollectionTransitionStage<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["transitionStage"];
34
+ type GlobalGet<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = QuestpieClient<TApp>["globals"][K$1] extends {
32
35
  get: infer TGet;
33
36
  } ? TGet extends AnyAsyncFn ? TGet : never : never;
34
- type GlobalUpdate<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends GlobalKeys<TCMS, TRPC>> = QuestpieClient<TCMS, TRPC>["globals"][K$1] extends {
37
+ type GlobalUpdate<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = QuestpieClient<TApp>["globals"][K$1] extends {
35
38
  update: infer TUpdate;
36
39
  } ? TUpdate extends AnyAsyncFn ? TUpdate : never : never;
37
- type RpcProcedureQueryOptionsAPI<TFn extends AnyAsyncFn> = {
40
+ type GlobalFindVersions<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = QuestpieClient<TApp>["globals"][K$1] extends {
41
+ findVersions: infer TFindVersions;
42
+ } ? TFindVersions extends AnyAsyncFn ? TFindVersions : never : never;
43
+ type GlobalRevertToVersion<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = QuestpieClient<TApp>["globals"][K$1] extends {
44
+ revertToVersion: infer TRevert;
45
+ } ? TRevert extends AnyAsyncFn ? TRevert : never : never;
46
+ type GlobalTransitionStage<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = QuestpieClient<TApp>["globals"][K$1] extends {
47
+ transitionStage: infer TTransition;
48
+ } ? TTransition extends AnyAsyncFn ? TTransition : never : never;
49
+ type RouteLeafQueryOptionsAPI<TFn extends AnyAsyncFn> = {
38
50
  query: QueryBuilder<TFn>;
39
51
  mutation: MutationBuilder<MutationVariables<TFn>, QueryData<TFn>>;
40
52
  key: KeyBuilder<TFn>;
41
53
  };
42
- type RpcQueryOptionsAPI<TRpcNode> = TRpcNode extends AnyAsyncFn ? RpcProcedureQueryOptionsAPI<TRpcNode> : TRpcNode extends Record<string, any> ? { [K in keyof TRpcNode]: RpcQueryOptionsAPI<TRpcNode[K]> } : never;
43
- type CollectionQueryOptionsAPI<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends CollectionKeys<TCMS, TRPC>> = {
44
- find: QueryBuilder<CollectionFind<TCMS, TRPC, K$1>>;
45
- count: QueryBuilder<CollectionCount<TCMS, TRPC, K$1>>;
46
- findOne: QueryBuilder<CollectionFindOne<TCMS, TRPC, K$1>>;
47
- create: MutationBuilder<FirstArg<CollectionCreate<TCMS, TRPC, K$1>>, QueryData<CollectionCreate<TCMS, TRPC, K$1>>>;
48
- update: MutationBuilder<FirstArg<CollectionUpdate<TCMS, TRPC, K$1>>, QueryData<CollectionUpdate<TCMS, TRPC, K$1>>>;
49
- delete: MutationBuilder<FirstArg<CollectionDelete<TCMS, TRPC, K$1>>, QueryData<CollectionDelete<TCMS, TRPC, K$1>>>;
50
- restore: MutationBuilder<FirstArg<CollectionRestore<TCMS, TRPC, K$1>>, QueryData<CollectionRestore<TCMS, TRPC, K$1>>>;
54
+ type RoutesQueryOptionsAPI<TNode> = TNode extends AnyAsyncFn ? RouteLeafQueryOptionsAPI<TNode> : TNode extends Record<string, any> ? { [K in keyof TNode]: RoutesQueryOptionsAPI<TNode[K]> } : never;
55
+ type CollectionQueryOptionsAPI<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = {
56
+ find: QueryBuilder<CollectionFind<TApp, K$1>>;
57
+ count: QueryBuilder<CollectionCount<TApp, K$1>>;
58
+ findOne: QueryBuilder<CollectionFindOne<TApp, K$1>>;
59
+ create: MutationBuilder<FirstArg<CollectionCreate<TApp, K$1>>, QueryData<CollectionCreate<TApp, K$1>>>;
60
+ update: MutationBuilder<FirstArg<CollectionUpdate<TApp, K$1>>, QueryData<CollectionUpdate<TApp, K$1>>>;
61
+ delete: MutationBuilder<FirstArg<CollectionDelete<TApp, K$1>>, QueryData<CollectionDelete<TApp, K$1>>>;
62
+ restore: MutationBuilder<FirstArg<CollectionRestore<TApp, K$1>>, QueryData<CollectionRestore<TApp, K$1>>>;
63
+ findVersions: QueryBuilder<CollectionFindVersions<TApp, K$1>>;
64
+ revertToVersion: MutationBuilder<FirstArg<CollectionRevertToVersion<TApp, K$1>>, QueryData<CollectionRevertToVersion<TApp, K$1>>>;
65
+ transitionStage: MutationBuilder<FirstArg<CollectionTransitionStage<TApp, K$1>>, QueryData<CollectionTransitionStage<TApp, K$1>>>;
51
66
  updateMany: MutationBuilder<{
52
67
  where: any;
53
68
  data: any;
54
- }, QueryData<CollectionUpdate<TCMS, TRPC, K$1>>>;
69
+ }, QueryData<CollectionUpdate<TApp, K$1>>>;
55
70
  deleteMany: MutationBuilder<{
56
71
  where: any;
57
72
  }, {
@@ -59,17 +74,26 @@ type CollectionQueryOptionsAPI<TCMS extends Questpie<any>, TRPC extends Record<s
59
74
  count: number;
60
75
  }>;
61
76
  };
62
- type GlobalQueryOptionsAPI<TCMS extends Questpie<any>, TRPC extends Record<string, any>, K$1 extends GlobalKeys<TCMS, TRPC>> = {
63
- get: QueryBuilder<GlobalGet<TCMS, TRPC, K$1>>;
77
+ type GlobalQueryOptionsAPI<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = {
78
+ get: QueryBuilder<GlobalGet<TApp, K$1>>;
64
79
  update: MutationBuilder<{
65
- data: Parameters<GlobalUpdate<TCMS, TRPC, K$1>>[0];
66
- options?: Parameters<GlobalUpdate<TCMS, TRPC, K$1>>[1];
67
- }, QueryData<GlobalUpdate<TCMS, TRPC, K$1>>>;
80
+ data: Parameters<GlobalUpdate<TApp, K$1>>[0];
81
+ options?: Parameters<GlobalUpdate<TApp, K$1>>[1];
82
+ }, QueryData<GlobalUpdate<TApp, K$1>>>;
83
+ findVersions: QueryBuilder<GlobalFindVersions<TApp, K$1>>;
84
+ revertToVersion: MutationBuilder<{
85
+ params: Parameters<GlobalRevertToVersion<TApp, K$1>>[0];
86
+ options?: Parameters<GlobalRevertToVersion<TApp, K$1>>[1];
87
+ }, QueryData<GlobalRevertToVersion<TApp, K$1>>>;
88
+ transitionStage: MutationBuilder<{
89
+ params: Parameters<GlobalTransitionStage<TApp, K$1>>[0];
90
+ options?: Parameters<GlobalTransitionStage<TApp, K$1>>[1];
91
+ }, QueryData<GlobalTransitionStage<TApp, K$1>>>;
68
92
  };
69
- type QuestpieQueryOptionsProxy<TCMS extends Questpie<any>, TRPC extends Record<string, any> = Record<string, never>> = {
70
- collections: { [K in CollectionKeys<TCMS, TRPC>]: CollectionQueryOptionsAPI<TCMS, TRPC, K> };
71
- globals: { [K in GlobalKeys<TCMS, TRPC>]: GlobalQueryOptionsAPI<TCMS, TRPC, K> };
72
- rpc: RpcQueryOptionsAPI<QuestpieClient<TCMS, TRPC>["rpc"]>;
93
+ type QuestpieQueryOptionsProxy<TApp extends QuestpieApp = QuestpieApp> = {
94
+ collections: { [K in CollectionKeys<TApp>]: CollectionQueryOptionsAPI<TApp, K> };
95
+ globals: { [K in GlobalKeys<TApp>]: GlobalQueryOptionsAPI<TApp, K> };
96
+ routes: RoutesQueryOptionsAPI<QuestpieClient<TApp>["routes"]>;
73
97
  custom: {
74
98
  query: <TData>(config: {
75
99
  key: QueryKey;
@@ -89,9 +113,9 @@ type QuestpieQueryOptionsProxy<TCMS extends Questpie<any>, TRPC extends Record<s
89
113
  * ```ts
90
114
  * import { createQuestpieQueryOptions } from "@questpie/tanstack-query"
91
115
  * import { createClient } from "questpie/client"
92
- * import type { AppCMS, AppRpc } from "@/cms"
116
+ * import type { App, AppRpc } from "@/app"
93
117
  *
94
- * const client = createClient<AppCMS, AppRpc>({ baseURL: "http://localhost:3000" })
118
+ * const client = createClient<App, AppRpc>({ baseURL: "http://localhost:3000" })
95
119
  * const cmsQueries = createQuestpieQueryOptions(client)
96
120
  *
97
121
  * // Use with useQuery
@@ -100,10 +124,10 @@ type QuestpieQueryOptionsProxy<TCMS extends Questpie<any>, TRPC extends Record<s
100
124
  * // Use with useMutation
101
125
  * const mutation = useMutation(cmsQueries.collections.posts.create())
102
126
  *
103
- * // Use with RPC
104
- * const stats = useQuery(cmsQueries.rpc.dashboard.getStats.query({ period: "week" }))
127
+ * // Use with routes
128
+ * const stats = useQuery(cmsQueries.routes.dashboard.getStats.query({ period: "week" }))
105
129
  * ```
106
130
  */
107
- declare function createQuestpieQueryOptions<TCMS extends Questpie<any>, TRPC extends Record<string, any> = Record<string, never>>(client: QuestpieClient<TCMS, TRPC>, config?: QuestpieQueryOptionsConfig): QuestpieQueryOptionsProxy<TCMS, TRPC>;
131
+ declare function createQuestpieQueryOptions<TApp extends QuestpieApp = QuestpieApp>(client: QuestpieClient<TApp>, config?: QuestpieQueryOptionsConfig): QuestpieQueryOptionsProxy<TApp>;
108
132
  //#endregion
109
133
  export { type DefaultError, type QueryKey, QuestpieQueryErrorMap, QuestpieQueryOptionsConfig, QuestpieQueryOptionsProxy, type RealtimeAPI, type TopicConfig, type UseMutationOptions, type UseQueryOptions, buildCollectionTopic, buildGlobalTopic, createQuestpieQueryOptions };
package/dist/index.mjs CHANGED
@@ -45,9 +45,9 @@ const wrapMutationFn = (mutationFn, errorMap) => {
45
45
  * ```ts
46
46
  * import { createQuestpieQueryOptions } from "@questpie/tanstack-query"
47
47
  * import { createClient } from "questpie/client"
48
- * import type { AppCMS, AppRpc } from "@/cms"
48
+ * import type { App, AppRpc } from "@/app"
49
49
  *
50
- * const client = createClient<AppCMS, AppRpc>({ baseURL: "http://localhost:3000" })
50
+ * const client = createClient<App, AppRpc>({ baseURL: "http://localhost:3000" })
51
51
  * const cmsQueries = createQuestpieQueryOptions(client)
52
52
  *
53
53
  * // Use with useQuery
@@ -56,14 +56,15 @@ const wrapMutationFn = (mutationFn, errorMap) => {
56
56
  * // Use with useMutation
57
57
  * const mutation = useMutation(cmsQueries.collections.posts.create())
58
58
  *
59
- * // Use with RPC
60
- * const stats = useQuery(cmsQueries.rpc.dashboard.getStats.query({ period: "week" }))
59
+ * // Use with routes
60
+ * const stats = useQuery(cmsQueries.routes.dashboard.getStats.query({ period: "week" }))
61
61
  * ```
62
62
  */
63
63
  function createQuestpieQueryOptions(client, config = {}) {
64
64
  const keyPrefix = config.keyPrefix ?? ["questpie"];
65
65
  const errorMap = config.errorMap ?? defaultErrorMap;
66
66
  const locale = config.locale;
67
+ const stage = config.stage;
67
68
  const collections = new Proxy({}, { get: (_target, collectionName) => {
68
69
  if (typeof collectionName !== "string") return void 0;
69
70
  const collection = client.collections[collectionName];
@@ -74,6 +75,7 @@ function createQuestpieQueryOptions(client, config = {}) {
74
75
  ...baseKey,
75
76
  "find",
76
77
  locale,
78
+ stage,
77
79
  normalizeQueryKeyOptions(options)
78
80
  ]);
79
81
  if (queryConfig?.realtime && client.realtime) {
@@ -98,6 +100,7 @@ function createQuestpieQueryOptions(client, config = {}) {
98
100
  ...baseKey,
99
101
  "count",
100
102
  locale,
103
+ stage,
101
104
  normalizeQueryKeyOptions(options)
102
105
  ]);
103
106
  if (queryConfig?.realtime && client.realtime) {
@@ -122,6 +125,7 @@ function createQuestpieQueryOptions(client, config = {}) {
122
125
  ...baseKey,
123
126
  "findOne",
124
127
  locale,
128
+ stage,
125
129
  normalizeQueryKeyOptions(options)
126
130
  ]),
127
131
  queryFn: wrapQueryFn(() => collection.findOne(options), errorMap)
@@ -130,49 +134,110 @@ function createQuestpieQueryOptions(client, config = {}) {
130
134
  mutationKey: buildKey(keyPrefix, [
131
135
  ...baseKey,
132
136
  "create",
133
- locale
137
+ locale,
138
+ stage
134
139
  ]),
135
- mutationFn: wrapMutationFn((data) => collection.create(data, locale ? { locale } : void 0), errorMap)
140
+ mutationFn: wrapMutationFn((data) => collection.create(data, locale || stage ? {
141
+ ...locale ? { locale } : {},
142
+ ...stage ? { stage } : {}
143
+ } : void 0), errorMap)
136
144
  }),
137
145
  update: () => mutationOptions({
138
146
  mutationKey: buildKey(keyPrefix, [
139
147
  ...baseKey,
140
148
  "update",
141
- locale
149
+ locale,
150
+ stage
142
151
  ]),
143
- mutationFn: wrapMutationFn((variables) => collection.update(variables, locale ? { locale } : void 0), errorMap)
152
+ mutationFn: wrapMutationFn((variables) => collection.update(variables, locale || stage ? {
153
+ ...locale ? { locale } : {},
154
+ ...stage ? { stage } : {}
155
+ } : void 0), errorMap)
144
156
  }),
145
157
  delete: () => mutationOptions({
146
158
  mutationKey: buildKey(keyPrefix, [
147
159
  ...baseKey,
148
160
  "delete",
149
- locale
161
+ locale,
162
+ stage
150
163
  ]),
151
- mutationFn: wrapMutationFn((variables) => collection.delete(variables, locale ? { locale } : void 0), errorMap)
164
+ mutationFn: wrapMutationFn((variables) => collection.delete(variables, locale || stage ? {
165
+ ...locale ? { locale } : {},
166
+ ...stage ? { stage } : {}
167
+ } : void 0), errorMap)
152
168
  }),
153
169
  restore: () => mutationOptions({
154
170
  mutationKey: buildKey(keyPrefix, [
155
171
  ...baseKey,
156
172
  "restore",
157
- locale
173
+ locale,
174
+ stage
175
+ ]),
176
+ mutationFn: wrapMutationFn((variables) => collection.restore(variables, locale || stage ? {
177
+ ...locale ? { locale } : {},
178
+ ...stage ? { stage } : {}
179
+ } : void 0), errorMap)
180
+ }),
181
+ findVersions: (params) => queryOptions({
182
+ queryKey: buildKey(keyPrefix, [
183
+ ...baseKey,
184
+ "findVersions",
185
+ locale,
186
+ stage,
187
+ normalizeQueryKeyOptions(params)
158
188
  ]),
159
- mutationFn: wrapMutationFn((variables) => collection.restore(variables, locale ? { locale } : void 0), errorMap)
189
+ queryFn: wrapQueryFn(() => collection.findVersions(params, locale || stage ? {
190
+ ...locale ? { locale } : {},
191
+ ...stage ? { stage } : {}
192
+ } : void 0), errorMap)
193
+ }),
194
+ revertToVersion: () => mutationOptions({
195
+ mutationKey: buildKey(keyPrefix, [
196
+ ...baseKey,
197
+ "revertToVersion",
198
+ locale,
199
+ stage
200
+ ]),
201
+ mutationFn: wrapMutationFn((variables) => collection.revertToVersion(variables, locale || stage ? {
202
+ ...locale ? { locale } : {},
203
+ ...stage ? { stage } : {}
204
+ } : void 0), errorMap)
205
+ }),
206
+ transitionStage: () => mutationOptions({
207
+ mutationKey: buildKey(keyPrefix, [
208
+ ...baseKey,
209
+ "transitionStage",
210
+ locale,
211
+ stage
212
+ ]),
213
+ mutationFn: wrapMutationFn((variables) => collection.transitionStage(variables, locale || stage ? {
214
+ ...locale ? { locale } : {},
215
+ ...stage ? { stage } : {}
216
+ } : void 0), errorMap)
160
217
  }),
161
218
  updateMany: () => mutationOptions({
162
219
  mutationKey: buildKey(keyPrefix, [
163
220
  ...baseKey,
164
221
  "updateMany",
165
- locale
222
+ locale,
223
+ stage
166
224
  ]),
167
- mutationFn: wrapMutationFn((variables) => collection.updateMany(variables, locale ? { locale } : void 0), errorMap)
225
+ mutationFn: wrapMutationFn((variables) => collection.updateMany(variables, locale || stage ? {
226
+ ...locale ? { locale } : {},
227
+ ...stage ? { stage } : {}
228
+ } : void 0), errorMap)
168
229
  }),
169
230
  deleteMany: () => mutationOptions({
170
231
  mutationKey: buildKey(keyPrefix, [
171
232
  ...baseKey,
172
233
  "deleteMany",
173
- locale
234
+ locale,
235
+ stage
174
236
  ]),
175
- mutationFn: wrapMutationFn((variables) => collection.deleteMany(variables, locale ? { locale } : void 0), errorMap)
237
+ mutationFn: wrapMutationFn((variables) => collection.deleteMany(variables, locale || stage ? {
238
+ ...locale ? { locale } : {},
239
+ ...stage ? { stage } : {}
240
+ } : void 0), errorMap)
176
241
  })
177
242
  };
178
243
  } });
@@ -186,6 +251,7 @@ function createQuestpieQueryOptions(client, config = {}) {
186
251
  ...baseKey,
187
252
  "get",
188
253
  locale,
254
+ stage,
189
255
  normalizeQueryKeyOptions(options)
190
256
  ]);
191
257
  if (queryConfig?.realtime && client.realtime) {
@@ -209,60 +275,104 @@ function createQuestpieQueryOptions(client, config = {}) {
209
275
  mutationKey: buildKey(keyPrefix, [
210
276
  ...baseKey,
211
277
  "update",
212
- locale
278
+ locale,
279
+ stage
213
280
  ]),
214
281
  mutationFn: wrapMutationFn((variables) => global.update(variables.data, {
282
+ ...variables.options,
283
+ ...locale ? { locale } : void 0,
284
+ ...stage ? { stage } : void 0
285
+ }), errorMap)
286
+ }),
287
+ findVersions: (options) => queryOptions({
288
+ queryKey: buildKey(keyPrefix, [
289
+ ...baseKey,
290
+ "findVersions",
291
+ locale,
292
+ stage,
293
+ normalizeQueryKeyOptions(options)
294
+ ]),
295
+ queryFn: wrapQueryFn(() => global.findVersions({
296
+ ...options,
297
+ ...locale ? { locale } : void 0,
298
+ ...stage ? { stage } : void 0
299
+ }), errorMap)
300
+ }),
301
+ revertToVersion: () => mutationOptions({
302
+ mutationKey: buildKey(keyPrefix, [
303
+ ...baseKey,
304
+ "revertToVersion",
305
+ locale,
306
+ stage
307
+ ]),
308
+ mutationFn: wrapMutationFn((variables) => global.revertToVersion(variables.params, {
309
+ ...variables.options,
310
+ ...locale ? { locale } : void 0,
311
+ ...stage ? { stage } : void 0
312
+ }), errorMap)
313
+ }),
314
+ transitionStage: () => mutationOptions({
315
+ mutationKey: buildKey(keyPrefix, [
316
+ ...baseKey,
317
+ "transitionStage",
318
+ locale,
319
+ stage
320
+ ]),
321
+ mutationFn: wrapMutationFn((variables) => global.transitionStage(variables.params, {
215
322
  ...variables.options,
216
323
  ...locale ? { locale } : void 0
217
324
  }), errorMap)
218
325
  })
219
326
  };
220
327
  } });
221
- const callRpcProcedure = async (segments, input) => {
222
- let current = client.rpc;
328
+ /**
329
+ * Resolve a nested route caller from the client by traversing dot segments.
330
+ */
331
+ const callRoute = async (segments, input) => {
332
+ let current = client.routes;
223
333
  for (const segment of segments) current = current?.[segment];
224
- if (typeof current !== "function") throw new Error(`RPC procedure not found at path: ${segments.join(".") || "<root>"}`);
334
+ if (typeof current !== "function") throw new Error(`Route not found at path: ${segments.join(".") || "<root>"}`);
225
335
  if (input === void 0) return current();
226
336
  return current(input);
227
337
  };
228
- const createRpcNodeProxy = (segments) => {
338
+ const createRouteNodeProxy = (segments) => {
229
339
  return new Proxy({}, { get: (_target, prop) => {
230
340
  if (prop === "query") return (input) => queryOptions({
231
341
  queryKey: buildKey(keyPrefix, [
232
- "rpc",
342
+ "routes",
233
343
  ...segments,
234
344
  "query",
235
345
  locale,
236
346
  normalizeQueryKeyOptions(input)
237
347
  ]),
238
- queryFn: wrapQueryFn(() => callRpcProcedure(segments, input), errorMap)
348
+ queryFn: wrapQueryFn(() => callRoute(segments, input), errorMap)
239
349
  });
240
350
  if (prop === "mutation") return () => mutationOptions({
241
351
  mutationKey: buildKey(keyPrefix, [
242
- "rpc",
352
+ "routes",
243
353
  ...segments,
244
354
  "mutation",
245
355
  locale
246
356
  ]),
247
- mutationFn: wrapMutationFn((variables) => callRpcProcedure(segments, variables), errorMap)
357
+ mutationFn: wrapMutationFn((variables) => callRoute(segments, variables), errorMap)
248
358
  });
249
359
  if (prop === "key") return (input) => buildKey(keyPrefix, [
250
- "rpc",
360
+ "routes",
251
361
  ...segments,
252
362
  "query",
253
363
  locale,
254
364
  normalizeQueryKeyOptions(input)
255
365
  ]);
256
366
  if (typeof prop !== "string") return void 0;
257
- return createRpcNodeProxy([...segments, prop]);
367
+ return createRouteNodeProxy([...segments, prop]);
258
368
  } });
259
369
  };
260
370
  return {
261
371
  collections,
262
372
  globals,
263
- rpc: new Proxy({}, { get: (_target, prop) => {
373
+ routes: new Proxy({}, { get: (_target, prop) => {
264
374
  if (typeof prop !== "string") return void 0;
265
- return createRpcNodeProxy([prop]);
375
+ return createRouteNodeProxy([prop]);
266
376
  } }),
267
377
  custom: {
268
378
  query: (customConfig) => queryOptions({
package/package.json CHANGED
@@ -1,39 +1,45 @@
1
1
  {
2
2
  "name": "@questpie/tanstack-query",
3
- "version": "2.0.0",
4
- "type": "module",
3
+ "version": "3.0.0",
5
4
  "repository": {
6
5
  "type": "git",
7
- "url": "https://github.com/questpie/questpie-cms.git",
6
+ "url": "https://github.com/questpie/questpie.git",
8
7
  "directory": "packages/tanstack-query"
9
8
  },
10
9
  "files": [
11
10
  "dist"
12
11
  ],
13
- "scripts": {
14
- "build": "tsdown",
15
- "check-types": "tsc --noEmit"
16
- },
17
- "devDependencies": {
18
- "bun-types": "latest",
19
- "tsdown": "^0.18.3"
20
- },
21
- "peerDependencies": {
22
- "questpie": "^2.0.0",
23
- "@tanstack/react-query": "^5.0.0"
24
- },
12
+ "type": "module",
13
+ "main": "./dist/index.mjs",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.mts",
25
16
  "exports": {
26
- ".": "./dist/index.mjs",
17
+ ".": {
18
+ "types": "./dist/index.d.mts",
19
+ "default": "./dist/index.mjs"
20
+ },
27
21
  "./package.json": "./package.json"
28
22
  },
29
23
  "publishConfig": {
30
24
  "access": "public",
31
25
  "exports": {
32
- ".": "./dist/index.mjs",
26
+ ".": {
27
+ "types": "./dist/index.d.mts",
28
+ "default": "./dist/index.mjs"
29
+ },
33
30
  "./package.json": "./package.json"
34
31
  }
35
32
  },
36
- "main": "./dist/index.mjs",
37
- "module": "./dist/index.mjs",
38
- "types": "./dist/index.d.mts"
33
+ "scripts": {
34
+ "build": "tsdown",
35
+ "check-types": "tsc --noEmit"
36
+ },
37
+ "devDependencies": {
38
+ "bun-types": "latest",
39
+ "tsdown": "^0.18.3"
40
+ },
41
+ "peerDependencies": {
42
+ "@tanstack/react-query": "^5.0.0",
43
+ "questpie": "^3.0.0"
44
+ }
39
45
  }