@braine/quantum-query 1.3.2 → 1.3.4

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/dist/index.cjs CHANGED
@@ -21,9 +21,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  HydrationBoundary: () => HydrationBoundary,
24
+ Match: () => Match,
25
+ MutationCache: () => MutationCache,
24
26
  QuantumDevTools: () => QuantumDevTools,
25
27
  QueryClient: () => QueryClient,
26
28
  QueryClientProvider: () => QueryClientProvider,
29
+ QueryMatch: () => QueryMatch,
27
30
  SignalValue: () => SignalValue,
28
31
  atom: () => atom,
29
32
  createHttpClient: () => createHttpClient,
@@ -120,7 +123,7 @@ function atom(initialValue, options) {
120
123
  return s;
121
124
  }
122
125
  function setupPersistence(s, options) {
123
- const { key, storage = "local", debug } = options;
126
+ const { key, storage = "local", debug, hydrateSync } = options;
124
127
  if (!key) return;
125
128
  let engine = null;
126
129
  if (typeof storage === "string") {
@@ -131,27 +134,34 @@ function setupPersistence(s, options) {
131
134
  engine = storage;
132
135
  }
133
136
  if (!engine) return;
134
- try {
135
- const stored = engine.getItem(key);
136
- const applyValue = (val) => {
137
- try {
138
- const parsed = JSON.parse(val);
139
- const validated = options.validate ? options.validate(parsed) : parsed;
140
- s.set(validated);
141
- if (debug) console.log(`[Quantum] Hydrated atom '${key}'`);
142
- } catch (e) {
143
- if (debug) console.error(`[Quantum] Hydration validation failed for '${key}'`, e);
137
+ const hydrate2 = () => {
138
+ try {
139
+ const stored = engine?.getItem(key);
140
+ const applyValue = (val) => {
141
+ try {
142
+ const parsed = JSON.parse(val);
143
+ const validated = options.validate ? options.validate(parsed) : parsed;
144
+ s.set(validated);
145
+ if (debug) console.log(`[Quantum] Hydrated atom '${key}'`);
146
+ } catch (e) {
147
+ if (debug) console.error(`[Quantum] Hydration validation failed for '${key}'`, e);
148
+ }
149
+ };
150
+ if (stored instanceof Promise) {
151
+ stored.then((val) => {
152
+ if (val) applyValue(val);
153
+ });
154
+ } else if (stored) {
155
+ applyValue(stored);
144
156
  }
145
- };
146
- if (stored instanceof Promise) {
147
- stored.then((val) => {
148
- if (val) applyValue(val);
149
- });
150
- } else if (stored) {
151
- applyValue(stored);
157
+ } catch (err) {
158
+ if (debug) console.error(`[Quantum] Hydration error`, err);
152
159
  }
153
- } catch (err) {
154
- if (debug) console.error(`[Quantum] Hydration error`, err);
160
+ };
161
+ if (hydrateSync) {
162
+ hydrate2();
163
+ } else {
164
+ Promise.resolve().then(hydrate2);
155
165
  }
156
166
  s.subscribe((value) => {
157
167
  try {
@@ -182,6 +192,19 @@ function SignalValue({ signal: signal2, render, children }) {
182
192
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: renderer(value) });
183
193
  }
184
194
 
195
+ // src/react/QueryMatch.tsx
196
+ var import_react2 = require("react");
197
+ var import_jsx_runtime2 = require("react/jsx-runtime");
198
+ function QueryMatch({ signal: signal2, selector, children }) {
199
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SignalValue, { signal: signal2, children: (value) => {
200
+ const selected = selector(value);
201
+ return children(selected);
202
+ } });
203
+ }
204
+ function Match({ signal: signal2, when, children }) {
205
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SignalValue, { signal: signal2, children: (value) => when(value) ? children : null });
206
+ }
207
+
185
208
  // src/store/scheduler.ts
186
209
  var pending = /* @__PURE__ */ new Set();
187
210
  var timer = null;
@@ -214,16 +237,16 @@ function enableDevTools(store, name = "Store") {
214
237
  }
215
238
 
216
239
  // src/devtools/index.tsx
217
- var import_react5 = require("react");
240
+ var import_react6 = require("react");
218
241
 
219
242
  // src/devtools/QueryPanel.tsx
220
- var import_react4 = require("react");
243
+ var import_react5 = require("react");
221
244
 
222
245
  // src/query/useQueryStore.ts
223
- var import_react3 = require("react");
246
+ var import_react4 = require("react");
224
247
 
225
248
  // src/query/context.tsx
226
- var import_react2 = require("react");
249
+ var import_react3 = require("react");
227
250
 
228
251
  // src/query/utils.ts
229
252
  function stableHash(value, depth = 0) {
@@ -245,41 +268,90 @@ function stableHash(value, depth = 0) {
245
268
  const keys = Object.keys(value).sort();
246
269
  return `object:{${keys.map((key) => `${key}:${stableHash(value[key], depth + 1)}`).join(",")}}`;
247
270
  }
248
- function isDeepEqual(a, b) {
249
- if (a === b) return true;
250
- if (a && b && typeof a === "object" && typeof b === "object") {
251
- const objA = a;
252
- const objB = b;
253
- if (objA.constructor !== objB.constructor) return false;
254
- let length, i, keys;
255
- if (Array.isArray(a)) {
256
- if (!Array.isArray(b)) return false;
257
- length = a.length;
258
- if (length !== b.length) return false;
259
- for (i = length; i-- !== 0; ) {
260
- if (!isDeepEqual(a[i], b[i])) return false;
271
+
272
+ // src/query/trie.ts
273
+ var TrieNode = class {
274
+ children = /* @__PURE__ */ new Map();
275
+ keys = /* @__PURE__ */ new Set();
276
+ // Stores the full hashed keys valid at this path
277
+ };
278
+ var QueryKeyTrie = class {
279
+ root = new TrieNode();
280
+ insert(queryKey, hashedKey) {
281
+ const parts = this.normalizeParts(queryKey);
282
+ let node = this.root;
283
+ for (const part of parts) {
284
+ const hash = stableHash(part);
285
+ let child = node.children.get(hash);
286
+ if (!child) {
287
+ child = new TrieNode();
288
+ node.children.set(hash, child);
289
+ }
290
+ node = child;
291
+ }
292
+ node.keys.add(hashedKey);
293
+ }
294
+ remove(queryKey, hashedKey) {
295
+ const parts = this.normalizeParts(queryKey);
296
+ this.removeRecursive(this.root, parts, 0, hashedKey);
297
+ }
298
+ removeRecursive(node, parts, index, hashedKey) {
299
+ if (index === parts.length) {
300
+ node.keys.delete(hashedKey);
301
+ return node.children.size === 0 && node.keys.size === 0;
302
+ }
303
+ const part = parts[index];
304
+ const hash = stableHash(part);
305
+ const child = node.children.get(hash);
306
+ if (child) {
307
+ const shouldDeleteChild = this.removeRecursive(child, parts, index + 1, hashedKey);
308
+ if (shouldDeleteChild) {
309
+ node.children.delete(hash);
261
310
  }
262
- return true;
263
311
  }
264
- if (objA.valueOf !== Object.prototype.valueOf) return objA.valueOf() === objB.valueOf();
265
- if (objA.toString !== Object.prototype.toString) return objA.toString() === objB.toString();
266
- keys = Object.keys(objA);
267
- length = keys.length;
268
- if (length !== Object.keys(objB).length) return false;
269
- for (const key of keys) {
270
- if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;
312
+ return node.children.size === 0 && node.keys.size === 0;
313
+ }
314
+ /**
315
+ * Get all hashed keys that match the given partial query key (prefix)
316
+ */
317
+ getMatchingKeys(partialKey) {
318
+ const parts = this.normalizeParts(partialKey);
319
+ let node = this.root;
320
+ for (const part of parts) {
321
+ const hash = stableHash(part);
322
+ const child = node.children.get(hash);
323
+ if (!child) {
324
+ return /* @__PURE__ */ new Set();
325
+ }
326
+ node = child;
327
+ }
328
+ const results = /* @__PURE__ */ new Set();
329
+ this.collectKeys(node, results);
330
+ return results;
331
+ }
332
+ collectKeys(node, results) {
333
+ for (const key of node.keys) {
334
+ results.add(key);
271
335
  }
272
- for (const key of keys) {
273
- if (!isDeepEqual(objA[key], objB[key])) return false;
336
+ for (const child of node.children.values()) {
337
+ this.collectKeys(child, results);
274
338
  }
275
- return true;
276
339
  }
277
- return a !== a && b !== b;
278
- }
340
+ normalizeParts(queryKey) {
341
+ if (Array.isArray(queryKey)) {
342
+ return queryKey;
343
+ }
344
+ if (queryKey && typeof queryKey === "object" && "key" in queryKey) {
345
+ const qk = queryKey;
346
+ return [qk.key, qk.params];
347
+ }
348
+ return [queryKey];
349
+ }
350
+ };
279
351
 
280
352
  // src/query/queryStorage.ts
281
353
  var QueryStorage = class {
282
- // Tracks access order (least to most recent)
354
+ // 10/10: O(K) Lookup
283
355
  // Default configuration
284
356
  constructor(defaultStaleTime = 5 * 60 * 1e3, defaultCacheTime = 5 * 60 * 1e3, maxSize = 100) {
285
357
  this.defaultStaleTime = defaultStaleTime;
@@ -289,6 +361,8 @@ var QueryStorage = class {
289
361
  signals = /* @__PURE__ */ new Map();
290
362
  gcTimers = /* @__PURE__ */ new Map();
291
363
  lruOrder = /* @__PURE__ */ new Set();
364
+ // Tracks access order (least to most recent)
365
+ trie = new QueryKeyTrie();
292
366
  generateKey(queryKey) {
293
367
  const key = Array.isArray(queryKey) ? stableHash(queryKey) : stableHash([queryKey.key, queryKey.params]);
294
368
  return key;
@@ -363,6 +437,7 @@ var QueryStorage = class {
363
437
  }
364
438
  this.enforceMaxSize();
365
439
  }
440
+ this.trie.insert(entry.key, key);
366
441
  }
367
442
  delete(key) {
368
443
  const entry = this.signals.get(key)?.get();
@@ -375,6 +450,9 @@ var QueryStorage = class {
375
450
  }
376
451
  }
377
452
  }
453
+ if (entry?.key) {
454
+ this.trie.remove(entry.key, key);
455
+ }
378
456
  this.signals.delete(key);
379
457
  this.lruOrder.delete(key);
380
458
  this.cancelGC(key);
@@ -399,6 +477,7 @@ var QueryStorage = class {
399
477
  clear() {
400
478
  this.signals.clear();
401
479
  this.tagIndex.clear();
480
+ this.trie = new QueryKeyTrie();
402
481
  this.lruOrder.clear();
403
482
  this.gcTimers.forEach((timer2) => clearTimeout(timer2));
404
483
  this.gcTimers.clear();
@@ -951,11 +1030,26 @@ var QueryClient = class {
951
1030
  });
952
1031
  this.pluginManager.onFetchStart(normalizedKey);
953
1032
  try {
954
- const data = await this.remotes.fetch(key, fn, {
1033
+ const fetchPromise = this.remotes.fetch(key, fn, {
955
1034
  signal: options?.signal,
956
1035
  retry: options?.retry,
957
1036
  retryDelay: options?.retryDelay
958
1037
  });
1038
+ this.storage.set(key, {
1039
+ data: currentEntry?.data,
1040
+ status: currentEntry?.status || "pending",
1041
+ error: null,
1042
+ isFetching: true,
1043
+ fetchDirection: direction,
1044
+ timestamp: currentEntry?.timestamp || Date.now(),
1045
+ staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
1046
+ cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
1047
+ key: queryKey,
1048
+ tags: mergedTags,
1049
+ promise: fetchPromise
1050
+ // Store for Suspense
1051
+ });
1052
+ const data = await fetchPromise;
959
1053
  this.storage.set(key, {
960
1054
  data,
961
1055
  status: "success",
@@ -968,7 +1062,9 @@ var QueryClient = class {
968
1062
  staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
969
1063
  cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
970
1064
  key: queryKey,
971
- tags: mergedTags
1065
+ tags: mergedTags,
1066
+ promise: void 0
1067
+ // Clear promise
972
1068
  });
973
1069
  const schema = options?.schema || this.defaultSchema;
974
1070
  const validatedData = validateWithSchema(data, schema);
@@ -986,7 +1082,9 @@ var QueryClient = class {
986
1082
  staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
987
1083
  cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
988
1084
  key: queryKey,
989
- tags: mergedTags
1085
+ tags: mergedTags,
1086
+ promise: void 0
1087
+ // Clear promise
990
1088
  });
991
1089
  this.pluginManager.onFetchError(normalizedKey, err);
992
1090
  throw err;
@@ -996,18 +1094,15 @@ var QueryClient = class {
996
1094
  * Invalidate queries
997
1095
  */
998
1096
  invalidate = (queryKey) => {
999
- const prefix = this.storage.generateKey(queryKey);
1000
1097
  const normalizedKey = this.normalizeKey(queryKey);
1001
1098
  this.pluginManager.onInvalidate(normalizedKey);
1002
- const allKeys = this.storage.getSnapshot().keys();
1003
- for (const key of allKeys) {
1004
- if (key === prefix || key.startsWith(prefix.slice(0, -1))) {
1005
- const signal2 = this.storage.get(key, false);
1006
- if (signal2) {
1007
- const current = signal2.get();
1008
- if (current) {
1009
- signal2.set({ ...current, isInvalidated: true });
1010
- }
1099
+ const matchingKeys = this.storage.trie.getMatchingKeys(queryKey);
1100
+ for (const key of matchingKeys) {
1101
+ const signal2 = this.storage.get(key, false);
1102
+ if (signal2) {
1103
+ const current = signal2.get();
1104
+ if (current) {
1105
+ signal2.set({ ...current, isInvalidated: true });
1011
1106
  }
1012
1107
  }
1013
1108
  }
@@ -1100,16 +1195,16 @@ var QueryClient = class {
1100
1195
  };
1101
1196
 
1102
1197
  // src/query/context.tsx
1103
- var import_jsx_runtime2 = require("react/jsx-runtime");
1104
- var QueryClientContext = (0, import_react2.createContext)(void 0);
1198
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1199
+ var QueryClientContext = (0, import_react3.createContext)(void 0);
1105
1200
  var QueryClientProvider = ({
1106
1201
  client,
1107
1202
  children
1108
1203
  }) => {
1109
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(QueryClientContext.Provider, { value: client, children: children || null });
1204
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(QueryClientContext.Provider, { value: client, children: children || null });
1110
1205
  };
1111
1206
  var useQueryClient = () => {
1112
- const client = (0, import_react2.useContext)(QueryClientContext);
1207
+ const client = (0, import_react3.useContext)(QueryClientContext);
1113
1208
  if (!client) {
1114
1209
  throw new Error("No QueryClient set, use QueryClientProvider to set one");
1115
1210
  }
@@ -1119,8 +1214,8 @@ var useQueryClient = () => {
1119
1214
  // src/query/useQueryStore.ts
1120
1215
  function useQueryStore() {
1121
1216
  const client = useQueryClient();
1122
- const [cache, setCache] = (0, import_react3.useState)(client.getAll());
1123
- (0, import_react3.useEffect)(() => {
1217
+ const [cache, setCache] = (0, import_react4.useState)(client.getAll());
1218
+ (0, import_react4.useEffect)(() => {
1124
1219
  const interval = setInterval(() => {
1125
1220
  setCache(new Map(client.getAll()));
1126
1221
  }, 500);
@@ -1130,13 +1225,13 @@ function useQueryStore() {
1130
1225
  }
1131
1226
 
1132
1227
  // src/devtools/QueryPanel.tsx
1133
- var import_jsx_runtime3 = require("react/jsx-runtime");
1228
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1134
1229
  function QueryPanel() {
1135
1230
  const cache = useQueryStore();
1136
1231
  const client = useQueryClient();
1137
- const [filter, setFilter] = (0, import_react4.useState)("");
1138
- const entries = (0, import_react4.useMemo)(() => Array.from(cache.entries()), [cache]);
1139
- const filteredEntries = (0, import_react4.useMemo)(() => {
1232
+ const [filter, setFilter] = (0, import_react5.useState)("");
1233
+ const entries = (0, import_react5.useMemo)(() => Array.from(cache.entries()), [cache]);
1234
+ const filteredEntries = (0, import_react5.useMemo)(() => {
1140
1235
  if (!filter) return entries;
1141
1236
  const search = filter.toLowerCase();
1142
1237
  return entries.filter(([_, entry]) => {
@@ -1147,15 +1242,15 @@ function QueryPanel() {
1147
1242
  return JSON.stringify(key).toLowerCase().includes(search);
1148
1243
  });
1149
1244
  }, [entries, filter]);
1150
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
1151
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1245
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [
1246
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1152
1247
  padding: "8px",
1153
1248
  borderBottom: "1px solid #222",
1154
1249
  background: "#0f0f0f",
1155
1250
  display: "flex",
1156
1251
  gap: "8px"
1157
1252
  }, children: [
1158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1253
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1159
1254
  "input",
1160
1255
  {
1161
1256
  type: "text",
@@ -1174,7 +1269,7 @@ function QueryPanel() {
1174
1269
  }
1175
1270
  }
1176
1271
  ),
1177
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1272
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1178
1273
  "button",
1179
1274
  {
1180
1275
  onClick: () => client.invalidateAll(),
@@ -1191,7 +1286,7 @@ function QueryPanel() {
1191
1286
  }
1192
1287
  )
1193
1288
  ] }),
1194
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: {
1289
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
1195
1290
  flex: 1,
1196
1291
  overflowY: "auto",
1197
1292
  padding: "8px",
@@ -1199,7 +1294,7 @@ function QueryPanel() {
1199
1294
  flexDirection: "column",
1200
1295
  gap: "8px",
1201
1296
  background: "#050505"
1202
- }, children: entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: "20px", textAlign: "center", color: "#444", fontSize: "12px" }, children: "No active queries." }) : filteredEntries.map(([keyHash, entry]) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1297
+ }, children: entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { padding: "20px", textAlign: "center", color: "#444", fontSize: "12px" }, children: "No active queries." }) : filteredEntries.map(([keyHash, entry]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1203
1298
  QueryItem,
1204
1299
  {
1205
1300
  entry,
@@ -1211,14 +1306,14 @@ function QueryPanel() {
1211
1306
  ] });
1212
1307
  }
1213
1308
  function QueryItem({ entry, client, isStale }) {
1214
- const [expanded, setExpanded] = (0, import_react4.useState)(false);
1215
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1309
+ const [expanded, setExpanded] = (0, import_react5.useState)(false);
1310
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1216
1311
  background: "#111",
1217
1312
  borderRadius: "4px",
1218
1313
  border: "1px solid #222",
1219
1314
  overflow: "hidden"
1220
1315
  }, children: [
1221
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1316
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1222
1317
  "div",
1223
1318
  {
1224
1319
  onClick: () => setExpanded(!expanded),
@@ -1231,13 +1326,13 @@ function QueryItem({ entry, client, isStale }) {
1231
1326
  background: expanded ? "#161616" : "transparent"
1232
1327
  },
1233
1328
  children: [
1234
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center", overflow: "hidden" }, children: [
1235
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: {
1329
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px", alignItems: "center", overflow: "hidden" }, children: [
1330
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
1236
1331
  color: isStale ? "#d69e2e" : "#b0fb5d",
1237
1332
  fontSize: "12px",
1238
1333
  fontWeight: "bold"
1239
1334
  }, children: "\u2022" }),
1240
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: {
1335
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
1241
1336
  color: "#e0e0e0",
1242
1337
  fontSize: "12px",
1243
1338
  whiteSpace: "nowrap",
@@ -1245,7 +1340,7 @@ function QueryItem({ entry, client, isStale }) {
1245
1340
  textOverflow: "ellipsis"
1246
1341
  }, children: JSON.stringify(entry.key) })
1247
1342
  ] }),
1248
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: {
1343
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: {
1249
1344
  fontSize: "9px",
1250
1345
  color: isStale ? "#d69e2e" : "#b0fb5d",
1251
1346
  padding: "1px 4px",
@@ -1256,13 +1351,13 @@ function QueryItem({ entry, client, isStale }) {
1256
1351
  ]
1257
1352
  }
1258
1353
  ),
1259
- expanded && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: {
1354
+ expanded && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1260
1355
  padding: "8px",
1261
1356
  borderTop: "1px solid #222",
1262
1357
  background: "#0a0a0a"
1263
1358
  }, children: [
1264
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", gap: "8px", marginBottom: "8px" }, children: [
1265
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1359
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px", marginBottom: "8px" }, children: [
1360
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1266
1361
  "button",
1267
1362
  {
1268
1363
  onClick: (e) => {
@@ -1273,7 +1368,7 @@ function QueryItem({ entry, client, isStale }) {
1273
1368
  children: "Invalidate"
1274
1369
  }
1275
1370
  ),
1276
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1371
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1277
1372
  "button",
1278
1373
  {
1279
1374
  onClick: (e) => {
@@ -1285,19 +1380,19 @@ function QueryItem({ entry, client, isStale }) {
1285
1380
  }
1286
1381
  )
1287
1382
  ] }),
1288
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("pre", { style: { margin: 0, fontSize: "10px", color: "#a0a0a0", overflowX: "auto", fontFamily: "monospace" }, children: JSON.stringify(entry.data, null, 2) })
1383
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("pre", { style: { margin: 0, fontSize: "10px", color: "#a0a0a0", overflowX: "auto", fontFamily: "monospace" }, children: JSON.stringify(entry.data, null, 2) })
1289
1384
  ] })
1290
1385
  ] });
1291
1386
  }
1292
1387
 
1293
1388
  // src/devtools/index.tsx
1294
- var import_jsx_runtime4 = require("react/jsx-runtime");
1389
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1295
1390
  function QuantumDevTools() {
1296
- const [isOpen, setIsOpen] = (0, import_react5.useState)(false);
1297
- const [activeTab, setActiveTab] = (0, import_react5.useState)("queries");
1298
- const [height, setHeight] = (0, import_react5.useState)(400);
1299
- const isResizingRef = (0, import_react5.useRef)(false);
1300
- (0, import_react5.useEffect)(() => {
1391
+ const [isOpen, setIsOpen] = (0, import_react6.useState)(false);
1392
+ const [activeTab, setActiveTab] = (0, import_react6.useState)("queries");
1393
+ const [height, setHeight] = (0, import_react6.useState)(400);
1394
+ const isResizingRef = (0, import_react6.useRef)(false);
1395
+ (0, import_react6.useEffect)(() => {
1301
1396
  const handleMouseMove = (e) => {
1302
1397
  if (!isResizingRef.current) return;
1303
1398
  const newHeight = window.innerHeight - e.clientY;
@@ -1315,7 +1410,7 @@ function QuantumDevTools() {
1315
1410
  };
1316
1411
  }, []);
1317
1412
  if (!isOpen) {
1318
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1413
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1319
1414
  "button",
1320
1415
  {
1321
1416
  onClick: () => setIsOpen(true),
@@ -1340,7 +1435,7 @@ function QuantumDevTools() {
1340
1435
  }
1341
1436
  );
1342
1437
  }
1343
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1438
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
1344
1439
  position: "fixed",
1345
1440
  bottom: 0,
1346
1441
  right: 0,
@@ -1353,7 +1448,7 @@ function QuantumDevTools() {
1353
1448
  flexDirection: "column",
1354
1449
  fontFamily: "monospace"
1355
1450
  }, children: [
1356
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1451
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1357
1452
  "div",
1358
1453
  {
1359
1454
  onMouseDown: () => {
@@ -1363,7 +1458,7 @@ function QuantumDevTools() {
1363
1458
  style: { height: "6px", top: "-3px", position: "absolute", width: "100%", cursor: "ns-resize", zIndex: 100 }
1364
1459
  }
1365
1460
  ),
1366
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: {
1461
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
1367
1462
  display: "flex",
1368
1463
  justifyContent: "space-between",
1369
1464
  alignItems: "center",
@@ -1371,20 +1466,20 @@ function QuantumDevTools() {
1371
1466
  background: "#111",
1372
1467
  borderBottom: "1px solid #222"
1373
1468
  }, children: [
1374
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "16px", alignItems: "center" }, children: [
1375
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "#b0fb5d", fontWeight: "bold" }, children: "Quantum DevTools" }),
1376
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "4px", background: "#000", padding: "2px", borderRadius: "4px" }, children: [
1377
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TabButton, { active: activeTab === "queries", onClick: () => setActiveTab("queries"), children: "Queries" }),
1378
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TabButton, { active: activeTab === "state", onClick: () => setActiveTab("state"), children: "State" })
1469
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: "16px", alignItems: "center" }, children: [
1470
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { color: "#b0fb5d", fontWeight: "bold" }, children: "Quantum DevTools" }),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: "4px", background: "#000", padding: "2px", borderRadius: "4px" }, children: [
1472
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TabButton, { active: activeTab === "queries", onClick: () => setActiveTab("queries"), children: "Queries" }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TabButton, { active: activeTab === "state", onClick: () => setActiveTab("state"), children: "State" })
1379
1474
  ] })
1380
1475
  ] }),
1381
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: () => setIsOpen(false), style: { background: "none", border: "none", color: "#666", cursor: "pointer" }, children: "\xD7" })
1476
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: () => setIsOpen(false), style: { background: "none", border: "none", color: "#666", cursor: "pointer" }, children: "\xD7" })
1382
1477
  ] }),
1383
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: activeTab === "queries" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(QueryPanel, {}) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: "State Panel (Under Construction)" }) })
1478
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { flex: 1, overflow: "hidden" }, children: activeTab === "queries" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QueryPanel, {}) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: "State Panel (Under Construction)" }) })
1384
1479
  ] });
