@questpie/tanstack-query 3.15.2 → 3.17.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
@@ -219,6 +219,13 @@ export default async function PostsPage() {
219
219
  }
220
220
  ```
221
221
 
222
+ TanStack Query's dehydrated state remains a JavaScript object and retains
223
+ QUESTPIE `Date` values. TanStack Start's Seroval hydration preserves them across
224
+ its serialization boundary. Other hosts must use their documented Date-aware
225
+ transport. If you move the state through an application-owned JSON-only
226
+ boundary, use a Date-aware serializer; never revive all ISO-looking strings
227
+ heuristically because `f.date()` and ordinary string fields must remain strings.
228
+
222
229
  ## Query Key Structure
223
230
 
224
231
  ```ts
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DefaultError, QueryKey, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
- import { ApplyQuery, CollectionRelations, CollectionSelect, FindManyOptions, FindOneOptionsBase, FindResult, GetCollection, GetGlobal, GlobalRelations, GlobalSelect, QuestpieApp, QuestpieClient, RealtimeAPI, ResolveRelationsDeep, TopicConfig, With, buildCollectionTopic, buildGlobalTopic } from "questpie/client";
2
+ import { ApplyQuery, CollectionRelations, CollectionSelect, FindManyOptions, FindOneOptionsBase, FindResult, GetCollection, GetGlobal, GlobalRelations, GlobalSelect, QuestpieApp, QuestpieClient, RealtimeAPI, ResolveRelationsDeep, TopicConfig, With, applyRealtimeFindEvent, applyRealtimeScalarEvent, applyRealtimeSingleEvent, buildCollectionTopic, buildGlobalTopic } from "questpie/client";
3
3
  import { AnyChannelDefinition, ChannelParamsOf, ChannelPresenceOf } from "questpie/channels";
4
4
 
5
5
  //#region src/index.d.ts
@@ -18,11 +18,11 @@ type QueryBuilder<TFn extends AnyAsyncFn> = HasArgs<TFn> extends false ? () => U
18
18
  /**
19
19
  * Per-query realtime flag — the second argument to `find` / `count` / `get`.
20
20
  */
21
- type RealtimeQueryConfig = {
21
+ type RealtimeQueryConfig<TData = unknown> = Omit<UseQueryOptions<TData>, "queryFn" | "queryKey"> & {
22
22
  /**
23
- * Subscribe the query to live snapshots over the multiplexed SSE stream.
24
- * The server supplies both the initial value and later full snapshots through
25
- * the same stream (via `experimental_streamedQuery`).
23
+ * Subscribe the query to the multiplexed realtime stream. The initial value
24
+ * and later keyed changes share one stream, so realtime mode performs no
25
+ * separate REST fetch.
26
26
  */
27
27
  realtime?: boolean;
28
28
  };
@@ -30,7 +30,7 @@ type RealtimeQueryConfig = {
30
30
  * Builder for reads that also have a live form — same as `QueryBuilder`
31
31
  * plus the optional `{ realtime }` second argument.
32
32
  */
33
- type LiveQueryBuilder<TFn extends AnyAsyncFn> = undefined extends FirstArg<TFn> ? (options?: FirstArg<TFn>, queryConfig?: RealtimeQueryConfig) => UseQueryOptions<QueryData<TFn>> : (options: FirstArg<TFn>, queryConfig?: RealtimeQueryConfig) => UseQueryOptions<QueryData<TFn>>;
33
+ type LiveQueryBuilder<TFn extends AnyAsyncFn> = undefined extends FirstArg<TFn> ? (options?: FirstArg<TFn>, queryConfig?: RealtimeQueryConfig<QueryData<TFn>>) => UseQueryOptions<QueryData<TFn>> : (options: FirstArg<TFn>, queryConfig?: RealtimeQueryConfig<QueryData<TFn>>) => UseQueryOptions<QueryData<TFn>>;
34
34
  type KeyBuilder<TFn extends AnyAsyncFn> = HasArgs<TFn> extends false ? () => QueryKey : undefined extends FirstArg<TFn> ? (options?: FirstArg<TFn>) => QueryKey : (options: FirstArg<TFn>) => QueryKey;
35
35
  type MutationVariables<TFn extends AnyAsyncFn> = HasArgs<TFn> extends false ? void : undefined extends FirstArg<TFn> ? FirstArg<TFn> | void : FirstArg<TFn>;
36
36
  type MutationBuilder<TVariables, TData> = () => UseMutationOptions<TData, DefaultError, TVariables>;
@@ -64,6 +64,9 @@ type CollectionCreate<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>
64
64
  type CollectionUpdate<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["update"];
65
65
  type CollectionDelete<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["delete"];
66
66
  type CollectionRestore<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["restore"];
67
+ type CollectionPurgeById<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1] extends {
68
+ purgeById: infer TPurge extends AnyAsyncFn;
69
+ } ? TPurge : never;
67
70
  type CollectionFindVersions<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["findVersions"];
68
71
  type CollectionRevertToVersion<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["revertToVersion"];
69
72
  type CollectionTransitionStage<TApp extends QuestpieApp, K$1 extends CollectionKeys<TApp>> = QuestpieClient<TApp>["collections"][K$1]["transitionStage"];
@@ -91,7 +94,7 @@ type CollectionQueryOptionsAPI<TApp extends QuestpieApp, K$1 extends CollectionK
91
94
  * client's `find()` (a `ReturnType`-based extraction would instantiate the
92
95
  * client generic at its constraint and collapse docs to `{}`).
93
96
  */
94
- find: <TQuery extends FindManyOptions<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>> | undefined = undefined>(options?: TQuery, queryConfig?: RealtimeQueryConfig) => UseQueryOptions<FindResult<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>, TQuery>>;
97
+ find: <TQuery extends FindManyOptions<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>> | undefined = undefined>(options?: TQuery, queryConfig?: RealtimeQueryConfig<FindResult<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>, TQuery>>) => UseQueryOptions<FindResult<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>, TQuery>>;
95
98
  count: LiveQueryBuilder<CollectionCount<TApp, K$1>>;
96
99
  findOne: <TQuery extends FindOneOptionsBase<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>> | undefined = undefined>(options?: TQuery) => UseQueryOptions<ApplyQuery<CollectionSelectOf<TApp, K$1>, CollectionRelationsOf<TApp, K$1>, TQuery> | null>;
97
100
  create: MutationBuilder<FirstArg<CollectionCreate<TApp, K$1>>, QueryData<CollectionCreate<TApp, K$1>>>;
@@ -111,7 +114,9 @@ type CollectionQueryOptionsAPI<TApp extends QuestpieApp, K$1 extends CollectionK
111
114
  success: boolean;
112
115
  count: number;
113
116
  }>;
114
- };
117
+ } & ([CollectionPurgeById<TApp, K$1>] extends [never] ? {} : {
118
+ purgeById: MutationBuilder<FirstArg<CollectionPurgeById<TApp, K$1>>, QueryData<CollectionPurgeById<TApp, K$1>>>;
119
+ });
115
120
  type GlobalQueryOptionsAPI<TApp extends QuestpieApp, K$1 extends GlobalKeys<TApp>> = {
116
121
  /** Generic per call — mirrors the direct client's `get()` narrowing. */
117
122
  get: <TQuery extends {
@@ -176,4 +181,4 @@ type QuestpieQueryOptionsProxy<TApp extends QuestpieApp = QuestpieApp> = {
176
181
  */
177
182
  declare function createQuestpieQueryOptions<TApp extends QuestpieApp = QuestpieApp>(client: QuestpieClient<TApp>, config?: QuestpieQueryOptionsConfig): QuestpieQueryOptionsProxy<TApp>;
178
183
  //#endregion
179
- export { type DefaultError, type QueryKey, QuestpieQueryErrorMap, QuestpieQueryOptionsConfig, QuestpieQueryOptionsProxy, type RealtimeAPI, RealtimeQueryConfig, type TopicConfig, type UseMutationOptions, type UseQueryOptions, buildCollectionTopic, buildGlobalTopic, createQuestpieQueryOptions };
184
+ export { type DefaultError, type QueryKey, QuestpieQueryErrorMap, QuestpieQueryOptionsConfig, QuestpieQueryOptionsProxy, type RealtimeAPI, RealtimeQueryConfig, type TopicConfig, type UseMutationOptions, type UseQueryOptions, applyRealtimeFindEvent, applyRealtimeScalarEvent, applyRealtimeSingleEvent, buildCollectionTopic, buildGlobalTopic, createQuestpieQueryOptions };
package/dist/index.mjs CHANGED
@@ -1,8 +1,27 @@
1
1
  import { experimental_streamedQuery, mutationOptions, queryOptions } from "@tanstack/react-query";
2
- import { buildCollectionTopic, buildCollectionTopic as buildCollectionTopic$1, buildGlobalTopic, buildGlobalTopic as buildGlobalTopic$1 } from "questpie/client";
2
+ import { applyRealtimeFindEvent, applyRealtimeFindEvent as applyRealtimeFindEvent$1, applyRealtimeScalarEvent, applyRealtimeScalarEvent as applyRealtimeScalarEvent$1, applyRealtimeSingleEvent, applyRealtimeSingleEvent as applyRealtimeSingleEvent$1, buildCollectionTopic, buildCollectionTopic as buildCollectionTopic$1, buildGlobalTopic, buildGlobalTopic as buildGlobalTopic$1 } from "questpie/client";
3
3
 
4
4
  //#region src/index.ts
5
5
  const defaultErrorMap = (error) => error instanceof Error ? error : new Error(typeof error === "string" ? error : "Unknown error");
6
+ const isNonRetryableError = (error) => Boolean(error && typeof error === "object" && "retryable" in error && error.retryable === false);
7
+ const mapRealtimeError = (error, errorMap) => {
8
+ const mapped = errorMap(error);
9
+ if (isNonRetryableError(error) && (!mapped || typeof mapped !== "object" || !("retryable" in mapped))) {
10
+ if (mapped && typeof mapped === "object" && Object.isExtensible(mapped)) {
11
+ Object.defineProperty(mapped, "retryable", {
12
+ value: false,
13
+ enumerable: true
14
+ });
15
+ return mapped;
16
+ }
17
+ return Object.assign(/* @__PURE__ */ new Error("Non-retryable realtime query failed"), {
18
+ cause: mapped,
19
+ retryable: false
20
+ });
21
+ }
22
+ return mapped;
23
+ };
24
+ const realtimeRetry = (failureCount, error) => !isNonRetryableError(error) && failureCount < 3;
6
25
  const buildKey = (prefix, parts) => prefix.length ? [...prefix, ...parts] : parts;
7
26
  const sanitizeKeyPart = (value) => {
8
27
  if (value === null || value === void 0) return value;
@@ -41,9 +60,9 @@ const wrapMutationFn = (mutationFn, errorMap) => {
41
60
  async function* streamRealtimeQuery(options) {
42
61
  const { realtime, topic, signal, errorMap } = options;
43
62
  try {
44
- for await (const snapshot of realtime.stream(topic, signal)) yield snapshot;
63
+ for await (const event of realtime.streamEvents(topic, signal)) yield event;
45
64
  } catch (error) {
46
- throw errorMap(error);
65
+ throw mapRealtimeError(error, errorMap);
47
66
  }
48
67
  }
49
68
  /**
@@ -87,9 +106,14 @@ function createQuestpieQueryOptions(client, config = {}) {
87
106
  normalizeQueryKeyOptions(options)
88
107
  ]);
89
108
  if (queryConfig?.realtime && client.realtime) {
109
+ const { realtime: _realtime, ...overrides } = queryConfig;
90
110
  const topic = buildCollectionTopic$1(collectionName, options);
91
111
  return queryOptions({
92
112
  queryKey: qKey,
113
+ retry: realtimeRetry,
114
+ staleTime: 0,
115
+ refetchOnMount: "always",
116
+ ...overrides,
93
117
  queryFn: experimental_streamedQuery({
94
118
  streamFn: ({ signal }) => streamRealtimeQuery({
95
119
  realtime: client.realtime,
@@ -97,7 +121,7 @@ function createQuestpieQueryOptions(client, config = {}) {
97
121
  signal,
98
122
  errorMap
99
123
  }),
100
- reducer: (_, chunk) => chunk,
124
+ reducer: applyRealtimeFindEvent$1,
101
125
  initialValue: void 0,
102
126
  refetchMode: "append"
103
127
  })
@@ -117,9 +141,14 @@ function createQuestpieQueryOptions(client, config = {}) {
117
141
  normalizeQueryKeyOptions(options)
118
142
  ]);
119
143
  if (queryConfig?.realtime && client.realtime) {
144
+ const { realtime: _realtime, ...overrides } = queryConfig;
120
145
  const topic = buildCollectionTopic$1(collectionName, options, "count");
121
146
  return queryOptions({
122
147
  queryKey: qKey,
148
+ retry: realtimeRetry,
149
+ staleTime: 0,
150
+ refetchOnMount: "always",
151
+ ...overrides,
123
152
  queryFn: experimental_streamedQuery({
124
153
  streamFn: ({ signal }) => streamRealtimeQuery({
125
154
  realtime: client.realtime,
@@ -127,7 +156,7 @@ function createQuestpieQueryOptions(client, config = {}) {
127
156
  signal,
128
157
  errorMap
129
158
  }),
130
- reducer: (_, chunk) => chunk,
159
+ reducer: (current, event) => applyRealtimeScalarEvent$1(current, event),
131
160
  initialValue: void 0,
132
161
  refetchMode: "append"
133
162
  })
@@ -196,6 +225,18 @@ function createQuestpieQueryOptions(client, config = {}) {
196
225
  ...stage ? { stage } : {}
197
226
  } : void 0), errorMap)
198
227
  }),
228
+ purgeById: () => mutationOptions({
229
+ mutationKey: buildKey(keyPrefix, [
230
+ ...baseKey,
231
+ "purgeById",
232
+ locale,
233
+ stage
234
+ ]),
235
+ mutationFn: wrapMutationFn((variables) => collection.purgeById(variables, locale || stage ? {
236
+ ...locale ? { locale } : {},
237
+ ...stage ? { stage } : {}
238
+ } : void 0), errorMap)
239
+ }),
199
240
  findVersions: (params) => queryOptions({
200
241
  queryKey: buildKey(keyPrefix, [
201
242
  ...baseKey,
@@ -273,9 +314,14 @@ function createQuestpieQueryOptions(client, config = {}) {
273
314
  normalizeQueryKeyOptions(options)
274
315
  ]);
275
316
  if (queryConfig?.realtime && client.realtime) {
317
+ const { realtime: _realtime, ...overrides } = queryConfig;
276
318
  const topic = buildGlobalTopic$1(globalName, options);
277
319
  return queryOptions({
278
320
  queryKey: qKey,
321
+ retry: realtimeRetry,
322
+ staleTime: 0,
323
+ refetchOnMount: "always",
324
+ ...overrides,
279
325
  queryFn: experimental_streamedQuery({
280
326
  streamFn: ({ signal }) => streamRealtimeQuery({
281
327
  realtime: client.realtime,
@@ -283,7 +329,7 @@ function createQuestpieQueryOptions(client, config = {}) {
283
329
  signal,
284
330
  errorMap
285
331
  }),
286
- reducer: (_, chunk) => chunk,
332
+ reducer: (current, event) => applyRealtimeSingleEvent$1(current, event),
287
333
  initialValue: void 0,
288
334
  refetchMode: "append"
289
335
  })
@@ -451,4 +497,4 @@ function createQuestpieQueryOptions(client, config = {}) {
451
497
  }
452
498
 
453
499
  //#endregion
454
- export { buildCollectionTopic, buildGlobalTopic, createQuestpieQueryOptions };
500
+ export { applyRealtimeFindEvent, applyRealtimeScalarEvent, applyRealtimeSingleEvent, buildCollectionTopic, buildGlobalTopic, createQuestpieQueryOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@questpie/tanstack-query",
3
- "version": "3.15.2",
3
+ "version": "3.17.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/questpie/questpie.git",
@@ -32,15 +32,17 @@
32
32
  },
33
33
  "scripts": {
34
34
  "build": "tsdown",
35
- "check-types": "tsc --noEmit"
35
+ "check-types": "tsc --noEmit",
36
+ "test": "bun test"
36
37
  },
37
38
  "devDependencies": {
38
39
  "bun-types": "latest",
40
+ "seroval": "1.5.4",
39
41
  "tsdown": "^0.18.3",
40
42
  "zod": "^4.2.1"
41
43
  },
42
44
  "peerDependencies": {
43
45
  "@tanstack/react-query": "^5.0.0",
44
- "questpie": "^3.0.0"
46
+ "questpie": "^3.17.0"
45
47
  }
46
48
  }