1385
1480
  }
1386
1481
  function TabButton({ active, children, onClick }) {
1387
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1482
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1388
1483
  "button",
1389
1484
  {
1390
1485
  onClick,
@@ -1550,6 +1645,8 @@ var RetryMiddleware = async (ctx, next) => {
1550
1645
  let config;
1551
1646
  if (typeof retryConfigRaw === "number") {
1552
1647
  config = { retries: retryConfigRaw };
1648
+ } else if (typeof retryConfigRaw === "boolean") {
1649
+ config = retryConfigRaw ? { retries: 3 } : { retries: 0 };
1553
1650
  } else if (typeof retryConfigRaw === "object" && retryConfigRaw !== null) {
1554
1651
  config = retryConfigRaw;
1555
1652
  } else {
@@ -1739,7 +1836,7 @@ function getPromiseState(promise) {
1739
1836
  }
1740
1837
 
1741
1838
  // src/query/pagination.ts
1742
- var import_react6 = require("react");
1839
+ var import_react7 = require("react");
1743
1840
  function usePaginatedQuery({
1744
1841
  queryKey,
1745
1842
  queryFn,
@@ -1750,18 +1847,18 @@ function usePaginatedQuery({
1750
1847
  retry
1751
1848
  }) {
1752
1849
  const client = useQueryClient();
1753
- const [page, setPage] = (0, import_react6.useState)(0);
1850
+ const [page, setPage] = (0, import_react7.useState)(0);
1754
1851
  const pageQueryKey = [...queryKey, "page", page];
1755
1852
  const pageQueryKeyHash = JSON.stringify(pageQueryKey);
1756
- const subscribe = (0, import_react6.useCallback)((onStoreChange) => {
1853
+ const subscribe = (0, import_react7.useCallback)((onStoreChange) => {
1757
1854
  const signal2 = client.getSignal(pageQueryKey);
1758
1855
  return signal2.subscribe(() => onStoreChange());
1759
1856
  }, [client, pageQueryKeyHash]);
1760
- const getSnapshot = (0, import_react6.useCallback)(() => {
1857
+ const getSnapshot = (0, import_react7.useCallback)(() => {
1761
1858
  const signal2 = client.getSignal(pageQueryKey);
1762
1859
  return signal2.get();
1763
1860
  }, [client, pageQueryKeyHash]);
1764
- const cacheEntry = (0, import_react6.useSyncExternalStore)(subscribe, getSnapshot);
1861
+ const cacheEntry = (0, import_react7.useSyncExternalStore)(subscribe, getSnapshot);
1765
1862
  const data = cacheEntry?.data;
1766
1863
  const status = cacheEntry?.status || "pending";
1767
1864
  const error = cacheEntry?.error || null;
@@ -1778,15 +1875,15 @@ function usePaginatedQuery({
1778
1875
  }
1779
1876
  }
1780
1877
  const hasPrevious = page > 0;
1781
- const queryFnRef = (0, import_react6.useRef)(queryFn);
1782
- const staleTimeRef = (0, import_react6.useRef)(staleTime);
1783
- const cacheTimeRef = (0, import_react6.useRef)(cacheTime);
1784
- (0, import_react6.useEffect)(() => {
1878
+ const queryFnRef = (0, import_react7.useRef)(queryFn);
1879
+ const staleTimeRef = (0, import_react7.useRef)(staleTime);
1880
+ const cacheTimeRef = (0, import_react7.useRef)(cacheTime);
1881
+ (0, import_react7.useEffect)(() => {
1785
1882
  queryFnRef.current = queryFn;
1786
1883
  staleTimeRef.current = staleTime;
1787
1884
  cacheTimeRef.current = cacheTime;
1788
1885
  });
1789
- const fetchPage = (0, import_react6.useCallback)(async (background = false) => {
1886
+ const fetchPage = (0, import_react7.useCallback)(async (background = false) => {
1790
1887
  if (!enabled) return;
1791
1888
  if (!background) {
1792
1889
  const currentEntry = getSnapshot();
@@ -1805,22 +1902,22 @@ function usePaginatedQuery({
1805
1902
  } catch (err) {
1806
1903
  }
1807
1904
  }, [pageQueryKeyHash, enabled, client, getSnapshot, page]);
1808
- (0, import_react6.useEffect)(() => {
1905
+ (0, import_react7.useEffect)(() => {
1809
1906
  if (enabled) {
1810
1907
  fetchPage();
1811
1908
  }
1812
1909
  }, [fetchPage, enabled]);
1813
- const nextPage = (0, import_react6.useCallback)(() => {
1910
+ const nextPage = (0, import_react7.useCallback)(() => {
1814
1911
  if (hasNext) {
1815
1912
  setPage((p) => p + 1);
1816
1913
  }
1817
1914
  }, [hasNext]);
1818
- const previousPage = (0, import_react6.useCallback)(() => {
1915
+ const previousPage = (0, import_react7.useCallback)(() => {
1819
1916
  if (page > 0) {
1820
1917
  setPage((p) => p - 1);
1821
1918
  }
1822
1919
  }, [page]);
1823
- const refetch = (0, import_react6.useCallback)(async () => {
1920
+ const refetch = (0, import_react7.useCallback)(async () => {
1824
1921
  client.invalidate(pageQueryKey);
1825
1922
  await fetchPage();
1826
1923
  }, [pageQueryKeyHash, fetchPage, client]);
@@ -1840,7 +1937,7 @@ function usePaginatedQuery({
1840
1937
  }
1841
1938
 
1842
1939
  // src/query/useQuery.ts
1843
- var import_react7 = require("react");
1940
+ var import_react8 = require("react");
1844
1941
 
1845
1942
  // src/query/focusManager.ts
1846
1943
  var FocusManager = class {
@@ -1976,7 +2073,7 @@ var QueryObserver = class {
1976
2073
  status: selectorError ? "error" : status,
1977
2074
  refetch: this.refetch
1978
2075
  };
1979
- const isDataEqual = lastResult?.data === nextResult.data || isDeepEqual(lastResult?.data, nextResult.data);
2076
+ const isDataEqual = lastResult?.data === nextResult.data;
1980
2077
  if (lastResult && isDataEqual && lastResult.status === nextResult.status && lastResult.isFetching === nextResult.isFetching && lastResult.isStale === nextResult.isStale && lastResult.error === nextResult.error) {
1981
2078
  return lastResult;
1982
2079
  }
@@ -2098,30 +2195,57 @@ var QueryObserver = class {
2098
2195
  // src/query/useQuery.ts
2099
2196
  function useQuery(options) {
2100
2197
  const client = useQueryClient();
2101
- const [observer] = (0, import_react7.useState)(() => new QueryObserver(client, options));
2102
- (0, import_react7.useEffect)(() => {
2198
+ const [observer] = (0, import_react8.useState)(() => new QueryObserver(client, options));
2199
+ (0, import_react8.useEffect)(() => {
2103
2200
  observer.setOptions(options);
2104
2201
  }, [observer, options]);
2105
- (0, import_react7.useEffect)(() => {
2202
+ (0, import_react8.useEffect)(() => {
2106
2203
  return () => {
2107
2204
  observer.destroy();
2108
2205
  };
2109
2206
  }, [observer]);
2110
- const subscribe = (0, import_react7.useCallback)((onStoreChange) => {
2207
+ const subscribe = (0, import_react8.useCallback)((onStoreChange) => {
2111
2208
  return observer.subscribe(onStoreChange);
2112
2209
  }, [observer]);
2113
- const getSnapshot = (0, import_react7.useCallback)(() => {
2210
+ const getSnapshot = (0, import_react8.useCallback)(() => {
2114
2211
  return observer.getSnapshot();
2115
2212
  }, [observer]);
2116
- const result = (0, import_react7.useSyncExternalStore)(subscribe, getSnapshot);
2213
+ const result = (0, import_react8.useSyncExternalStore)(subscribe, getSnapshot);
2117
2214
  return {
2118
2215
  ...result,
2119
2216
  signal: observer.result$
2120
2217
  };
2121
2218
  }
2122
2219
 
2220
+ // src/query/useSuspenseQuery.ts
2221
+ function useSuspenseQuery(options) {
2222
+ const client = useQueryClient();
2223
+ const signal2 = client.getSignal(options.queryKey);
2224
+ const entry = signal2.get();
2225
+ if (entry?.status === "error") {
2226
+ throw entry.error;
2227
+ }
2228
+ if (!entry || entry.status === "pending" && entry.data === void 0) {
2229
+ if (entry?.promise) {
2230
+ throw entry.promise;
2231
+ }
2232
+ const fetchPromise = client.fetch(options.queryKey, options.queryFn, {
2233
+ retry: options.retry,
2234
+ retryDelay: options.retryDelay,
2235
+ tags: options.tags,
2236
+ schema: options.schema
2237
+ });
2238
+ throw fetchPromise;
2239
+ }
2240
+ const query = useQuery(options);
2241
+ return {
2242
+ ...query,
2243
+ data: query.data
2244
+ };
2245
+ }
2246
+
2123
2247
  // src/query/useMutation.ts
2124
- var import_react8 = require("react");
2248
+ var import_react9 = require("react");
2125
2249
 
2126
2250
  // src/query/mutationObserver.ts
2127
2251
  var generateId = () => {
@@ -2238,18 +2362,18 @@ var MutationObserver = class {
2238
2362
  // src/query/useMutation.ts
2239
2363
  function useMutation(options) {
2240
2364
  const client = useQueryClient();
2241
- const [observer] = (0, import_react8.useState)(() => new MutationObserver(client, options));
2242
- (0, import_react8.useEffect)(() => {
2365
+ const [observer] = (0, import_react9.useState)(() => new MutationObserver(client, options));
2366
+ (0, import_react9.useEffect)(() => {
2243
2367
  observer.setOptions(options);
2244
2368
  }, [observer, options]);
2245
- const state = (0, import_react8.useSyncExternalStore)(
2246
- (0, import_react8.useCallback)((cb) => observer.signal.subscribe(cb), [observer]),
2369
+ const state = (0, import_react9.useSyncExternalStore)(
2370
+ (0, import_react9.useCallback)((cb) => observer.signal.subscribe(cb), [observer]),
2247
2371
  () => observer.signal.get()
2248
2372
  );
2249
- const mutateAsync = (0, import_react8.useCallback)((variables) => {
2373
+ const mutateAsync = (0, import_react9.useCallback)((variables) => {
2250
2374
  return observer.mutate(variables);
2251
2375
  }, [observer]);
2252
- const mutate = (0, import_react8.useCallback)((variables) => {
2376
+ const mutate = (0, import_react9.useCallback)((variables) => {
2253
2377
  observer.mutate(variables).catch(() => {
2254
2378
  });
2255
2379
  }, [observer]);
@@ -2268,7 +2392,7 @@ function useMutation(options) {
2268
2392
  }
2269
2393
 
2270
2394
  // src/query/infiniteQuery.ts
2271
- var import_react9 = require("react");
2395
+ var import_react10 = require("react");
2272
2396
 
2273
2397
  // src/query/plugins/logger.ts
2274
2398
  var consoleLogger = {
@@ -2285,7 +2409,7 @@ var InfiniteQueryObserver = class {
2285
2409
  options$;
2286
2410
  result$;
2287
2411
  unsubscribe = null;
2288
- lastFetchTime = 0;
2412
+ abortController = null;
2289
2413
  constructor(client, options) {
2290
2414
  this.client = client;
2291
2415
  this.options$ = createSignal(options);
@@ -2338,8 +2462,7 @@ var InfiniteQueryObserver = class {
2338
2462
  fetchPreviousPage: this.fetchPreviousPage,
2339
2463
  refetch: this.refetch
2340
2464
  };
2341
- const isDataEqual = lastResult?.data === nextResult.data || isDeepEqual(lastResult?.data, nextResult.data);
2342
- if (lastResult && isDataEqual && lastResult.isFetching === nextResult.isFetching && lastResult.status === nextResult.status && lastResult.hasNextPage === nextResult.hasNextPage && lastResult.hasPreviousPage === nextResult.hasPreviousPage) {
2465
+ if (lastResult && lastResult.data === nextResult.data && lastResult.isFetching === nextResult.isFetching && lastResult.status === nextResult.status && lastResult.hasNextPage === nextResult.hasNextPage && lastResult.hasPreviousPage === nextResult.hasPreviousPage && lastResult.error === nextResult.error) {
2343
2466
  return lastResult;
2344
2467
  }
2345
2468
  lastResult = nextResult;
@@ -2351,8 +2474,8 @@ var InfiniteQueryObserver = class {
2351
2474
  const current = this.options$.get();
2352
2475
  if (current === options) return;
2353
2476
  const isKeyEqual = stableHash(current.queryKey) === stableHash(options.queryKey);
2354
- const isConfigEqual = current.enabled === options.enabled && current.staleTime === options.staleTime;
2355
- if (!isKeyEqual || !isConfigEqual || current.getNextPageParam !== options.getNextPageParam || current.getPreviousPageParam !== options.getPreviousPageParam) {
2477
+ const isConfigEqual = current.enabled === options.enabled && current.staleTime === options.staleTime && current.retry === options.retry;
2478
+ if (!isKeyEqual || !isConfigEqual) {
2356
2479
  this.options$.set(options);
2357
2480
  }
2358
2481
  }
@@ -2385,25 +2508,37 @@ var InfiniteQueryObserver = class {
2385
2508
  }
2386
2509
  });
2387
2510
  this.unsubscribe = () => {
2511
+ this.cancel();
2388
2512
  dispose();
2389
2513
  disposeFocus();
2390
2514
  disposeOnline();
2391
2515
  };
2392
2516
  }
2517
+ cancel() {
2518
+ if (this.abortController) {
2519
+ this.abortController.abort();
2520
+ this.abortController = null;
2521
+ }
2522
+ }
2393
2523
  fetchInitial = async (options) => {
2524
+ this.cancel();
2525
+ this.abortController = new AbortController();
2526
+ const signal2 = this.abortController.signal;
2394
2527
  const opts = this.options$.get();
2395
2528
  const infiniteKey = [...opts.queryKey, "__infinite__"];
2396
2529
  try {
2397
2530
  const entrySignal = this.client.getSignal(infiniteKey);
2398
- const data = entrySignal.get()?.data;
2399
- if (data && data.pageParams.length > 0 && !options?.force) {
2400
- const firstParam2 = data.pageParams[0];
2531
+ const currentState = entrySignal.get()?.data;
2532
+ if (currentState && currentState.pageParams.length > 0 && !options?.force) {
2533
+ const firstParam2 = currentState.pageParams[0];
2401
2534
  const firstPage = await opts.queryFn({ pageParam: firstParam2 });
2535
+ if (signal2.aborted) return;
2402
2536
  const latest = entrySignal.get()?.data;
2403
2537
  if (!latest) return;
2404
2538
  const updatedData = {
2405
2539
  ...latest,
2406
2540
  pages: [firstPage, ...latest.pages.slice(1)]
2541
+ // pageParams remain the same
2407
2542
  };
2408
2543
  this.client.set(infiniteKey, updatedData, {
2409
2544
  staleTime: opts.staleTime,
@@ -2413,107 +2548,116 @@ var InfiniteQueryObserver = class {
2413
2548
  }
2414
2549
  const initialParam = opts.initialPageParam;
2415
2550
  const firstParam = initialParam !== void 0 ? initialParam : 0;
2416
- const initialData = await this.client.fetch(infiniteKey, async () => {
2551
+ const initialData = await this.client.fetch(infiniteKey, async (ctx) => {
2417
2552
  const firstPage = await opts.queryFn({ pageParam: firstParam });
2418
2553
  return {
2419
2554
  pages: [firstPage],
2420
2555
  pageParams: [firstParam]
2421
2556
  };
2422
- }, { fetchDirection: "initial", retry: opts.retry });
2423
- this.client.set(infiniteKey, initialData, {
2424
- staleTime: opts.staleTime,
2425
- cacheTime: opts.cacheTime
2557
+ }, {
2558
+ fetchDirection: "initial",
2559
+ retry: opts.retry,
2560
+ signal: signal2
2426
2561
  });
2562
+ if (!signal2.aborted) {
2563
+ this.client.set(infiniteKey, initialData, {
2564
+ staleTime: opts.staleTime,
2565
+ cacheTime: opts.cacheTime
2566
+ });
2567
+ }
2427
2568
  } catch (err) {
2428
- getLogger().error("Initial fetch failed", err);
2569
+ if (!signal2.aborted) {
2570
+ getLogger().error("Initial fetch failed", err);
2571
+ }
2429
2572
  }
2430
2573
  };
2431
2574
  fetchNextPage = async () => {
2432
2575
  const res = this.result$.get();
2433
- const opts = this.options$.get();
2434
2576
  if (!res.hasNextPage || res.isFetching || !res.data) return;
2577
+ this.cancel();
2578
+ this.abortController = new AbortController();
2579
+ const signal2 = this.abortController.signal;
2580
+ const opts = this.options$.get();
2435
2581
  const infiniteKey = [...opts.queryKey, "__infinite__"];
2436
2582
  const lastPage = res.data.pages[res.data.pages.length - 1];
2437
- if (!lastPage) {
2438
- return;
2439
- }
2583
+ if (!lastPage) return;
2440
2584
  const nextPageParam = opts.getNextPageParam?.(lastPage, res.data.pages);
2441
2585
  if (nextPageParam === void 0) return;
2442
2586
  try {
2443
- const updatedData = await this.client.fetch(infiniteKey, async () => {
2587
+ const updatedData = await this.client.fetch(infiniteKey, async (ctx) => {
2444
2588
  const newPage = await opts.queryFn({ pageParam: nextPageParam });
2589
+ if (ctx.signal?.aborted) throw new Error("Aborted");
2445
2590
  const currentData = this.client.getSignal(infiniteKey).get()?.data;
2446
2591
  if (!currentData) throw new Error("Infinite query data missing");
2447
- const updatedParams = [...currentData.pageParams, nextPageParam];
2448
- const nextCursor = opts.getNextPageParam?.(newPage, [...currentData.pages, newPage]);
2449
- if (nextCursor !== void 0) {
2450
- updatedParams.push(nextCursor);
2451
- }
2452
2592
  return {
2453
2593
  pages: [...currentData.pages, newPage],
2454
- pageParams: updatedParams
2594
+ pageParams: [...currentData.pageParams, nextPageParam]
2455
2595
  };
2456
- }, { fetchDirection: "next", retry: opts.retry });
2457
- this.client.set(infiniteKey, updatedData, {
2458
- staleTime: opts.staleTime,
2459
- cacheTime: opts.cacheTime
2596
+ }, {
2597
+ fetchDirection: "next",
2598
+ retry: opts.retry,
2599
+ signal: signal2
2460
2600
  });
2601
+ if (!signal2.aborted) {
2602
+ this.client.set(infiniteKey, updatedData, {
2603
+ staleTime: opts.staleTime,
2604
+ cacheTime: opts.cacheTime
2605
+ });
2606
+ }
2461
2607
  } catch (err) {
2462
- getLogger().error("Fetch next page failed", err);
2608
+ if (!signal2.aborted) {
2609
+ getLogger().error("Fetch next page failed", err);
2610
+ }
2463
2611
  }
2464
2612
  };
2465
2613
  fetchPreviousPage = async () => {
2466
2614
  const res = this.result$.get();
2467
- const opts = this.options$.get();
2468
2615
  if (!res.hasPreviousPage || res.isFetching || !res.data) return;
2616
+ this.cancel();
2617
+ this.abortController = new AbortController();
2618
+ const signal2 = this.abortController.signal;
2619
+ const opts = this.options$.get();
2469
2620
  const infiniteKey = [...opts.queryKey, "__infinite__"];
2470
2621
  const firstPage = res.data.pages[0];
2471
- if (!firstPage) {
2472
- return;
2473
- }
2622
+ if (!firstPage) return;
2474
2623
  const previousPageParam = opts.getPreviousPageParam?.(firstPage, res.data.pages);
2475
2624
  if (previousPageParam === void 0) return;
2476
2625
  try {
2477
- const updatedData = await this.client.fetch(infiniteKey, async () => {
2626
+ const updatedData = await this.client.fetch(infiniteKey, async (ctx) => {
2478
2627
  const newPage = await opts.queryFn({ pageParam: previousPageParam });
2628
+ if (ctx.signal?.aborted) throw new Error("Aborted");
2479
2629
  const currentData = this.client.getSignal(infiniteKey).get()?.data;
2480
2630
  if (!currentData) throw new Error("Infinite query data missing");
2481
2631
  return {
2482
2632
  pages: [newPage, ...currentData.pages],
2483
2633
  pageParams: [previousPageParam, ...currentData.pageParams]
2484
2634
  };
2485
- }, { fetchDirection: "previous", retry: opts.retry });
2486
- this.client.set(infiniteKey, updatedData, {
2487
- staleTime: opts.staleTime,
2488
- cacheTime: opts.cacheTime
2635
+ }, {
2636
+ fetchDirection: "previous",
2637
+ retry: opts.retry,
2638
+ signal: signal2
2489
2639
  });
2640
+ if (!signal2.aborted) {
2641
+ this.client.set(infiniteKey, updatedData, {
2642
+ staleTime: opts.staleTime,
2643
+ cacheTime: opts.cacheTime
2644
+ });
2645
+ }
2490
2646
  } catch (err) {
2491
- getLogger().error("Fetch previous page failed", err);
2647
+ if (!signal2.aborted) {
2648
+ getLogger().error("Fetch previous page failed", err);
2649
+ }
2492
2650
  }
2493
2651
  };
2494
2652
  refetch = async () => {
2653
+ this.cancel();
2495
2654
  const opts = this.options$.get();
2496
2655
  const infiniteKey = [...opts.queryKey, "__infinite__"];
2497
2656
  this.client.invalidate(infiniteKey);
2498
- const initialParam = opts.initialPageParam;
2499
- const firstParam = initialParam !== void 0 ? initialParam : 0;
2500
- try {
2501
- const initialData = await this.client.fetch(infiniteKey, async () => {
2502
- const firstPage = await opts.queryFn({ pageParam: firstParam });
2503
- return {
2504
- pages: [firstPage],
2505
- pageParams: [firstParam]
2506
- };
2507
- }, { fetchDirection: "initial", retry: opts.retry });
2508
- this.client.set(infiniteKey, initialData, {
2509
- staleTime: opts.staleTime,
2510
- cacheTime: opts.cacheTime
2511
- });
2512
- } catch (err) {
2513
- getLogger().error("Refetch failed", err);
2514
- }
2657
+ await this.fetchInitial({ force: true });
2515
2658
  };
2516
2659
  destroy() {
2660
+ this.cancel();
2517
2661
  if (this.unsubscribe) this.unsubscribe();
2518
2662
  }
2519
2663
  };
@@ -2521,26 +2665,26 @@ var InfiniteQueryObserver = class {
2521
2665
  // src/query/infiniteQuery.ts
2522
2666
  function useInfiniteQuery(options) {
2523
2667
  const client = useQueryClient();
2524
- const [observer] = (0, import_react9.useState)(() => new InfiniteQueryObserver(client, options));
2525
- (0, import_react9.useEffect)(() => {
2668
+ const [observer] = (0, import_react10.useState)(() => new InfiniteQueryObserver(client, options));
2669
+ (0, import_react10.useEffect)(() => {
2526
2670
  observer.setOptions(options);
2527
2671
  }, [observer, options]);
2528
- (0, import_react9.useEffect)(() => {
2672
+ (0, import_react10.useEffect)(() => {
2529
2673
  return () => {
2530
2674
  observer.destroy();
2531
2675
  };
2532
2676
  }, [observer]);
2533
- const subscribe = (0, import_react9.useCallback)((onStoreChange) => {
2677
+ const subscribe = (0, import_react10.useCallback)((onStoreChange) => {
2534
2678
  return observer.result$.subscribe(() => onStoreChange());
2535
2679
  }, [observer]);
2536
- const getSnapshot = (0, import_react9.useCallback)(() => {
2680
+ const getSnapshot = (0, import_react10.useCallback)(() => {
2537
2681
  return observer.result$.get();
2538
2682
  }, [observer]);
2539
- return (0, import_react9.useSyncExternalStore)(subscribe, getSnapshot);
2683
+ return (0, import_react10.useSyncExternalStore)(subscribe, getSnapshot);
2540
2684
  }
2541
2685
 
2542
2686
  // src/query/HydrationBoundary.tsx
2543
- var import_react10 = require("react");
2687
+ var import_react11 = require("react");
2544
2688
 
2545
2689
  // src/query/hydration.ts
2546
2690
  function dehydrate(client) {
@@ -2565,53 +2709,26 @@ function hydrate(client, state) {
2565
2709
  }
2566
2710
 
2567
2711
  // src/query/HydrationBoundary.tsx
2568
- var import_jsx_runtime5 = require("react/jsx-runtime");
2712
+ var import_jsx_runtime6 = require("react/jsx-runtime");
2569
2713
  function HydrationBoundary({ state, children }) {
2570
2714
  const client = useQueryClient();
2571
- const hydratedRef = (0, import_react10.useRef)(false);
2715
+ const hydratedRef = (0, import_react11.useRef)(false);
2572
2716
  if (state && !hydratedRef.current) {
2573
2717
  hydrate(client, state);
2574
2718
  hydratedRef.current = true;
2575
2719
  }
2576
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
2577
- }
2578
-
2579
- // src/query/useSuspenseQuery.ts
2580
- function useSuspenseQuery(options) {
2581
- const client = useQueryClient();
2582
- const signal2 = client.getSignal(options.queryKey);
2583
- const entry = signal2.get();
2584
- const shouldSuspend = !entry || entry.status === "pending" && entry.data === void 0;
2585
- if (shouldSuspend) {
2586
- const fetchPromise = client.fetch(
2587
- options.queryKey,
2588
- (ctx) => options.queryFn({ ...ctx, signal: void 0 }),
2589
- { signal: void 0 }
2590
- ).then((data) => {
2591
- client.set(options.queryKey, data);
2592
- return data;
2593
- });
2594
- throw fetchPromise;
2595
- }
2596
- if (entry?.status === "error") {
2597
- throw entry.error;
2598
- }
2599
- const query = useQuery(options);
2600
- return {
2601
- ...query,
2602
- data: query.data
2603
- };
2720
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children });
2604
2721
  }
2605
2722
 
2606
2723
  // src/query/useQuerySignal.ts
2607
- var import_react11 = require("react");
2724
+ var import_react12 = require("react");
2608
2725
  function useQuery$(options) {
2609
2726
  const client = useQueryClient();
2610
- const [observer] = (0, import_react11.useState)(() => new QueryObserver(client, options));
2611
- (0, import_react11.useEffect)(() => {
2727
+ const [observer] = (0, import_react12.useState)(() => new QueryObserver(client, options));
2728
+ (0, import_react12.useEffect)(() => {
2612
2729
  observer.setOptions(options);
2613
2730
  }, [observer, options.enabled, options.staleTime, options.cacheTime, options.queryKey, options.refetchInterval]);
2614
- (0, import_react11.useEffect)(() => {
2731
+ (0, import_react12.useEffect)(() => {
2615
2732
  const unsubscribe = observer.subscribe(() => {
2616
2733
  });
2617
2734
  return () => {
@@ -2623,18 +2740,18 @@ function useQuery$(options) {
2623
2740
  }
2624
2741
 
2625
2742
  // src/query/useQueries.ts
2626
- var import_react12 = require("react");
2743
+ var import_react13 = require("react");
2627
2744
  function useQueries(queries, client) {
2628
2745
  const queryClient = client || globalThis.__QUANTUM_CLIENT__;
2629
2746
  if (!queryClient) {
2630
2747
  throw new Error("[Quantum] No QueryClient found. Wrap your app with QueryClientProvider or pass a client.");
2631
2748
  }
2632
- const [observers] = (0, import_react12.useState)(
2749
+ const [observers] = (0, import_react13.useState)(
2633
2750
  () => queries.map(
2634
2751
  (options) => new QueryObserver(queryClient, options)
2635
2752
  )
2636
2753
  );
2637
- const results = (0, import_react12.useSyncExternalStore)(
2754
+ const results = (0, import_react13.useSyncExternalStore)(
2638
2755
  (callback) => {
2639
2756
  const unsubscribes = observers.map((observer) => observer.subscribe(callback));
2640
2757
  return () => {
@@ -2644,7 +2761,7 @@ function useQueries(queries, client) {
2644
2761
  () => observers.map((observer) => observer.getSnapshot()),
2645
2762
  () => observers.map((observer) => observer.getSnapshot())
2646
2763
  );
2647
- (0, import_react12.useEffect)(() => {
2764
+ (0, import_react13.useEffect)(() => {
2648
2765
  queries.forEach((options, index) => {
2649
2766
  const observer = observers[index];
2650
2767
  if (observer) {
@@ -2652,7 +2769,7 @@ function useQueries(queries, client) {
2652
2769
  }
2653
2770
  });
2654
2771
  }, [queries, observers]);
2655
- (0, import_react12.useEffect)(() => {
2772
+ (0, import_react13.useEffect)(() => {
2656
2773
  return () => {
2657
2774
  observers.forEach((observer) => observer.destroy());
2658
2775
  };
@@ -2683,9 +2800,12 @@ function useCombinedQueries(queries, client) {
2683
2800
  // Annotate the CommonJS export names for ESM import in node:
2684
2801
  0 && (module.exports = {
2685
2802
  HydrationBoundary,
2803
+ Match,
2804
+ MutationCache,
2686
2805
  QuantumDevTools,
2687
2806
  QueryClient,
2688
2807
  QueryClientProvider,
2808
+ QueryMatch,
2689
2809
  SignalValue,
2690
2810
  atom,
2691
2811
  createHttpClient,