@braine/quantum-query 1.2.5 → 1.2.7

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.js CHANGED
@@ -1,293 +1,167 @@
1
- // src/core/scheduler.ts
2
- var pending = /* @__PURE__ */ new Set();
3
- var timer = null;
4
- function flush() {
5
- timer = null;
6
- const tasks = [...pending];
7
- pending.clear();
8
- tasks.forEach((task) => task());
9
- }
10
- function scheduleUpdate(callback) {
11
- pending.add(callback);
12
- if (!timer) {
13
- timer = Promise.resolve().then(flush);
14
- }
15
- }
16
-
17
- // src/core/asyncUtils.ts
18
- var PROMISE_CACHE = /* @__PURE__ */ new WeakMap();
19
- var PROMISE_STATUS = /* @__PURE__ */ new WeakMap();
20
- var PROMISE_ERROR = /* @__PURE__ */ new WeakMap();
21
- function isPromise(value) {
22
- return !!value && typeof value.then === "function";
23
- }
24
- function handlePromise(promise, triggerUpdate) {
25
- if (PROMISE_STATUS.has(promise)) return;
26
- PROMISE_STATUS.set(promise, "pending");
27
- promise.then(
28
- (value) => {
29
- PROMISE_STATUS.set(promise, "fulfilled");
30
- PROMISE_CACHE.set(promise, value);
31
- triggerUpdate();
1
+ // src/signals.ts
2
+ import {
3
+ signal,
4
+ computed as preactComputed,
5
+ effect as preactEffect,
6
+ batch as preactBatch,
7
+ untracked as preactUntracked
8
+ } from "@preact/signals-core";
9
+ function createSignal(initialValue, options) {
10
+ const s = signal(initialValue);
11
+ let subscriberCount = 0;
12
+ return {
13
+ get: () => s.value,
14
+ set: (newValue) => {
15
+ s.value = newValue;
32
16
  },
33
- (error) => {
34
- PROMISE_STATUS.set(promise, "rejected");
35
- PROMISE_ERROR.set(promise, error);
36
- triggerUpdate();
37
- }
38
- );
39
- }
40
- function unwrapPromise(promise) {
41
- const status = PROMISE_STATUS.get(promise);
42
- if (status === "fulfilled") {
43
- return PROMISE_CACHE.get(promise);
44
- } else if (status === "rejected") {
45
- throw PROMISE_ERROR.get(promise);
46
- } else {
47
- throw promise;
48
- }
17
+ subscribe: (fn) => {
18
+ if (subscriberCount === 0) {
19
+ options?.onActive?.();
20
+ }
21
+ subscriberCount++;
22
+ const dispose = preactEffect(() => {
23
+ fn(s.value);
24
+ });
25
+ return () => {
26
+ dispose();
27
+ subscriberCount--;
28
+ if (subscriberCount === 0) {
29
+ options?.onInactive?.();
30
+ }
31
+ };
32
+ },
33
+ isWatched: () => subscriberCount > 0
34
+ };
49
35
  }
50
- function getPromiseState(promise) {
36
+ function computed(fn) {
37
+ const c = preactComputed(fn);
38
+ let subscriberCount = 0;
51
39
  return {
52
- status: PROMISE_STATUS.get(promise) || "pending",
53
- value: PROMISE_CACHE.get(promise),
54
- error: PROMISE_ERROR.get(promise)
40
+ get: () => c.value,
41
+ set: () => {
42
+ throw new Error("[Quantum] Cannot set a computed signal directly.");
43
+ },
44
+ subscribe: (fn2) => {
45
+ subscriberCount++;
46
+ const dispose = preactEffect(() => {
47
+ fn2(c.value);
48
+ });
49
+ return () => {
50
+ dispose();
51
+ subscriberCount--;
52
+ };
53
+ },
54
+ isWatched: () => subscriberCount > 0
55
55
  };
56
56
  }
57
-
58
- // src/core/proxy.ts
59
- var LISTENERS = /* @__PURE__ */ new WeakMap();
60
- var PROXIES = /* @__PURE__ */ new WeakMap();
61
- var PROXY_TO_TARGET = /* @__PURE__ */ new WeakMap();
62
- var activeListener = null;
63
- function setActiveListener(listener) {
64
- activeListener = listener;
65
- }
66
- function getActiveListener() {
67
- return activeListener;
68
- }
69
- var GLOBAL_LISTENERS = /* @__PURE__ */ new WeakMap();
70
- function subscribe(store, callback) {
71
- const target = PROXY_TO_TARGET.get(store) || store;
72
- let listeners = GLOBAL_LISTENERS.get(target);
73
- if (!listeners) {
74
- listeners = /* @__PURE__ */ new Set();
75
- GLOBAL_LISTENERS.set(target, listeners);
76
- }
77
- listeners.add(callback);
78
- return () => listeners?.delete(callback);
57
+ function effect(fn) {
58
+ return preactEffect(fn);
79
59
  }
80
- var handler = {
81
- get(target, prop, receiver) {
82
- if (activeListener) {
83
- let listeners = LISTENERS.get(target);
84
- if (!listeners) {
85
- listeners = /* @__PURE__ */ new Set();
86
- LISTENERS.set(target, listeners);
87
- }
88
- listeners.add(activeListener);
89
- }
90
- const value = Reflect.get(target, prop, receiver);
91
- if (isPromise(value)) {
92
- return unwrapPromise(value);
93
- }
94
- if (typeof value === "object" && value !== null) {
95
- return createState(value);
96
- }
97
- return value;
98
- },
99
- set(target, prop, value, receiver) {
100
- const oldValue = Reflect.get(target, prop, receiver);
101
- if (Object.is(oldValue, value)) return true;
102
- if (isPromise(value)) {
103
- const trigger = () => {
104
- const listeners2 = LISTENERS.get(target);
105
- if (listeners2) listeners2.forEach((l) => l());
106
- };
107
- handlePromise(value, trigger);
108
- }
109
- const result = Reflect.set(target, prop, value, receiver);
110
- const listeners = LISTENERS.get(target);
111
- if (listeners) {
112
- listeners.forEach((l) => l());
113
- }
114
- const globals = GLOBAL_LISTENERS.get(target);
115
- if (globals) {
116
- globals.forEach((cb) => cb(target, prop, value));
117
- }
118
- return result;
119
- },
120
- deleteProperty(target, prop) {
121
- const result = Reflect.deleteProperty(target, prop);
122
- const listeners = LISTENERS.get(target);
123
- if (listeners) {
124
- listeners.forEach((l) => l());
125
- }
126
- return result;
127
- }
128
- };
129
- function createState(initialState) {
130
- if (PROXIES.has(initialState)) {
131
- return PROXIES.get(initialState);
132
- }
133
- const proxy = new Proxy(initialState, handler);
134
- PROXIES.set(initialState, proxy);
135
- PROXY_TO_TARGET.set(proxy, initialState);
136
- return proxy;
60
+ function untracked(fn) {
61
+ return preactUntracked(fn);
137
62
  }
138
63
 
139
- // src/core/model.ts
140
- function debounce(fn, ms) {
141
- let timeout;
142
- return (...args) => {
143
- clearTimeout(timeout);
144
- timeout = setTimeout(() => fn(...args), ms);
145
- };
64
+ // src/store/model.ts
65
+ function atom(initialValue, options) {
66
+ const s = createSignal(initialValue);
67
+ if (options?.key) {
68
+ setupPersistence(s, options);
69
+ }
70
+ return s;
146
71
  }
147
- function defineModel(def) {
148
- const target = def.state;
149
- if (def.actions) {
150
- for (const [key, fn] of Object.entries(def.actions)) {
151
- target[key] = fn;
72
+ function setupPersistence(s, options) {
73
+ const { key, storage = "local", debug } = options;
74
+ if (!key) return;
75
+ let engine = null;
76
+ if (typeof storage === "string") {
77
+ if (typeof window !== "undefined") {
78
+ engine = storage === "local" ? window.localStorage : window.sessionStorage;
152
79
  }
80
+ } else {
81
+ engine = storage;
153
82
  }
154
- if (def.computed) {
155
- for (const [key, getter] of Object.entries(def.computed)) {
156
- if (typeof getter === "function") {
157
- Object.defineProperty(target, key, {
158
- get: function() {
159
- return getter.call(this);
160
- },
161
- enumerable: true,
162
- configurable: true
163
- });
83
+ if (!engine) return;
84
+ try {
85
+ const stored = engine.getItem(key);
86
+ const applyValue = (val) => {
87
+ try {
88
+ const parsed = JSON.parse(val);
89
+ const validated = options.validate ? options.validate(parsed) : parsed;
90
+ s.set(validated);
91
+ if (debug) console.log(`[Quantum] Hydrated atom '${key}'`);
92
+ } catch (e) {
93
+ if (debug) console.error(`[Quantum] Hydration validation failed for '${key}'`, e);
164
94
  }
95
+ };
96
+ if (stored instanceof Promise) {
97
+ stored.then((val) => {
98
+ if (val) applyValue(val);
99
+ });
100
+ } else if (stored) {
101
+ applyValue(stored);
165
102
  }
103
+ } catch (err) {
104
+ if (debug) console.error(`[Quantum] Hydration error`, err);
166
105
  }
167
- const proxy = createState(target);
168
- if (def.persist) {
169
- const { key, storage = "local", paths, debug } = def.persist;
170
- let engine = null;
171
- if (typeof storage === "string") {
172
- if (typeof window !== "undefined") {
173
- engine = storage === "local" ? window.localStorage : window.sessionStorage;
174
- }
175
- } else {
176
- engine = storage;
177
- }
178
- if (engine) {
179
- const hydrate = () => {
180
- const process = (stored) => {
181
- try {
182
- if (stored) {
183
- const parsed = JSON.parse(stored);
184
- Object.assign(proxy, parsed);
185
- if (debug) console.log(`[Quantum] Hydrated '${key}'`, parsed);
186
- }
187
- } catch (err) {
188
- if (debug) console.error(`[Quantum] Hydration Failed for '${key}'`, err);
189
- }
190
- };
191
- try {
192
- const result = engine.getItem(key);
193
- if (result instanceof Promise) {
194
- result.then(process);
195
- } else {
196
- process(result);
197
- }
198
- } catch (err) {
199
- if (debug) console.error(`[Quantum] Storage Access Failed`, err);
200
- }
201
- };
202
- hydrate();
203
- const save = debounce(async () => {
204
- try {
205
- let stateToSave;
206
- if (paths) {
207
- stateToSave = {};
208
- for (const p of paths) {
209
- stateToSave[p] = proxy[p];
210
- }
211
- } else {
212
- stateToSave = {};
213
- const keys = Object.keys(def.state);
214
- for (const k of keys) {
215
- stateToSave[k] = proxy[k];
216
- }
217
- }
218
- const serialized = JSON.stringify(stateToSave);
219
- await engine.setItem(key, serialized);
220
- if (debug) console.log(`[Quantum] Saved '${key}'`);
221
- } catch (err) {
222
- }
223
- }, 100);
224
- subscribe(proxy, () => {
225
- save();
226
- });
106
+ s.subscribe((value) => {
107
+ try {
108
+ const serialized = JSON.stringify(value);
109
+ engine.setItem(key, serialized);
110
+ if (debug) console.log(`[Quantum] Saved atom '${key}'`);
111
+ } catch (err) {
112
+ if (debug) console.error(`[Quantum] Save error`, err);
227
113
  }
114
+ });
115
+ }
116
+
117
+ // src/react/SignalValue.tsx
118
+ import { useEffect, useState } from "react";
119
+ import { Fragment, jsx } from "react/jsx-runtime";
120
+ function SignalValue({ signal: signal2, render, children }) {
121
+ const [value, setValue] = useState(() => signal2.get());
122
+ useEffect(() => {
123
+ const unsubscribe = signal2.subscribe((newValue) => {
124
+ setValue(newValue);
125
+ });
126
+ return () => unsubscribe();
127
+ }, [signal2]);
128
+ const renderer = render || children;
129
+ if (!renderer) return null;
130
+ return /* @__PURE__ */ jsx(Fragment, { children: renderer(value) });
131
+ }
132
+
133
+ // src/store/scheduler.ts
134
+ var pending = /* @__PURE__ */ new Set();
135
+ var timer = null;
136
+ function flush() {
137
+ timer = null;
138
+ const tasks = [...pending];
139
+ pending.clear();
140
+ tasks.forEach((task) => task());
141
+ }
142
+ function scheduleUpdate(callback) {
143
+ pending.add(callback);
144
+ if (!timer) {
145
+ timer = Promise.resolve().then(flush);
228
146
  }
229
- return proxy;
230
147
  }
231
148
 
232
- // src/react/autoHook.ts
233
- import { useSyncExternalStore, useRef, useCallback, useLayoutEffect, useEffect } from "react";
234
- function useStore(store) {
235
- const versionRef = useRef(0);
236
- const notifyRef = useRef(void 0);
237
- const listener = useCallback(() => {
238
- versionRef.current++;
239
- if (notifyRef.current) {
240
- notifyRef.current();
241
- }
242
- }, []);
243
- const subscribe2 = useCallback((onStoreChange) => {
244
- notifyRef.current = onStoreChange;
245
- return () => {
246
- notifyRef.current = void 0;
247
- };
248
- }, []);
249
- const getSnapshot = useCallback(() => versionRef.current, []);
250
- useSyncExternalStore(subscribe2, getSnapshot, getSnapshot);
251
- const proxy = new Proxy(store, {
252
- get(target, prop, receiver) {
253
- const prev = getActiveListener();
254
- setActiveListener(listener);
255
- try {
256
- return Reflect.get(target, prop, receiver);
257
- } finally {
258
- setActiveListener(prev);
259
- }
260
- }
261
- });
262
- return proxy;
149
+ // src/devtools/registry.ts
150
+ var stores = createSignal([]);
151
+ function registerStore(store, name = "Store") {
152
+ const current = stores.get();
153
+ if (!current.find((s) => s.store === store)) {
154
+ stores.set([...current, { name, store }]);
155
+ }
263
156
  }
264
157
 
265
158
  // src/middleware/devtools.ts
266
159
  function enableDevTools(store, name = "Store") {
267
- if (typeof window === "undefined" || !window.__REDUX_DEVTOOLS_EXTENSION__) return;
268
- const devTools = window.__REDUX_DEVTOOLS_EXTENSION__.connect({ name });
269
- devTools.init(store);
270
- subscribe(store, (target, prop, value) => {
271
- devTools.send({ type: `SET_${String(prop)}`, payload: value }, store);
272
- });
273
- }
274
-
275
- // src/core/computed.ts
276
- function computed(fn) {
277
- let value;
278
- let dirty = true;
279
- return {
280
- get value() {
281
- if (dirty) {
282
- value = fn();
283
- dirty = false;
284
- }
285
- return value;
286
- }
287
- };
160
+ registerStore(store, name);
161
+ console.warn("DevTools for Signals are WIP");
288
162
  }
289
163
 
290
- // src/addon/clientTypes.ts
164
+ // src/clientTypes.ts
291
165
  var HttpError = class extends Error {
292
166
  constructor(status, message) {
293
167
  super(message);
@@ -296,7 +170,7 @@ var HttpError = class extends Error {
296
170
  }
297
171
  };
298
172
 
299
- // src/addon/middleware/types.ts
173
+ // src/middleware/types.ts
300
174
  function compose(middleware) {
301
175
  return (ctx, next) => {
302
176
  let index = -1;
@@ -315,7 +189,7 @@ function compose(middleware) {
315
189
  };
316
190
  }
317
191
 
318
- // src/addon/middleware/dedupe.ts
192
+ // src/middleware/dedupe.ts
319
193
  var DedupeMiddleware = async (ctx, next) => {
320
194
  if (ctx.req.method !== "GET") {
321
195
  return next(ctx);
@@ -341,10 +215,10 @@ var DedupeMiddleware = async (ctx, next) => {
341
215
  }
342
216
  };
343
217
 
344
- // src/addon/middleware/cache.ts
218
+ // src/middleware/cache.ts
345
219
  var CacheMiddleware = async (ctx, next) => {
346
220
  const { method, url } = ctx.req;
347
- const { cache: cacheConfig } = ctx.config;
221
+ const cacheConfig = typeof ctx.config.cache === "object" ? ctx.config.cache : void 0;
348
222
  if (method !== "GET" || !cacheConfig?.ttl || cacheConfig.force) {
349
223
  return next(ctx);
350
224
  }
@@ -377,7 +251,7 @@ var CacheMiddleware = async (ctx, next) => {
377
251
  return response;
378
252
  };
379
253
 
380
- // src/addon/middleware/auth.ts
254
+ // src/middleware/auth.ts
381
255
  var AuthMiddleware = async (ctx, next) => {
382
256
  const { auth } = ctx.client.config;
383
257
  if (auth && auth.getToken && !ctx.req.headers.get("Authorization")) {
@@ -422,15 +296,21 @@ var AuthMiddleware = async (ctx, next) => {
422
296
  return response;
423
297
  };
424
298
 
425
- // src/addon/middleware/fetch.ts
299
+ // src/middleware/fetch.ts
426
300
  var FetchMiddleware = async (ctx) => {
427
301
  return fetch(ctx.req);
428
302
  };
429
303
  var delay = (ms) => new Promise((res) => setTimeout(res, ms));
430
304
  var RetryMiddleware = async (ctx, next) => {
431
- const retryConfig = ctx.config.retry;
432
- if (!retryConfig) return next(ctx);
433
- const { retries = 0, baseDelay = 1e3, maxDelay = 3e3 } = typeof retryConfig === "number" ? { retries: retryConfig } : retryConfig;
305
+ const retryConfigRaw = ctx.config.retry;
306
+ if (!retryConfigRaw) return next(ctx);
307
+ let config;
308
+ if (typeof retryConfigRaw === "number") {
309
+ config = { retries: retryConfigRaw };
310
+ } else {
311
+ config = retryConfigRaw;
312
+ }
313
+ const { retries = 0, baseDelay = 1e3, maxDelay = 3e3 } = config;
434
314
  const attempt = async (count) => {
435
315
  try {
436
316
  const response = await next(ctx);
@@ -443,7 +323,7 @@ var RetryMiddleware = async (ctx, next) => {
443
323
  return response;
444
324
  } catch (err) {
445
325
  if (count < retries) {
446
- if (err.name === "AbortError") throw err;
326
+ if (err instanceof Error && err.name === "AbortError") throw err;
447
327
  const d = Math.min(baseDelay * 2 ** count, maxDelay);
448
328
  await delay(d);
449
329
  return attempt(count + 1);
@@ -454,7 +334,7 @@ var RetryMiddleware = async (ctx, next) => {
454
334
  return attempt(0);
455
335
  };
456
336
 
457
- // src/addon/httpClient.ts
337
+ // src/httpClient.ts
458
338
  function createHttpClient(config) {
459
339
  const cache = /* @__PURE__ */ new Map();
460
340
  const inflight = /* @__PURE__ */ new Map();
@@ -489,15 +369,17 @@ function createHttpClient(config) {
489
369
  controller.abort();
490
370
  });
491
371
  }
372
+ const { cache: _c, schema: _s, timeout: _t, retry: _r, ...fetchOptions } = options;
492
373
  let req = new Request(url, {
493
- ...options,
374
+ ...fetchOptions,
494
375
  headers,
495
376
  signal: controller.signal
496
377
  });
497
378
  if (config.interceptors?.request) {
498
379
  const newConfig = await config.interceptors.request({ ...options, headers: Object.fromEntries(headers) });
380
+ const { cache: _nc, schema: _ns, timeout: _nt, retry: _nr, ...newFetchOptions } = newConfig;
499
381
  req = new Request(url, {
500
- ...newConfig,
382
+ ...newFetchOptions,
501
383
  signal: controller.signal
502
384
  });
503
385
  }
@@ -538,8 +420,9 @@ function createHttpClient(config) {
538
420
  if (options.schema.parse) return options.schema.parse(data);
539
421
  if (options.schema.validateSync) return options.schema.validateSync(data);
540
422
  } catch (error) {
541
- if (error.errors || error.name === "ZodError" || error.name === "ValidationError") {
542
- throw new Error(`Validation Error: ${JSON.stringify(error.errors || error.message)}`);
423
+ const err = error;
424
+ if (err.errors || err.name === "ZodError" || err.name === "ValidationError") {
425
+ throw new Error(`Validation Error: ${JSON.stringify(err.errors || err.message)}`);
543
426
  }
544
427
  throw error;
545
428
  }
@@ -569,345 +452,783 @@ function createHttpClient(config) {
569
452
  return client;
570
453
  }
571
454
 
572
- // src/addon/query/utils.ts
573
- function stableHash(value) {
574
- if (value === null || typeof value !== "object") {
575
- return String(value);
455
+ // src/store/asyncUtils.ts
456
+ var PROMISE_CACHE = /* @__PURE__ */ new WeakMap();
457
+ var PROMISE_STATUS = /* @__PURE__ */ new WeakMap();
458
+ var PROMISE_ERROR = /* @__PURE__ */ new WeakMap();
459
+ function isPromise(value) {
460
+ return !!value && typeof value.then === "function";
461
+ }
462
+ function handlePromise(promise, triggerUpdate) {
463
+ if (PROMISE_STATUS.has(promise)) return;
464
+ PROMISE_STATUS.set(promise, "pending");
465
+ promise.then(
466
+ (value) => {
467
+ PROMISE_STATUS.set(promise, "fulfilled");
468
+ PROMISE_CACHE.set(promise, value);
469
+ triggerUpdate();
470
+ },
471
+ (error) => {
472
+ PROMISE_STATUS.set(promise, "rejected");
473
+ PROMISE_ERROR.set(promise, error);
474
+ triggerUpdate();
475
+ }
476
+ );
477
+ }
478
+ function unwrapPromise(promise) {
479
+ const status = PROMISE_STATUS.get(promise);
480
+ if (status === "fulfilled") {
481
+ return PROMISE_CACHE.get(promise);
482
+ } else if (status === "rejected") {
483
+ throw PROMISE_ERROR.get(promise);
484
+ } else {
485
+ throw promise;
486
+ }
487
+ }
488
+ function getPromiseState(promise) {
489
+ return {
490
+ status: PROMISE_STATUS.get(promise) || "pending",
491
+ value: PROMISE_CACHE.get(promise),
492
+ error: PROMISE_ERROR.get(promise)
493
+ };
494
+ }
495
+
496
+ // src/query/utils.ts
497
+ function stableHash(value, depth = 0) {
498
+ if (depth > 15) {
499
+ throw new Error("[Quantum] Query key is too deeply nested. Max depth is 15.");
500
+ }
501
+ if (value === null) return "null";
502
+ if (value === void 0) return "undefined";
503
+ if (typeof value !== "object") {
504
+ const strValue = String(value);
505
+ if (strValue.length > 1e3) {
506
+ return `${typeof value}:[large-string:${strValue.slice(0, 10)}...]`;
507
+ }
508
+ return `${typeof value}:${strValue}`;
576
509
  }
577
510
  if (Array.isArray(value)) {
578
- return "[" + value.map(stableHash).join(",") + "]";
511
+ return `array:[${value.map((v) => stableHash(v, depth + 1)).join(",")}]`;
579
512
  }
580
513
  const keys = Object.keys(value).sort();
581
- return "{" + keys.map((key) => `${key}:${stableHash(value[key])}`).join(",") + "}";
514
+ return `object:{${keys.map((key) => `${key}:${stableHash(value[key], depth + 1)}`).join(",")}}`;
582
515
  }
583
-
584
- // src/addon/signals.ts
585
- var pendingSignals = /* @__PURE__ */ new Set();
586
- var isFlushScheduled = false;
587
- function flushPendingSignals() {
588
- const toFlush = Array.from(pendingSignals);
589
- pendingSignals.clear();
590
- isFlushScheduled = false;
591
- toFlush.forEach((signal) => signal.flush());
592
- }
593
- var SignalImpl = class {
594
- value;
595
- subscribers = /* @__PURE__ */ new Set();
596
- constructor(initialValue) {
597
- this.value = initialValue;
598
- }
599
- get = () => this.value;
600
- set = (newValue) => {
601
- if (this.value === newValue) return;
602
- this.value = newValue;
603
- pendingSignals.add(this);
604
- if (!isFlushScheduled) {
605
- isFlushScheduled = true;
606
- queueMicrotask(flushPendingSignals);
516
+ function isDeepEqual(a, b) {
517
+ if (a === b) return true;
518
+ if (a && b && typeof a === "object" && typeof b === "object") {
519
+ const objA = a;
520
+ const objB = b;
521
+ if (objA.constructor !== objB.constructor) return false;
522
+ let length, i, keys;
523
+ if (Array.isArray(a)) {
524
+ const arrA = a;
525
+ const arrB = b;
526
+ length = arrA.length;
527
+ if (length !== arrB.length) return false;
528
+ for (i = length; i-- !== 0; ) {
529
+ if (!isDeepEqual(arrA[i], arrB[i])) return false;
530
+ }
531
+ return true;
607
532
  }
608
- };
609
- flush() {
610
- const currentValue = this.value;
611
- this.subscribers.forEach((fn) => fn(currentValue));
533
+ if (objA.valueOf !== Object.prototype.valueOf) return objA.valueOf() === objB.valueOf();
534
+ if (objA.toString !== Object.prototype.toString) return objA.toString() === objB.toString();
535
+ keys = Object.keys(objA);
536
+ length = keys.length;
537
+ if (length !== Object.keys(objB).length) return false;
538
+ for (const key of keys) {
539
+ if (!Object.prototype.hasOwnProperty.call(objB, key)) return false;
540
+ }
541
+ for (const key of keys) {
542
+ if (!isDeepEqual(objA[key], objB[key])) return false;
543
+ }
544
+ return true;
612
545
  }
613
- subscribe = (fn) => {
614
- this.subscribers.add(fn);
615
- return () => {
616
- this.subscribers.delete(fn);
617
- };
618
- };
619
- };
620
- function createSignal(initialValue) {
621
- return new SignalImpl(initialValue);
546
+ return a !== a && b !== b;
622
547
  }
623
548
 
624
- // src/addon/query/queryCache.ts
625
- var QueryCache = class {
626
- // Store signals instead of raw values
549
+ // src/query/queryStorage.ts
550
+ var QueryStorage = class {
551
+ // Tracks access order (least to most recent)
552
+ // Default configuration
553
+ constructor(defaultStaleTime = 0, defaultCacheTime = 5 * 60 * 1e3, maxSize = 100) {
554
+ this.defaultStaleTime = defaultStaleTime;
555
+ this.defaultCacheTime = defaultCacheTime;
556
+ this.maxSize = maxSize;
557
+ }
627
558
  signals = /* @__PURE__ */ new Map();
628
- gcInterval = null;
629
- defaultStaleTime = 0;
630
- // Immediately stale
631
- defaultCacheTime = 5 * 60 * 1e3;
632
- // 5 minutes
633
- constructor(config) {
634
- if (config?.enableGC !== false) {
635
- this.startGarbageCollection();
636
- }
559
+ gcTimers = /* @__PURE__ */ new Map();
560
+ lruOrder = /* @__PURE__ */ new Set();
561
+ generateKey(queryKey) {
562
+ const key = Array.isArray(queryKey) ? stableHash(queryKey) : stableHash([queryKey.key, queryKey.params]);
563
+ return key;
637
564
  }
638
- /**
639
- * Generate cache key from query key array
640
- */
641
- generateKey = (queryKey) => {
642
- if (Array.isArray(queryKey)) {
643
- return stableHash(queryKey);
565
+ get(key, autoCreate = true) {
566
+ let signal2 = this.signals.get(key);
567
+ if (signal2) {
568
+ this.touch(key);
644
569
  }
645
- return stableHash([queryKey.key, queryKey.params]);
646
- };
647
- /**
648
- * Get data (wrapper around signal.get)
649
- */
650
- get = (queryKey) => {
651
- const key = this.generateKey(queryKey);
652
- const signal = this.signals.get(key);
653
- if (!signal) return void 0;
654
- const entry = signal.get();
655
- if (!entry) return void 0;
656
- const now = Date.now();
657
- const age = now - entry.timestamp;
658
- if (age > entry.cacheTime) {
659
- this.signals.delete(key);
660
- return void 0;
570
+ if (!signal2 && autoCreate) {
571
+ const newSignal = createSignal(void 0, {
572
+ onActive: () => {
573
+ this.cancelGC(key);
574
+ },
575
+ onInactive: () => {
576
+ const currentEntry = this.signals.get(key)?.get();
577
+ const cacheTime = currentEntry?.cacheTime ?? this.defaultCacheTime;
578
+ this.scheduleGC(key, cacheTime);
579
+ }
580
+ });
581
+ this.signals.set(key, newSignal);
582
+ this.touch(key);
583
+ if (!newSignal.isWatched()) {
584
+ this.scheduleGC(key, this.defaultCacheTime);
585
+ }
586
+ this.enforceMaxSize();
587
+ signal2 = newSignal;
661
588
  }
662
- return entry.data;
663
- };
664
- /**
665
- * Get Signal for a key (Low level API for hooks)
666
- * Automatically creates a signal if one doesn't exist
667
- */
668
- getSignal = (queryKey) => {
669
- const key = this.generateKey(queryKey);
670
- let signal = this.signals.get(key);
671
- if (!signal) {
672
- signal = createSignal(void 0);
673
- this.signals.set(key, signal);
589
+ return signal2;
590
+ }
591
+ tagIndex = /* @__PURE__ */ new Map();
592
+ set(key, entry) {
593
+ this.touch(key);
594
+ const oldEntry = this.signals.get(key)?.get();
595
+ if (oldEntry?.tags) {
596
+ for (const tag of oldEntry.tags) {
597
+ const set = this.tagIndex.get(tag);
598
+ if (set) {
599
+ set.delete(key);
600
+ if (set.size === 0) this.tagIndex.delete(tag);
601
+ }
602
+ }
674
603
  }
675
- return signal;
676
- };
677
- /**
678
- * Check if data is stale
679
- */
680
- isStale = (queryKey) => {
681
- const key = this.generateKey(queryKey);
682
- const signal = this.signals.get(key);
683
- if (!signal) return true;
684
- const entry = signal.get();
685
- if (!entry) return true;
686
- const now = Date.now();
687
- const age = now - entry.timestamp;
688
- return age > entry.staleTime;
689
- };
690
- /**
691
- * Set cached data (updates signal)
692
- */
693
- set = (queryKey, data, options) => {
694
- const key = this.generateKey(queryKey);
695
- const entry = {
696
- data,
697
- timestamp: Date.now(),
698
- staleTime: options?.staleTime !== void 0 ? options.staleTime : this.defaultStaleTime,
699
- cacheTime: options?.cacheTime !== void 0 ? options.cacheTime : this.defaultCacheTime,
700
- key: Array.isArray(queryKey) ? queryKey : [queryKey]
701
- };
702
- const existingSignal = this.signals.get(key);
703
- if (existingSignal) {
704
- existingSignal.set(entry);
705
- } else {
706
- this.signals.set(key, createSignal(entry));
604
+ if (entry.tags) {
605
+ for (const tag of entry.tags) {
606
+ if (!this.tagIndex.has(tag)) {
607
+ this.tagIndex.set(tag, /* @__PURE__ */ new Set());
608
+ }
609
+ this.tagIndex.get(tag).add(key);
610
+ }
707
611
  }
708
- const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
709
- this.plugins.forEach((p) => p.onQueryUpdated?.(normalizedKey, data));
710
- };
711
- // --- DEDUPLICATION ---
712
- deduplicationCache = /* @__PURE__ */ new Map();
713
- // --- MIDDLEWARE / PLUGINS ---
714
- plugins = [];
715
- /**
716
- * Register a middleware plugin
717
- */
718
- use = (plugin) => {
719
- this.plugins.push(plugin);
720
- return this;
721
- };
722
- /**
723
- * Fetch data with deduplication.
724
- * If a request for the same key is already in flight, returns the existing promise.
725
- */
726
- fetch = async (queryKey, fn) => {
727
- const key = this.generateKey(queryKey);
728
- const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
729
- if (this.deduplicationCache.has(key)) {
730
- return this.deduplicationCache.get(key);
612
+ let signal2 = this.signals.get(key);
613
+ if (signal2) {
614
+ signal2.set(entry);
615
+ } else {
616
+ const newSignal = createSignal(entry, {
617
+ onActive: () => {
618
+ this.cancelGC(key);
619
+ },
620
+ onInactive: () => {
621
+ const entry2 = this.signals.get(key)?.get();
622
+ const cacheTime = entry2?.cacheTime ?? this.defaultCacheTime;
623
+ this.scheduleGC(key, cacheTime);
624
+ }
625
+ });
626
+ this.signals.set(key, newSignal);
627
+ if (!newSignal.isWatched()) {
628
+ this.scheduleGC(key, entry.cacheTime ?? this.defaultCacheTime);
629
+ }
630
+ this.enforceMaxSize();
731
631
  }
732
- this.plugins.forEach((p) => p.onFetchStart?.(normalizedKey));
733
- const promise = fn().then(
734
- (data) => {
735
- this.deduplicationCache.delete(key);
736
- this.plugins.forEach((p) => p.onFetchSuccess?.(normalizedKey, data));
737
- return data;
738
- },
739
- (error) => {
740
- this.deduplicationCache.delete(key);
741
- this.plugins.forEach((p) => p.onFetchError?.(normalizedKey, error));
742
- throw error;
632
+ }
633
+ delete(key) {
634
+ const entry = this.signals.get(key)?.get();
635
+ if (entry?.tags) {
636
+ for (const tag of entry.tags) {
637
+ const set = this.tagIndex.get(tag);
638
+ if (set) {
639
+ set.delete(key);
640
+ if (set.size === 0) this.tagIndex.delete(tag);
641
+ }
743
642
  }
744
- );
745
- this.deduplicationCache.set(key, promise);
746
- return promise;
643
+ }
644
+ this.signals.delete(key);
645
+ this.lruOrder.delete(key);
646
+ this.cancelGC(key);
647
+ }
648
+ touch(key) {
649
+ this.lruOrder.delete(key);
650
+ this.lruOrder.add(key);
651
+ }
652
+ enforceMaxSize() {
653
+ if (this.signals.size <= this.maxSize) return;
654
+ for (const key of this.lruOrder) {
655
+ const signal2 = this.signals.get(key);
656
+ if (signal2 && !signal2.isWatched()) {
657
+ this.delete(key);
658
+ if (this.signals.size <= this.maxSize) break;
659
+ }
660
+ }
661
+ }
662
+ getKeysByTag(tag) {
663
+ return this.tagIndex.get(tag);
664
+ }
665
+ clear() {
666
+ this.signals.clear();
667
+ this.tagIndex.clear();
668
+ this.lruOrder.clear();
669
+ this.gcTimers.forEach((timer2) => clearTimeout(timer2));
670
+ this.gcTimers.clear();
671
+ }
672
+ getAll() {
673
+ const map = /* @__PURE__ */ new Map();
674
+ for (const [key, signal2] of this.signals.entries()) {
675
+ const val = signal2.get();
676
+ if (val) map.set(key, val);
677
+ }
678
+ return map;
679
+ }
680
+ getStats() {
681
+ return {
682
+ size: this.signals.size,
683
+ keys: Array.from(this.signals.keys()),
684
+ tags: this.tagIndex.size
685
+ };
686
+ }
687
+ getSnapshot() {
688
+ const snapshot = /* @__PURE__ */ new Map();
689
+ for (const [key, signal2] of this.signals.entries()) {
690
+ const value = signal2.get();
691
+ if (value) {
692
+ snapshot.set(key, value);
693
+ }
694
+ }
695
+ return snapshot;
696
+ }
697
+ // --- GC Logic ---
698
+ scheduleGC(key, delay2) {
699
+ if (this.gcTimers.has(key)) {
700
+ clearTimeout(this.gcTimers.get(key));
701
+ }
702
+ const timer2 = setTimeout(() => {
703
+ this.delete(key);
704
+ }, delay2);
705
+ this.gcTimers.set(key, timer2);
706
+ }
707
+ cancelGC(key) {
708
+ if (this.gcTimers.has(key)) {
709
+ clearTimeout(this.gcTimers.get(key));
710
+ this.gcTimers.delete(key);
711
+ }
712
+ }
713
+ };
714
+
715
+ // src/query/remotes.ts
716
+ var QueryRemotes = class {
717
+ deduplicationCache = /* @__PURE__ */ new Map();
718
+ async fetch(key, fn, options) {
719
+ if (this.deduplicationCache.has(key)) {
720
+ return this.deduplicationCache.get(key);
721
+ }
722
+ const promise = this.executeWithRetry(fn, options);
723
+ this.deduplicationCache.set(key, promise);
724
+ try {
725
+ const result = await promise;
726
+ this.deduplicationCache.delete(key);
727
+ return result;
728
+ } catch (error) {
729
+ this.deduplicationCache.delete(key);
730
+ throw error;
731
+ }
732
+ }
733
+ async executeWithRetry(fn, options) {
734
+ const maxRetries = this.resolveRetry(options?.retry);
735
+ let attempt = 0;
736
+ while (true) {
737
+ attempt++;
738
+ try {
739
+ return await fn({ signal: options?.signal });
740
+ } catch (error) {
741
+ if (attempt > maxRetries || options?.signal && options.signal.aborted) {
742
+ throw error;
743
+ }
744
+ let delay2 = 1e3 * 2 ** (attempt - 1);
745
+ if (options?.retryDelay) {
746
+ delay2 = typeof options.retryDelay === "number" ? options.retryDelay : options.retryDelay(attempt - 1);
747
+ }
748
+ delay2 = Math.min(delay2, 3e4);
749
+ await this.wait(delay2, options?.signal);
750
+ }
751
+ }
752
+ }
753
+ resolveRetry(retry) {
754
+ if (typeof retry === "number") return retry;
755
+ if (retry === false) return 0;
756
+ return 3;
757
+ }
758
+ wait(ms, signal2) {
759
+ return new Promise((resolve, reject) => {
760
+ if (signal2?.aborted) return reject(new Error("Aborted"));
761
+ const timer2 = setTimeout(() => {
762
+ cleanup();
763
+ resolve();
764
+ }, ms);
765
+ const onAbort = () => {
766
+ cleanup();
767
+ reject(new Error("Aborted"));
768
+ };
769
+ const cleanup = () => {
770
+ clearTimeout(timer2);
771
+ signal2?.removeEventListener("abort", onAbort);
772
+ };
773
+ signal2?.addEventListener("abort", onAbort);
774
+ });
775
+ }
776
+ };
777
+
778
+ // src/query/mutationCache.ts
779
+ var MutationCache = class {
780
+ // Map<ID, Signal> - Stores state for every unique mutation execution
781
+ mutations = /* @__PURE__ */ new Map();
782
+ // Map<KeyHash, Set<ID>> - Index to find mutations by key
783
+ mutationKeys = /* @__PURE__ */ new Map();
784
+ /**
785
+ * Get or Create a Signal for a specific mutation instance (ID)
786
+ */
787
+ getSignal = (id) => {
788
+ let signal2 = this.mutations.get(id);
789
+ if (!signal2) {
790
+ const initialState = {
791
+ data: void 0,
792
+ error: null,
793
+ variables: void 0,
794
+ context: void 0,
795
+ status: "idle",
796
+ submittedAt: 0
797
+ };
798
+ signal2 = createSignal(initialState);
799
+ this.mutations.set(id, signal2);
800
+ }
801
+ return signal2;
747
802
  };
748
803
  /**
749
- * Invalidate all queries
804
+ * Register a mutation ID with a Key (for tracking shared keys)
750
805
  */
751
- invalidateAll = () => {
752
- for (const key of this.signals.keys()) {
753
- this.signals.get(key)?.set(void 0);
806
+ register = (id, key) => {
807
+ if (!key) return;
808
+ const hash = stableHash(key);
809
+ if (!this.mutationKeys.has(hash)) {
810
+ this.mutationKeys.set(hash, /* @__PURE__ */ new Set());
754
811
  }
812
+ this.mutationKeys.get(hash).add(id);
755
813
  };
756
814
  /**
757
- * Remove a specific query from cache
815
+ * Unregister cleanup
758
816
  */
759
- remove = (queryKey) => {
760
- const key = this.generateKey(queryKey);
761
- this.signals.delete(key);
817
+ unregister = (id, key) => {
818
+ if (!key) return;
819
+ const hash = stableHash(key);
820
+ const set = this.mutationKeys.get(hash);
821
+ if (set) {
822
+ set.delete(id);
823
+ if (set.size === 0) {
824
+ this.mutationKeys.delete(hash);
825
+ }
826
+ }
827
+ this.mutations.delete(id);
828
+ };
829
+ notify = (id, state) => {
830
+ const signal2 = this.getSignal(id);
831
+ const current = signal2.get();
832
+ signal2.set({ ...current, ...state });
762
833
  };
763
834
  /**
764
- * Invalidate queries matching the key prefix
765
- * Marks them as undefined to trigger refetches without breaking subscriptions
835
+ * Get number of mutations currently pending
766
836
  */
767
- invalidate = (queryKey) => {
768
- const prefix = this.generateKey(queryKey);
769
- const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
770
- this.plugins.forEach((p) => p.onInvalidate?.(normalizedKey));
771
- const invalidateKey = (key) => {
772
- const signal = this.signals.get(key);
773
- if (signal) {
774
- signal.set(void 0);
837
+ isMutating = (filters) => {
838
+ let count = 0;
839
+ if (filters?.mutationKey) {
840
+ const hash = stableHash(filters.mutationKey);
841
+ const ids = this.mutationKeys.get(hash);
842
+ if (!ids) return 0;
843
+ for (const id of ids) {
844
+ if (this.mutations.get(id)?.get().status === "pending") {
845
+ count++;
846
+ }
775
847
  }
776
- };
777
- invalidateKey(prefix);
778
- for (const key of this.signals.keys()) {
779
- if (key.startsWith(prefix.slice(0, -1))) {
780
- invalidateKey(key);
848
+ } else {
849
+ for (const signal2 of this.mutations.values()) {
850
+ if (signal2.get().status === "pending") count++;
781
851
  }
782
852
  }
853
+ return count;
783
854
  };
855
+ };
856
+
857
+ // src/query/pluginManager.ts
858
+ var PluginManager = class {
859
+ plugins = [];
860
+ client;
861
+ setClient(client) {
862
+ this.client = client;
863
+ }
864
+ add(plugin) {
865
+ this.plugins.push(plugin);
866
+ }
867
+ onFetchStart(queryKey) {
868
+ this.plugins.forEach((p) => p.onFetchStart?.(queryKey));
869
+ }
870
+ onFetchSuccess(queryKey, data) {
871
+ this.plugins.forEach((p) => p.onFetchSuccess?.(queryKey, data));
872
+ }
873
+ onFetchError(queryKey, error) {
874
+ this.plugins.forEach((p) => p.onFetchError?.(queryKey, error));
875
+ }
876
+ onInvalidate(queryKey) {
877
+ this.plugins.forEach((p) => p.onInvalidate?.(queryKey));
878
+ }
879
+ onQueryUpdated(queryKey, data) {
880
+ this.plugins.forEach((p) => p.onQueryUpdated?.(queryKey, data));
881
+ }
882
+ };
883
+
884
+ // src/query/plugins/validation.ts
885
+ function validateWithSchema(data, schema) {
886
+ if (!schema) {
887
+ return data;
888
+ }
889
+ return schema.parse(data);
890
+ }
891
+
892
+ // src/query/queryClient.ts
893
+ var QueryClient = class {
894
+ // Components
895
+ storage;
896
+ remotes;
897
+ // Mutation Cache
898
+ mutationCache = new MutationCache();
899
+ // Plugins
900
+ pluginManager = new PluginManager();
901
+ // Config defaults
902
+ defaultStaleTime;
903
+ defaultCacheTime;
904
+ defaultSchema;
905
+ constructor(config) {
906
+ this.defaultStaleTime = config?.defaultStaleTime ?? 0;
907
+ this.defaultCacheTime = config?.defaultCacheTime ?? 5 * 60 * 1e3;
908
+ this.defaultSchema = config?.defaultSchema;
909
+ this.storage = new QueryStorage(
910
+ this.defaultStaleTime,
911
+ this.defaultCacheTime,
912
+ config?.maxCacheSize
913
+ );
914
+ this.remotes = new QueryRemotes();
915
+ this.pluginManager.setClient(this);
916
+ }
917
+ // --- FACADE API ---
784
918
  /**
785
- * Remove all cache entries
919
+ * Get data from storage
786
920
  */
787
- clear = () => {
788
- this.signals.clear();
921
+ get = (queryKey) => {
922
+ const key = this.storage.generateKey(queryKey);
923
+ const signal2 = this.storage.get(key, false);
924
+ if (!signal2) return void 0;
925
+ const entry = signal2.get();
926
+ if (!this.isCacheEntry(entry)) return void 0;
927
+ const now = Date.now();
928
+ const age = now - entry.timestamp;
929
+ if (age > entry.cacheTime) {
930
+ this.storage.delete(key);
931
+ return void 0;
932
+ }
933
+ return entry.data;
789
934
  };
790
935
  /**
791
- * Prefetch data (same as set but explicit intent)
936
+ * Get Signal for reactivity (used by hooks)
792
937
  */
793
- prefetch = (queryKey, data, options) => {
794
- this.set(queryKey, data, options);
938
+ getSignal = (queryKey) => {
939
+ const key = this.storage.generateKey(queryKey);
940
+ return this.storage.get(key, true);
795
941
  };
796
942
  /**
797
- * Garbage collection - remove expired entries
943
+ * Set data manually (Optimistic updates / Prefetch)
798
944
  */
799
- startGarbageCollection = () => {
800
- this.gcInterval = setInterval(() => {
801
- const now = Date.now();
802
- for (const [key, signal] of this.signals.entries()) {
803
- const entry = signal.get();
804
- if (!entry) continue;
805
- const age = now - entry.timestamp;
806
- if (age > entry.cacheTime) {
807
- this.signals.delete(key);
808
- }
809
- }
810
- }, 60 * 1e3);
945
+ set = (queryKey, data, options) => {
946
+ const key = this.storage.generateKey(queryKey);
947
+ const entry = {
948
+ data,
949
+ status: "success",
950
+ error: null,
951
+ isFetching: false,
952
+ fetchDirection: "idle",
953
+ timestamp: Date.now(),
954
+ staleTime: options?.staleTime ?? this.defaultStaleTime,
955
+ cacheTime: options?.cacheTime ?? this.defaultCacheTime,
956
+ key: queryKey,
957
+ tags: options?.tags
958
+ };
959
+ this.storage.set(key, entry);
960
+ const normalizedKey = this.normalizeKey(queryKey);
961
+ this.pluginManager.onQueryUpdated(normalizedKey, data);
811
962
  };
812
963
  /**
813
- * Stop garbage collection
964
+ * Restore cache entry (Hydration)
814
965
  */
815
- destroy = () => {
816
- if (this.gcInterval) {
817
- clearInterval(this.gcInterval);
818
- this.gcInterval = null;
819
- }
820
- this.clear();
966
+ restore = (queryKey, entry) => {
967
+ const key = this.storage.generateKey(queryKey);
968
+ this.storage.set(key, entry);
969
+ const normalizedKey = this.normalizeKey(queryKey);
970
+ this.pluginManager.onQueryUpdated(normalizedKey, entry.data);
821
971
  };
822
972
  /**
823
- * Get cache stats (for debugging)
973
+ * Fetch data (Orchestration)
824
974
  */
825
- getStats = () => {
826
- return {
827
- size: this.signals.size,
828
- keys: Array.from(this.signals.keys())
829
- };
975
+ fetch = async (queryKey, fn, options) => {
976
+ const key = this.storage.generateKey(queryKey);
977
+ const normalizedKey = this.normalizeKey(queryKey);
978
+ const direction = options?.fetchDirection || "initial";
979
+ const signal2 = this.storage.get(key, true);
980
+ const currentEntry = signal2.get();
981
+ const mergedTags = options?.tags ?? currentEntry?.tags;
982
+ this.storage.set(key, {
983
+ data: currentEntry?.data,
984
+ status: currentEntry?.status || "pending",
985
+ error: null,
986
+ isFetching: true,
987
+ fetchDirection: direction,
988
+ timestamp: currentEntry?.timestamp || Date.now(),
989
+ staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
990
+ cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
991
+ key: queryKey,
992
+ tags: mergedTags
993
+ });
994
+ this.pluginManager.onFetchStart(normalizedKey);
995
+ try {
996
+ const data = await this.remotes.fetch(key, fn, {
997
+ signal: options?.signal,
998
+ retry: options?.retry,
999
+ retryDelay: options?.retryDelay
1000
+ });
1001
+ this.storage.set(key, {
1002
+ data,
1003
+ status: "success",
1004
+ error: null,
1005
+ isFetching: false,
1006
+ isInvalidated: void 0,
1007
+ // Clear invalidation on success
1008
+ fetchDirection: "idle",
1009
+ timestamp: Date.now(),
1010
+ staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
1011
+ cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
1012
+ key: queryKey,
1013
+ tags: mergedTags
1014
+ });
1015
+ const schema = options?.schema || this.defaultSchema;
1016
+ const validatedData = validateWithSchema(data, schema);
1017
+ this.pluginManager.onFetchSuccess(normalizedKey, validatedData);
1018
+ return validatedData;
1019
+ } catch (error) {
1020
+ const err = error instanceof Error ? error : new Error(String(error));
1021
+ this.storage.set(key, {
1022
+ data: currentEntry?.data,
1023
+ status: "error",
1024
+ error: err,
1025
+ isFetching: false,
1026
+ fetchDirection: "idle",
1027
+ timestamp: currentEntry?.timestamp || Date.now(),
1028
+ staleTime: currentEntry?.staleTime ?? this.defaultStaleTime,
1029
+ cacheTime: currentEntry?.cacheTime ?? this.defaultCacheTime,
1030
+ key: queryKey,
1031
+ tags: mergedTags
1032
+ });
1033
+ this.pluginManager.onFetchError(normalizedKey, err);
1034
+ throw err;
1035
+ }
830
1036
  };
831
1037
  /**
832
- * Get all entries (wrapper for DevTools)
1038
+ * Invalidate queries
833
1039
  */
834
- getAll = () => {
835
- const map = /* @__PURE__ */ new Map();
836
- for (const [key, signal] of this.signals.entries()) {
837
- const val = signal.get();
838
- if (val) map.set(key, val);
1040
+ invalidate = (queryKey) => {
1041
+ const prefix = this.storage.generateKey(queryKey);
1042
+ const normalizedKey = this.normalizeKey(queryKey);
1043
+ this.pluginManager.onInvalidate(normalizedKey);
1044
+ const allKeys = this.storage.getSnapshot().keys();
1045
+ for (const key of allKeys) {
1046
+ if (key === prefix || key.startsWith(prefix.slice(0, -1))) {
1047
+ const signal2 = this.storage.get(key, false);
1048
+ if (signal2) {
1049
+ const current = signal2.get();
1050
+ if (current) {
1051
+ signal2.set({ ...current, isInvalidated: true });
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+ };
1057
+ invalidateTags = (tags) => {
1058
+ const tagsToInvalidate = new Set(tags);
1059
+ for (const tag of tagsToInvalidate) {
1060
+ const keys = this.storage.getKeysByTag(tag);
1061
+ if (keys) {
1062
+ for (const key of keys) {
1063
+ const signal2 = this.storage.get(key, false);
1064
+ if (signal2) {
1065
+ const current = signal2.get();
1066
+ if (current) {
1067
+ signal2.set({ ...current, isInvalidated: true });
1068
+ }
1069
+ }
1070
+ }
1071
+ }
839
1072
  }
840
- return map;
1073
+ };
1074
+ // --- Helpers ---
1075
+ use = (plugin) => {
1076
+ this.pluginManager.add(plugin);
1077
+ return this;
1078
+ };
1079
+ isStale = (queryKey) => {
1080
+ const key = this.storage.generateKey(queryKey);
1081
+ const signal2 = this.storage.get(key);
1082
+ const entry = signal2?.get();
1083
+ if (!entry) return true;
1084
+ if (entry.isInvalidated) return true;
1085
+ return Date.now() - entry.timestamp > entry.staleTime;
1086
+ };
1087
+ getAll = () => this.storage.getAll();
1088
+ snapshot = () => this.storage.getSnapshot();
1089
+ clear = () => this.storage.clear();
1090
+ remove = (key) => this.storage.delete(this.storage.generateKey(key));
1091
+ // Restored Methods
1092
+ prefetch = (queryKey, data, options) => {
1093
+ this.set(queryKey, data, options);
1094
+ };
1095
+ destroy = () => {
1096
+ this.storage.clear();
1097
+ };
1098
+ getStats = () => this.storage.getStats();
1099
+ // For Hydration
1100
+ getSnapshot = () => this.storage.getSnapshot();
1101
+ invalidateAll = () => {
1102
+ const snapshot = this.storage.getSnapshot();
1103
+ for (const key of snapshot.keys()) {
1104
+ const signal2 = this.storage.get(key, false);
1105
+ const entry = signal2?.get();
1106
+ if (entry) {
1107
+ signal2?.set({ ...entry, isInvalidated: true });
1108
+ }
1109
+ }
1110
+ };
1111
+ normalizeKey(queryKey) {
1112
+ return Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
1113
+ }
1114
+ isCacheEntry(entry) {
1115
+ return entry !== null && typeof entry === "object" && "status" in entry && "timestamp" in entry;
1116
+ }
1117
+ // --- DEBUGGER API (For DevTools) ---
1118
+ debugSet = (key, data) => {
1119
+ this.set(key, data);
1120
+ };
1121
+ debugInvalidate = (key) => {
1122
+ this.invalidate(key);
841
1123
  };
842
1124
  };
843
- var queryCache = new QueryCache();
844
1125
 
845
- // src/addon/query/pagination.ts
846
- import { useState, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
1126
+ // src/query/pagination.ts
1127
+ import { useState as useState2, useEffect as useEffect2, useCallback, useRef as useRef2, useSyncExternalStore } from "react";
1128
+
1129
+ // src/query/context.tsx
1130
+ import { createContext, useContext } from "react";
1131
+ import { jsx as jsx2 } from "react/jsx-runtime";
1132
+ var QueryClientContext = createContext(void 0);
1133
+ var QueryClientProvider = ({
1134
+ client,
1135
+ children
1136
+ }) => {
1137
+ return /* @__PURE__ */ jsx2(QueryClientContext.Provider, { value: client, children: children || null });
1138
+ };
1139
+ var useQueryClient = () => {
1140
+ const client = useContext(QueryClientContext);
1141
+ if (!client) {
1142
+ throw new Error("No QueryClient set, use QueryClientProvider to set one");
1143
+ }
1144
+ return client;
1145
+ };
1146
+
1147
+ // src/query/pagination.ts
847
1148
  function usePaginatedQuery({
848
1149
  queryKey,
849
1150
  queryFn,
850
1151
  pageSize = 20,
851
1152
  staleTime,
852
1153
  cacheTime,
853
- enabled = true
1154
+ enabled = true,
1155
+ retry
854
1156
  }) {
855
- const [page, setPage] = useState(0);
856
- const [data, setData] = useState();
857
- const [isLoading, setIsLoading] = useState(true);
858
- const [isError, setIsError] = useState(false);
859
- const [error, setError] = useState(null);
860
- const [hasNext, setHasNext] = useState(true);
1157
+ const client = useQueryClient();
1158
+ const [page, setPage] = useState2(0);
1159
+ const pageQueryKey = [...queryKey, "page", page];
1160
+ const pageQueryKeyHash = JSON.stringify(pageQueryKey);
1161
+ const subscribe = useCallback((onStoreChange) => {
1162
+ const signal2 = client.getSignal(pageQueryKey);
1163
+ return signal2.subscribe(() => onStoreChange());
1164
+ }, [client, pageQueryKeyHash]);
1165
+ const getSnapshot = useCallback(() => {
1166
+ const signal2 = client.getSignal(pageQueryKey);
1167
+ return signal2.get();
1168
+ }, [client, pageQueryKeyHash]);
1169
+ const cacheEntry = useSyncExternalStore(subscribe, getSnapshot);
1170
+ const data = cacheEntry?.data;
1171
+ const status = cacheEntry?.status || "pending";
1172
+ const error = cacheEntry?.error || null;
1173
+ const isFetching = cacheEntry?.isFetching || false;
1174
+ const dataTimestamp = cacheEntry?.timestamp;
1175
+ const isError = status === "error";
1176
+ const isLoading = data === void 0 && (isFetching || status === "pending");
1177
+ let hasNext = true;
1178
+ if (data) {
1179
+ if (Array.isArray(data)) {
1180
+ hasNext = data.length === pageSize;
1181
+ } else if (typeof data === "object" && "hasMore" in data) {
1182
+ hasNext = !!data.hasMore;
1183
+ }
1184
+ }
1185
+ const hasPrevious = page > 0;
861
1186
  const queryFnRef = useRef2(queryFn);
1187
+ const staleTimeRef = useRef2(staleTime);
1188
+ const cacheTimeRef = useRef2(cacheTime);
862
1189
  useEffect2(() => {
863
1190
  queryFnRef.current = queryFn;
1191
+ staleTimeRef.current = staleTime;
1192
+ cacheTimeRef.current = cacheTime;
864
1193
  });
865
- const queryKeyHash = JSON.stringify(queryKey);
866
- const fetchPage = useCallback2(async (pageNum) => {
1194
+ const fetchPage = useCallback(async (background = false) => {
867
1195
  if (!enabled) return;
868
- const pageQueryKey = [...queryKey, "page", pageNum];
869
- const cached = queryCache.get(pageQueryKey);
870
- if (cached && !queryCache.isStale(pageQueryKey)) {
871
- setData(cached);
872
- setIsLoading(false);
873
- return;
1196
+ if (!background) {
1197
+ const currentEntry = getSnapshot();
1198
+ if (currentEntry && Date.now() - currentEntry.timestamp <= (staleTimeRef.current || 0)) {
1199
+ return;
1200
+ }
874
1201
  }
875
1202
  try {
876
- setIsLoading(true);
877
- setIsError(false);
878
- setError(null);
879
- const result = await queryFnRef.current(pageNum);
880
- queryCache.set(pageQueryKey, result, { staleTime, cacheTime });
881
- setData(result);
882
- if (Array.isArray(result)) {
883
- setHasNext(result.length === pageSize);
884
- } else if (result && typeof result === "object" && "hasMore" in result) {
885
- setHasNext(result.hasMore);
886
- }
887
- setIsLoading(false);
1203
+ const result = await client.fetch(pageQueryKey, async () => {
1204
+ return await queryFnRef.current(page);
1205
+ }, { retry });
1206
+ client.set(pageQueryKey, result, {
1207
+ staleTime: staleTimeRef.current,
1208
+ cacheTime: cacheTimeRef.current
1209
+ });
888
1210
  } catch (err) {
889
- setIsError(true);
890
- setError(err);
891
- setIsLoading(false);
892
1211
  }
893
- }, [queryKeyHash, enabled, pageSize, staleTime, cacheTime]);
1212
+ }, [pageQueryKeyHash, enabled, client, getSnapshot, page]);
894
1213
  useEffect2(() => {
895
- fetchPage(page);
896
- }, [page, fetchPage]);
897
- const nextPage = useCallback2(() => {
1214
+ if (enabled) {
1215
+ fetchPage();
1216
+ }
1217
+ }, [fetchPage, enabled]);
1218
+ const nextPage = useCallback(() => {
898
1219
  if (hasNext) {
899
1220
  setPage((p) => p + 1);
900
1221
  }
901
1222
  }, [hasNext]);
902
- const previousPage = useCallback2(() => {
1223
+ const previousPage = useCallback(() => {
903
1224
  if (page > 0) {
904
1225
  setPage((p) => p - 1);
905
1226
  }
906
1227
  }, [page]);
907
- const refetch = useCallback2(async () => {
908
- queryCache.invalidate([...queryKey, "page", String(page)]);
909
- await fetchPage(page);
910
- }, [queryKeyHash, page, fetchPage]);
1228
+ const refetch = useCallback(async () => {
1229
+ client.invalidate(pageQueryKey);
1230
+ await fetchPage();
1231
+ }, [pageQueryKeyHash, fetchPage, client]);
911
1232
  return {
912
1233
  data,
913
1234
  isLoading,
@@ -918,502 +1239,723 @@ function usePaginatedQuery({
918
1239
  nextPage,
919
1240
  previousPage,
920
1241
  hasNext,
921
- hasPrevious: page > 0,
1242
+ hasPrevious,
922
1243
  refetch
923
1244
  };
924
1245
  }
925
1246
 
926
- // src/addon/query/useQuery.ts
927
- import { useEffect as useEffect3, useCallback as useCallback3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2, useReducer } from "react";
1247
+ // src/query/useQuery.ts
1248
+ import { useState as useState3, useEffect as useEffect3, useCallback as useCallback2, useSyncExternalStore as useSyncExternalStore2 } from "react";
928
1249
 
929
- // src/addon/query/context.tsx
930
- import { createContext, useContext } from "react";
931
- import { jsx } from "react/jsx-runtime";
932
- var QueryClientContext = createContext(void 0);
933
- var QueryClientProvider = ({
934
- client,
935
- children
936
- }) => {
937
- return /* @__PURE__ */ jsx(QueryClientContext.Provider, { value: client, children });
1250
+ // src/query/focusManager.ts
1251
+ var FocusManager = class {
1252
+ listeners = /* @__PURE__ */ new Set();
1253
+ focused = true;
1254
+ unsubscribe = null;
1255
+ constructor() {
1256
+ if (typeof window !== "undefined" && window.addEventListener) {
1257
+ const handleFocus = () => {
1258
+ this.focused = true;
1259
+ this.onFocus();
1260
+ };
1261
+ const handleBlur = () => {
1262
+ this.focused = false;
1263
+ };
1264
+ window.addEventListener("focus", handleFocus, false);
1265
+ window.addEventListener("visibilitychange", handleFocus, false);
1266
+ window.addEventListener("blur", handleBlur, false);
1267
+ this.unsubscribe = () => {
1268
+ window.removeEventListener("focus", handleFocus);
1269
+ window.removeEventListener("visibilitychange", handleFocus);
1270
+ window.removeEventListener("blur", handleBlur);
1271
+ };
1272
+ }
1273
+ }
1274
+ onFocus() {
1275
+ this.listeners.forEach((listener) => listener());
1276
+ }
1277
+ subscribe(listener) {
1278
+ this.listeners.add(listener);
1279
+ return () => this.listeners.delete(listener);
1280
+ }
1281
+ isFocused() {
1282
+ return this.focused;
1283
+ }
1284
+ setFocused(focused) {
1285
+ this.focused = focused;
1286
+ if (focused) this.onFocus();
1287
+ }
938
1288
  };
939
- var useQueryClient = () => {
940
- const client = useContext(QueryClientContext);
941
- return client || queryCache;
1289
+ var focusManager = new FocusManager();
1290
+
1291
+ // src/query/onlineManager.ts
1292
+ var OnlineManager = class {
1293
+ listeners = /* @__PURE__ */ new Set();
1294
+ online = true;
1295
+ constructor() {
1296
+ if (typeof window !== "undefined" && window.addEventListener) {
1297
+ window.addEventListener("online", () => this.setOnline(true), false);
1298
+ window.addEventListener("offline", () => this.setOnline(false), false);
1299
+ this.online = navigator.onLine;
1300
+ }
1301
+ }
1302
+ setOnline(online) {
1303
+ this.online = online;
1304
+ this.listeners.forEach((listener) => listener(online));
1305
+ }
1306
+ subscribe(listener) {
1307
+ this.listeners.add(listener);
1308
+ return () => this.listeners.delete(listener);
1309
+ }
1310
+ isOnline() {
1311
+ return this.online;
1312
+ }
942
1313
  };
1314
+ var onlineManager = new OnlineManager();
943
1315
 
944
- // src/addon/query/useQuery.ts
945
- function useQuery({
946
- queryKey,
947
- queryFn,
948
- schema,
949
- staleTime = 0,
950
- cacheTime = 5 * 60 * 1e3,
951
- enabled = true,
952
- refetchOnWindowFocus = false,
953
- refetchOnReconnect = false,
954
- refetchInterval
955
- }) {
956
- const client = useQueryClient();
957
- const queryKeyHash = stableHash(queryKey);
958
- const subscribe2 = useCallback3((onStoreChange) => {
959
- const signal = client.getSignal(queryKey);
960
- return signal.subscribe(() => {
961
- onStoreChange();
962
- });
963
- }, [client, queryKeyHash]);
964
- const getSnapshot = useCallback3(() => {
965
- const signal = client.getSignal(queryKey);
966
- return signal.get();
967
- }, [client, queryKeyHash]);
968
- const cacheEntry = useSyncExternalStore2(subscribe2, getSnapshot);
969
- const data = cacheEntry?.data;
970
- const dataTimestamp = cacheEntry?.timestamp;
971
- const [statusState, dispatch] = useReducer(statusReducer, {
972
- isFetching: false,
973
- error: null
974
- });
975
- const abortControllerRef = useRef3(null);
976
- const intervalRef = useRef3(null);
977
- const isStale = dataTimestamp ? Date.now() - dataTimestamp > staleTime : true;
978
- const isLoading = data === void 0 && statusState.isFetching;
979
- const derivedIsLoading = data === void 0;
980
- const queryFnRef = useRef3(queryFn);
981
- const schemaRef = useRef3(schema);
982
- const queryKeyRef = useRef3(queryKey);
983
- useEffect3(() => {
984
- queryFnRef.current = queryFn;
985
- schemaRef.current = schema;
986
- queryKeyRef.current = queryKey;
987
- });
988
- const fetchData = useCallback3(async (background = false) => {
989
- if (!enabled) return;
990
- if (abortControllerRef.current) abortControllerRef.current.abort();
991
- abortControllerRef.current = new AbortController();
992
- if (!background) {
993
- const currentEntry = getSnapshot();
994
- if (currentEntry && Date.now() - currentEntry.timestamp <= staleTime) {
995
- return;
1316
+ // src/query/queryObserver.ts
1317
+ var QueryObserver = class {
1318
+ client;
1319
+ // Reactive Inputs
1320
+ options$;
1321
+ // Reactive Outputs
1322
+ result$;
1323
+ // Internal
1324
+ unsubscribe = null;
1325
+ gcUnsubscribe = null;
1326
+ abortController = null;
1327
+ intervalParams = { id: null };
1328
+ constructor(client, options) {
1329
+ this.client = client;
1330
+ this.options$ = createSignal(options);
1331
+ let lastResult = null;
1332
+ this.result$ = computed(() => {
1333
+ const opts = this.options$.get();
1334
+ const queryKey = opts.queryKey;
1335
+ const cacheSignal = this.client.getSignal(queryKey);
1336
+ const entry = cacheSignal.get();
1337
+ const data = entry?.data;
1338
+ const status = entry?.status || "pending";
1339
+ const error = entry?.error || null;
1340
+ const isFetching = entry?.isFetching || false;
1341
+ const staleTime = opts.staleTime ?? 0;
1342
+ const now = Date.now();
1343
+ const dataTimestamp = entry?.timestamp;
1344
+ const diff = dataTimestamp ? now - dataTimestamp : -1;
1345
+ const isTimeStale = dataTimestamp ? diff > staleTime : true;
1346
+ const isStale = entry?.isInvalidated || isTimeStale;
1347
+ const isError = status === "error";
1348
+ const isSuccess = status === "success";
1349
+ const isPending = status === "pending";
1350
+ const isLoading = data === void 0 && isFetching;
1351
+ let finalData;
1352
+ if (data !== void 0) {
1353
+ try {
1354
+ finalData = opts.select ? opts.select(data) : data;
1355
+ } catch (e) {
1356
+ return {
1357
+ ...lastResult ?? {},
1358
+ status: "error",
1359
+ error: e,
1360
+ isError: true
1361
+ };
1362
+ }
1363
+ }
1364
+ const nextResult = {
1365
+ data: finalData,
1366
+ isLoading,
1367
+ isError,
1368
+ isSuccess,
1369
+ isPending,
1370
+ isFetching,
1371
+ isStale,
1372
+ error,
1373
+ status,
1374
+ refetch: this.refetch
1375
+ };
1376
+ const isDataEqual = lastResult?.data === nextResult.data || isDeepEqual(lastResult?.data, nextResult.data);
1377
+ if (lastResult && isDataEqual && lastResult.status === nextResult.status && lastResult.isFetching === nextResult.isFetching && lastResult.isStale === nextResult.isStale && lastResult.error === nextResult.error) {
1378
+ return lastResult;
996
1379
  }
1380
+ lastResult = nextResult;
1381
+ return nextResult;
1382
+ });
1383
+ this.initSideEffects();
1384
+ }
1385
+ setOptions(options) {
1386
+ const current = this.options$.get();
1387
+ if (current === options) return;
1388
+ const isKeyEqual = stableHash(current.queryKey) === stableHash(options.queryKey);
1389
+ const isConfigEqual = current.enabled === options.enabled && current.staleTime === options.staleTime && current.cacheTime === options.cacheTime && current.refetchInterval === options.refetchInterval && current.retry === options.retry;
1390
+ if (!isKeyEqual || !isConfigEqual || current.select !== options.select) {
1391
+ this.options$.set(options);
997
1392
  }
998
- try {
999
- dispatch({ type: "FETCH_START", background });
1000
- const fn = queryFnRef.current;
1001
- const sc = schemaRef.current;
1002
- const key = queryKeyRef.current;
1003
- let result = await client.fetch(key, async () => {
1004
- let res = await fn();
1005
- if (sc) {
1006
- res = sc.parse(res);
1393
+ }
1394
+ subscribe(listener) {
1395
+ const unsub = this.result$.subscribe(() => {
1396
+ listener();
1397
+ });
1398
+ return unsub;
1399
+ }
1400
+ getSnapshot = () => {
1401
+ return this.result$.get();
1402
+ };
1403
+ initSideEffects() {
1404
+ const disposeEffect = effect(() => {
1405
+ const opts = this.options$.get();
1406
+ const res = this.result$.get();
1407
+ if (opts.enabled !== false) {
1408
+ if (res.isLoading && !res.isFetching && !res.isError) {
1409
+ untracked(() => this.fetch());
1410
+ } else if (res.isStale && !res.isFetching && !res.isError) {
1411
+ untracked(() => this.fetch());
1007
1412
  }
1008
- return res;
1413
+ }
1414
+ });
1415
+ const disposeGC = effect(() => {
1416
+ const opts = this.options$.get();
1417
+ const signal2 = this.client.getSignal(opts.queryKey);
1418
+ const unsub = signal2.subscribe(() => {
1009
1419
  });
1010
- client.set(key, result, { staleTime, cacheTime });
1011
- dispatch({ type: "FETCH_SUCCESS" });
1012
- } catch (err) {
1013
- if (err.name === "AbortError") return;
1014
- dispatch({ type: "FETCH_ERROR", error: err });
1420
+ return () => unsub();
1421
+ });
1422
+ const disposeFocus = focusManager.subscribe(() => {
1423
+ const opts = this.options$.get();
1424
+ if (opts.enabled !== false && opts.refetchOnWindowFocus !== false) {
1425
+ this.fetch();
1426
+ }
1427
+ });
1428
+ const disposeOnline = onlineManager.subscribe((isOnline) => {
1429
+ const opts = this.options$.get();
1430
+ if (isOnline && opts.enabled !== false && opts.refetchOnReconnect !== false) {
1431
+ this.fetch();
1432
+ }
1433
+ });
1434
+ this.unsubscribe = () => {
1435
+ disposeEffect();
1436
+ disposeGC();
1437
+ disposeFocus();
1438
+ disposeOnline();
1439
+ };
1440
+ }
1441
+ refetch = async () => {
1442
+ const opts = this.options$.get();
1443
+ this.client.invalidate(opts.queryKey);
1444
+ await this.fetch();
1445
+ };
1446
+ fetch = async (background = false) => {
1447
+ const opts = this.options$.get();
1448
+ if (opts.enabled === false) return;
1449
+ if (this.abortController) {
1450
+ this.abortController.abort();
1015
1451
  }
1016
- }, [queryKeyHash, enabled, staleTime, cacheTime, client, getSnapshot]);
1017
- useEffect3(() => {
1018
- if (data === void 0 && !statusState.error) {
1019
- fetchData();
1452
+ this.abortController = new AbortController();
1453
+ const signal2 = this.abortController.signal;
1454
+ try {
1455
+ await this.client.fetch(
1456
+ opts.queryKey,
1457
+ async (ctx) => {
1458
+ const data = await opts.queryFn(ctx);
1459
+ if (opts.schema) {
1460
+ return validateWithSchema(data, opts.schema);
1461
+ }
1462
+ return data;
1463
+ },
1464
+ { signal: signal2, retry: opts.retry, retryDelay: opts.retryDelay, tags: opts.tags }
1465
+ );
1466
+ } catch (e) {
1020
1467
  }
1021
- }, [fetchData, data, statusState.error]);
1022
- useEffect3(() => {
1023
- if (!enabled || !refetchInterval) return;
1024
- intervalRef.current = setInterval(() => fetchData(true), refetchInterval);
1025
- return () => {
1026
- if (intervalRef.current) clearInterval(intervalRef.current);
1027
- };
1028
- }, [enabled, refetchInterval, fetchData]);
1468
+ };
1469
+ destroy() {
1470
+ if (this.unsubscribe) this.unsubscribe();
1471
+ if (this.abortController) this.abortController.abort();
1472
+ }
1473
+ };
1474
+
1475
+ // src/query/useQuery.ts
1476
+ function useQuery(options) {
1477
+ const client = useQueryClient();
1478
+ const [observer] = useState3(() => new QueryObserver(client, options));
1029
1479
  useEffect3(() => {
1030
- if (!enabled || !refetchOnWindowFocus) return;
1031
- const handleFocus = () => {
1032
- const entry = getSnapshot();
1033
- const isStaleNow = !entry || Date.now() - entry.timestamp > staleTime;
1034
- if (isStaleNow) fetchData(true);
1035
- };
1036
- window.addEventListener("focus", handleFocus);
1037
- return () => window.removeEventListener("focus", handleFocus);
1038
- }, [enabled, refetchOnWindowFocus, fetchData, getSnapshot, staleTime]);
1480
+ observer.setOptions(options);
1481
+ }, [observer, options]);
1039
1482
  useEffect3(() => {
1040
- if (!enabled || !refetchOnReconnect) return;
1041
- const handleOnline = () => {
1042
- const entry = getSnapshot();
1043
- const isStaleNow = !entry || Date.now() - entry.timestamp > staleTime;
1044
- if (isStaleNow) fetchData(true);
1483
+ return () => {
1484
+ observer.destroy();
1045
1485
  };
1046
- window.addEventListener("online", handleOnline);
1047
- return () => window.removeEventListener("online", handleOnline);
1048
- }, [enabled, refetchOnReconnect, fetchData, getSnapshot, staleTime]);
1049
- const refetch = useCallback3(async () => {
1050
- client.invalidate(queryKey);
1051
- await fetchData();
1052
- }, [queryKeyHash, fetchData, client]);
1486
+ }, [observer]);
1487
+ const subscribe = useCallback2((onStoreChange) => {
1488
+ return observer.subscribe(onStoreChange);
1489
+ }, [observer]);
1490
+ const getSnapshot = useCallback2(() => {
1491
+ return observer.getSnapshot();
1492
+ }, [observer]);
1493
+ const result = useSyncExternalStore2(subscribe, getSnapshot);
1053
1494
  return {
1054
- data,
1055
- isLoading: derivedIsLoading,
1056
- isError: !!statusState.error,
1057
- isFetching: statusState.isFetching,
1058
- isStale,
1059
- error: statusState.error,
1060
- refetch
1495
+ ...result,
1496
+ signal: observer.result$
1061
1497
  };
1062
1498
  }
1063
- function statusReducer(state, action) {
1064
- switch (action.type) {
1065
- case "FETCH_START":
1066
- return {
1067
- ...state,
1068
- isFetching: true,
1069
- error: null
1070
- };
1071
- case "FETCH_SUCCESS":
1072
- return {
1073
- ...state,
1074
- isFetching: false,
1075
- error: null
1076
- };
1077
- case "FETCH_ERROR":
1078
- return {
1079
- ...state,
1080
- isFetching: false,
1081
- error: action.error
1082
- };
1083
- default:
1084
- return state;
1085
- }
1086
- }
1087
1499
 
1088
- // src/addon/query/useMutation.ts
1089
- import { useState as useState3, useCallback as useCallback4 } from "react";
1090
- function useMutation({
1091
- mutationFn,
1092
- onMutate,
1093
- onSuccess,
1094
- onError,
1095
- onSettled
1096
- }) {
1097
- const [data, setData] = useState3();
1098
- const [error, setError] = useState3(null);
1099
- const [isLoading, setIsLoading] = useState3(false);
1100
- const [isError, setIsError] = useState3(false);
1101
- const [isSuccess, setIsSuccess] = useState3(false);
1102
- const mutateAsync = useCallback4(async (variables) => {
1500
+ // src/query/useMutation.ts
1501
+ import { useCallback as useCallback3, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore3, useState as useState4 } from "react";
1502
+
1503
+ // src/query/mutationObserver.ts
1504
+ var generateId = () => {
1505
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
1506
+ return crypto.randomUUID();
1507
+ }
1508
+ return "mutation-" + Date.now() + "-" + Math.random().toString(36).slice(2);
1509
+ };
1510
+ var MutationObserver = class {
1511
+ client;
1512
+ options;
1513
+ currentMutationId = null;
1514
+ // We maintain our own signal for this observer's view of the mutation
1515
+ // This ensures we don't miss updates if we switch IDs.
1516
+ signal;
1517
+ constructor(client, options) {
1518
+ this.client = client;
1519
+ this.options = options;
1520
+ this.signal = createSignal({
1521
+ status: "idle",
1522
+ data: void 0,
1523
+ error: null,
1524
+ variables: void 0,
1525
+ context: void 0,
1526
+ submittedAt: 0
1527
+ });
1528
+ }
1529
+ setOptions(options) {
1530
+ this.options = options;
1531
+ }
1532
+ mutate = (variables) => {
1533
+ const id = generateId();
1534
+ this.currentMutationId = id;
1535
+ this.client.mutationCache.register(id, this.options.mutationKey);
1536
+ const pendingState = {
1537
+ status: "pending",
1538
+ variables,
1539
+ submittedAt: Date.now(),
1540
+ data: void 0,
1541
+ error: null,
1542
+ context: void 0
1543
+ };
1544
+ this.signal.set({
1545
+ ...this.signal.get(),
1546
+ ...pendingState
1547
+ });
1548
+ this.client.mutationCache.notify(id, pendingState);
1549
+ const unsubscribe = this.client.mutationCache.getSignal(id).subscribe((state) => {
1550
+ const current = this.signal.get();
1551
+ if (current.status !== state.status || current.data !== state.data || current.error !== state.error) {
1552
+ this.signal.set(state);
1553
+ }
1554
+ });
1555
+ return this.executeMutation(id, variables).finally(() => {
1556
+ });
1557
+ };
1558
+ executeMutation = async (id, variables) => {
1559
+ const { mutationFn, onMutate, onSuccess, onError, onSettled, invalidatesTags, optimistic, mutationKey } = this.options;
1103
1560
  let context;
1561
+ let optimisticSnapshot;
1562
+ const notify = (update) => {
1563
+ this.client.mutationCache.notify(id, update);
1564
+ };
1104
1565
  try {
1105
- setIsLoading(true);
1106
- setIsError(false);
1107
- setError(null);
1108
- setIsSuccess(false);
1566
+ if (optimistic) {
1567
+ const { queryKey, update } = optimistic;
1568
+ const signal2 = this.client.getSignal(queryKey);
1569
+ const currentData = signal2.get()?.data;
1570
+ optimisticSnapshot = currentData;
1571
+ const optimisticData = update(variables, currentData);
1572
+ this.client.set(queryKey, optimisticData);
1573
+ }
1109
1574
  if (onMutate) {
1110
1575
  context = await onMutate(variables);
1576
+ notify({ context });
1111
1577
  }
1112
1578
  const result = await mutationFn(variables);
1113
- setData(result);
1114
- setIsSuccess(true);
1115
- setIsLoading(false);
1116
- if (onSuccess) {
1117
- onSuccess(result, variables, context);
1579
+ notify({ status: "success", data: result });
1580
+ if (onSuccess) await onSuccess(result, variables, context);
1581
+ if (this.client.invalidateTags && invalidatesTags) {
1582
+ this.client.invalidateTags(invalidatesTags);
1118
1583
  }
1119
- if (onSettled) {
1120
- onSettled(result, null, variables, context);
1584
+ if (optimistic) {
1585
+ this.client.invalidate(optimistic.queryKey);
1121
1586
  }
1587
+ if (onSettled) onSettled(result, null, variables, context);
1122
1588
  return result;
1123
- } catch (err) {
1124
- setIsError(true);
1125
- setError(err);
1126
- setIsLoading(false);
1127
- if (onError) {
1128
- onError(err, variables, context);
1129
- }
1130
- if (onSettled) {
1131
- onSettled(void 0, err, variables, context);
1589
+ } catch (error) {
1590
+ if (optimistic && optimisticSnapshot !== void 0) {
1591
+ this.client.set(optimistic.queryKey, optimisticSnapshot);
1132
1592
  }
1133
- throw err;
1593
+ notify({ status: "error", error });
1594
+ if (onError) onError(error, variables, context);
1595
+ if (onSettled) onSettled(void 0, error, variables, context);
1596
+ throw error;
1134
1597
  }
1135
- }, [mutationFn, onMutate, onSuccess, onError, onSettled]);
1136
- const mutate = useCallback4(async (variables) => {
1137
- try {
1138
- await mutateAsync(variables);
1139
- } catch {
1140
- }
1141
- }, [mutateAsync]);
1142
- const reset = useCallback4(() => {
1143
- setData(void 0);
1144
- setError(null);
1145
- setIsLoading(false);
1146
- setIsError(false);
1147
- setIsSuccess(false);
1148
- }, []);
1598
+ };
1599
+ reset = () => {
1600
+ this.signal.set({
1601
+ status: "idle",
1602
+ data: void 0,
1603
+ error: null,
1604
+ variables: void 0,
1605
+ context: void 0,
1606
+ submittedAt: 0
1607
+ });
1608
+ this.currentMutationId = null;
1609
+ };
1610
+ };
1611
+
1612
+ // src/query/useMutation.ts
1613
+ function useMutation(options) {
1614
+ const client = useQueryClient();
1615
+ const [observer] = useState4(() => new MutationObserver(client, options));
1616
+ useEffect4(() => {
1617
+ observer.setOptions(options);
1618
+ }, [observer, options]);
1619
+ const state = useSyncExternalStore3(
1620
+ useCallback3((cb) => observer.signal.subscribe(cb), [observer]),
1621
+ () => observer.signal.get()
1622
+ );
1623
+ const mutateAsync = useCallback3((variables) => {
1624
+ return observer.mutate(variables);
1625
+ }, [observer]);
1626
+ const mutate = useCallback3((variables) => {
1627
+ observer.mutate(variables).catch(() => {
1628
+ });
1629
+ }, [observer]);
1149
1630
  return {
1150
1631
  mutate,
1151
1632
  mutateAsync,
1152
- data,
1153
- error,
1154
- isLoading,
1155
- isError,
1156
- isSuccess,
1157
- reset
1633
+ data: state.data,
1634
+ error: state.error,
1635
+ status: state.status,
1636
+ isLoading: state.status === "pending",
1637
+ isError: state.status === "error",
1638
+ isSuccess: state.status === "success",
1639
+ isIdle: state.status === "idle",
1640
+ reset: observer.reset
1158
1641
  };
1159
1642
  }
1160
- var optimisticHelpers = {
1161
- /**
1162
- * Cancel ongoing queries for a key
1163
- */
1164
- async cancelQueries(queryKey) {
1165
- },
1166
- /**
1167
- * Get current query data
1168
- */
1169
- getQueryData(queryKey) {
1170
- return queryCache.get(queryKey);
1171
- },
1172
- /**
1173
- * Set query data (for optimistic updates)
1174
- */
1175
- setQueryData(queryKey, updater) {
1176
- const current = queryCache.get(queryKey);
1177
- const newData = typeof updater === "function" ? updater(current) : updater;
1178
- queryCache.set(queryKey, newData);
1179
- return current;
1180
- },
1181
- /**
1182
- * Invalidate queries (trigger refetch)
1183
- */
1184
- invalidateQueries(queryKey) {
1185
- queryCache.invalidate(queryKey);
1186
- }
1643
+
1644
+ // src/query/infiniteQuery.ts
1645
+ import { useState as useState5, useEffect as useEffect5, useCallback as useCallback4, useSyncExternalStore as useSyncExternalStore4 } from "react";
1646
+
1647
+ // src/query/plugins/logger.ts
1648
+ var consoleLogger = {
1649
+ log: console.log,
1650
+ warn: console.warn,
1651
+ error: console.error
1187
1652
  };
1653
+ var currentLogger = consoleLogger;
1654
+ var getLogger = () => currentLogger;
1188
1655
 
1189
- // src/addon/query/infiniteQuery.ts
1190
- import { useEffect as useEffect4, useCallback as useCallback5, useRef as useRef4, useReducer as useReducer2, useSyncExternalStore as useSyncExternalStore3 } from "react";
1191
- function statusReducer2(state, action) {
1192
- switch (action.type) {
1193
- case "FETCH_START":
1194
- return {
1195
- ...state,
1196
- isFetching: true,
1197
- isFetchingNextPage: action.direction === "next",
1198
- isFetchingPreviousPage: action.direction === "previous",
1199
- error: null
1200
- };
1201
- case "FETCH_SUCCESS":
1202
- return {
1203
- ...state,
1204
- isFetching: false,
1205
- isFetchingNextPage: false,
1206
- isFetchingPreviousPage: false,
1207
- hasNextPage: action.hasNextPage !== void 0 ? action.hasNextPage : state.hasNextPage,
1208
- hasPreviousPage: action.hasPreviousPage !== void 0 ? action.hasPreviousPage : state.hasPreviousPage
1209
- };
1210
- case "FETCH_ERROR":
1211
- return {
1212
- ...state,
1213
- isFetching: false,
1214
- isFetchingNextPage: false,
1215
- isFetchingPreviousPage: false,
1216
- error: action.error
1217
- };
1218
- case "SET_PAGINATION":
1219
- return {
1220
- ...state,
1221
- hasNextPage: action.hasNextPage !== void 0 ? action.hasNextPage : state.hasNextPage,
1222
- hasPreviousPage: action.hasPreviousPage !== void 0 ? action.hasPreviousPage : state.hasPreviousPage
1656
+ // src/query/infiniteQueryObserver.ts
1657
+ var InfiniteQueryObserver = class {
1658
+ client;
1659
+ options$;
1660
+ result$;
1661
+ unsubscribe = null;
1662
+ lastFetchTime = 0;
1663
+ constructor(client, options) {
1664
+ this.client = client;
1665
+ this.options$ = createSignal(options);
1666
+ const infiniteQueryKey = computed(() => {
1667
+ const opts = this.options$.get();
1668
+ return [...opts.queryKey, "__infinite__"];
1669
+ });
1670
+ let lastResult = null;
1671
+ this.result$ = computed(() => {
1672
+ const opts = this.options$.get();
1673
+ const key = infiniteQueryKey.get();
1674
+ const cacheSignal = this.client.getSignal(key);
1675
+ const entry = cacheSignal.get();
1676
+ const data = entry?.data;
1677
+ const isFetching = entry?.isFetching || false;
1678
+ const fetchDirection = entry?.fetchDirection || "idle";
1679
+ const status = entry?.status || "pending";
1680
+ const error = entry?.error || null;
1681
+ let hasNextPage = false;
1682
+ let hasPreviousPage = false;
1683
+ if (data && data.pages.length > 0) {
1684
+ if (opts.getNextPageParam) {
1685
+ const lastPage = data.pages[data.pages.length - 1];
1686
+ hasNextPage = opts.getNextPageParam(lastPage, data.pages) !== void 0;
1687
+ }
1688
+ if (opts.getPreviousPageParam) {
1689
+ const firstPage = data.pages[0];
1690
+ hasPreviousPage = opts.getPreviousPageParam(firstPage, data.pages) !== void 0;
1691
+ }
1692
+ }
1693
+ const isFetchingNextPage = isFetching && fetchDirection === "next";
1694
+ const isFetchingPreviousPage = isFetching && fetchDirection === "previous";
1695
+ const isLoading = data === void 0 && isFetching;
1696
+ const nextResult = {
1697
+ data,
1698
+ isFetching,
1699
+ isFetchingNextPage,
1700
+ isFetchingPreviousPage,
1701
+ isLoading,
1702
+ isError: status === "error",
1703
+ hasNextPage,
1704
+ hasPreviousPage,
1705
+ error,
1706
+ status,
1707
+ fetchNextPage: this.fetchNextPage,
1708
+ fetchPreviousPage: this.fetchPreviousPage,
1709
+ refetch: this.refetch
1223
1710
  };
1224
- default:
1225
- return state;
1711
+ const isDataEqual = lastResult?.data === nextResult.data || isDeepEqual(lastResult?.data, nextResult.data);
1712
+ if (lastResult && isDataEqual && lastResult.isFetching === nextResult.isFetching && lastResult.status === nextResult.status && lastResult.hasNextPage === nextResult.hasNextPage && lastResult.hasPreviousPage === nextResult.hasPreviousPage) {
1713
+ return lastResult;
1714
+ }
1715
+ lastResult = nextResult;
1716
+ return nextResult;
1717
+ });
1718
+ this.initSideEffects();
1226
1719
  }
1227
- }
1228
- function useInfiniteQuery({
1229
- queryKey,
1230
- queryFn,
1231
- getNextPageParam,
1232
- getPreviousPageParam,
1233
- initialPageParam,
1234
- staleTime = 0,
1235
- cacheTime = 5 * 60 * 1e3,
1236
- enabled = true
1237
- }) {
1238
- const client = useQueryClient();
1239
- const queryKeyHash = stableHash(queryKey);
1240
- const infiniteQueryKey = [...queryKey, "__infinite__"];
1241
- const subscribe2 = useCallback5((onStoreChange) => {
1242
- const signal = client.getSignal(infiniteQueryKey);
1243
- return signal.subscribe(() => onStoreChange());
1244
- }, [client, queryKeyHash]);
1245
- const getSnapshot = useCallback5(() => {
1246
- const signal = client.getSignal(infiniteQueryKey);
1247
- return signal.get();
1248
- }, [client, queryKeyHash]);
1249
- const cacheEntry = useSyncExternalStore3(subscribe2, getSnapshot);
1250
- const data = cacheEntry?.data;
1251
- const [statusState, dispatch] = useReducer2(statusReducer2, {
1252
- isFetching: false,
1253
- isFetchingNextPage: false,
1254
- isFetchingPreviousPage: false,
1255
- error: null,
1256
- hasNextPage: false,
1257
- // Will be set after first fetch
1258
- hasPreviousPage: false
1259
- });
1260
- const queryFnRef = useRef4(queryFn);
1261
- const getNextPageParamRef = useRef4(getNextPageParam);
1262
- const getPreviousPageParamRef = useRef4(getPreviousPageParam);
1263
- const initialFetchDoneRef = useRef4(false);
1264
- const clientRef = useRef4(client);
1265
- const infiniteQueryKeyRef = useRef4(infiniteQueryKey);
1266
- const initialPageParamRef = useRef4(initialPageParam);
1267
- const staleTimeRef = useRef4(staleTime);
1268
- const cacheTimeRef = useRef4(cacheTime);
1269
- useEffect4(() => {
1270
- queryFnRef.current = queryFn;
1271
- getNextPageParamRef.current = getNextPageParam;
1272
- getPreviousPageParamRef.current = getPreviousPageParam;
1273
- clientRef.current = client;
1274
- infiniteQueryKeyRef.current = infiniteQueryKey;
1275
- initialPageParamRef.current = initialPageParam;
1276
- staleTimeRef.current = staleTime;
1277
- cacheTimeRef.current = cacheTime;
1278
- });
1279
- const prevDataRef = useRef4(data);
1280
- useEffect4(() => {
1281
- if (prevDataRef.current && !data) {
1282
- initialFetchDoneRef.current = false;
1720
+ setOptions(options) {
1721
+ const current = this.options$.get();
1722
+ if (current === options) return;
1723
+ const isKeyEqual = stableHash(current.queryKey) === stableHash(options.queryKey);
1724
+ const isConfigEqual = current.enabled === options.enabled && current.staleTime === options.staleTime;
1725
+ if (!isKeyEqual || !isConfigEqual || current.getNextPageParam !== options.getNextPageParam || current.getPreviousPageParam !== options.getPreviousPageParam) {
1726
+ this.options$.set(options);
1283
1727
  }
1284
- prevDataRef.current = data;
1285
- }, [data]);
1286
- useEffect4(() => {
1287
- if (!enabled) return;
1288
- if (data) return;
1289
- const doFetch = async () => {
1290
- const initialParam = initialPageParamRef.current;
1291
- const firstParam = initialParam !== void 0 ? initialParam : 0;
1292
- if (!initialFetchDoneRef.current) {
1293
- initialFetchDoneRef.current = true;
1728
+ }
1729
+ initSideEffects() {
1730
+ const dispose = effect(() => {
1731
+ const opts = this.options$.get();
1732
+ const res = this.result$.get();
1733
+ if (opts.enabled !== false) {
1734
+ const infiniteKey = [...opts.queryKey, "__infinite__"];
1735
+ const entry = this.client.getSignal(infiniteKey).get();
1736
+ const staleTime = opts.staleTime ?? 0;
1737
+ const now = Date.now();
1738
+ const timestamp = entry?.timestamp || 0;
1739
+ const isStale = entry?.isInvalidated || entry?.data && now - timestamp > staleTime;
1740
+ if ((!entry?.data || isStale) && !res.isFetching && !res.isError) {
1741
+ untracked(() => this.fetchInitial());
1742
+ }
1294
1743
  }
1295
- dispatch({ type: "FETCH_START", direction: "initial" });
1296
- const pageKey = [...infiniteQueryKey, "initial", String(firstParam)];
1297
- let firstPage;
1298
- try {
1299
- firstPage = await clientRef.current.fetch(
1300
- pageKey,
1301
- () => queryFnRef.current({ pageParam: firstParam })
1302
- );
1303
- } catch (error) {
1304
- dispatch({ type: "FETCH_ERROR", error });
1744
+ });
1745
+ const disposeFocus = focusManager.subscribe(() => {
1746
+ const opts = this.options$.get();
1747
+ if (opts.enabled !== false && opts.refetchOnWindowFocus !== false) {
1748
+ this.fetchInitial();
1749
+ }
1750
+ });
1751
+ const disposeOnline = onlineManager.subscribe((isOnline) => {
1752
+ const opts = this.options$.get();
1753
+ if (isOnline && opts.enabled !== false && opts.refetchOnReconnect !== false) {
1754
+ this.fetchInitial();
1755
+ }
1756
+ });
1757
+ this.unsubscribe = () => {
1758
+ dispose();
1759
+ disposeFocus();
1760
+ disposeOnline();
1761
+ };
1762
+ }
1763
+ fetchInitial = async (options) => {
1764
+ const opts = this.options$.get();
1765
+ const infiniteKey = [...opts.queryKey, "__infinite__"];
1766
+ try {
1767
+ const entrySignal = this.client.getSignal(infiniteKey);
1768
+ const data = entrySignal.get()?.data;
1769
+ if (data && data.pageParams.length > 0 && !options?.force) {
1770
+ const firstParam2 = data.pageParams[0];
1771
+ const firstPage = await opts.queryFn({ pageParam: firstParam2 });
1772
+ const latest = entrySignal.get()?.data;
1773
+ if (!latest) return;
1774
+ const updatedData = {
1775
+ ...latest,
1776
+ pages: [firstPage, ...latest.pages.slice(1)]
1777
+ };
1778
+ this.client.set(infiniteKey, updatedData, {
1779
+ staleTime: opts.staleTime,
1780
+ cacheTime: opts.cacheTime
1781
+ });
1305
1782
  return;
1306
1783
  }
1307
- if (firstPage) {
1308
- const initialData = {
1784
+ const initialParam = opts.initialPageParam;
1785
+ const firstParam = initialParam !== void 0 ? initialParam : 0;
1786
+ const initialData = await this.client.fetch(infiniteKey, async () => {
1787
+ const firstPage = await opts.queryFn({ pageParam: firstParam });
1788
+ return {
1309
1789
  pages: [firstPage],
1310
1790
  pageParams: [firstParam]
1311
1791
  };
1312
- let hasNext = false;
1313
- if (getNextPageParamRef.current) {
1314
- const nextParam = getNextPageParamRef.current(firstPage, [firstPage]);
1315
- hasNext = nextParam !== void 0;
1316
- }
1317
- clientRef.current.set(infiniteQueryKeyRef.current, initialData, {
1318
- staleTime: staleTimeRef.current,
1319
- cacheTime: cacheTimeRef.current
1320
- });
1321
- dispatch({ type: "FETCH_SUCCESS", hasNextPage: hasNext });
1322
- }
1323
- };
1324
- doFetch();
1325
- }, [enabled, data]);
1326
- const fetchPageHelper = useCallback5(async (pageParam) => {
1327
- try {
1328
- const pageKey = [...infiniteQueryKey, String(pageParam)];
1329
- return await clientRef.current.fetch(
1330
- pageKey,
1331
- () => queryFnRef.current({ pageParam })
1332
- );
1333
- } catch (error) {
1334
- dispatch({ type: "FETCH_ERROR", error });
1335
- return void 0;
1792
+ }, { fetchDirection: "initial", retry: opts.retry });
1793
+ this.client.set(infiniteKey, initialData, {
1794
+ staleTime: opts.staleTime,
1795
+ cacheTime: opts.cacheTime
1796
+ });
1797
+ } catch (err) {
1798
+ getLogger().error("Initial fetch failed", err);
1336
1799
  }
1337
- }, [client, infiniteQueryKey]);
1338
- const fetchNextPage = useCallback5(async () => {
1339
- if (!statusState.hasNextPage || statusState.isFetchingNextPage || !data) return;
1340
- const lastPage = data.pages[data.pages.length - 1];
1341
- if (!lastPage || !getNextPageParamRef.current) return;
1342
- const nextPageParam = getNextPageParamRef.current(lastPage, data.pages);
1800
+ };
1801
+ fetchNextPage = async () => {
1802
+ const res = this.result$.get();
1803
+ const opts = this.options$.get();
1804
+ if (!res.hasNextPage || res.isFetching || !res.data) return;
1805
+ const infiniteKey = [...opts.queryKey, "__infinite__"];
1806
+ const lastPage = res.data.pages[res.data.pages.length - 1];
1807
+ const nextPageParam = opts.getNextPageParam?.(lastPage, res.data.pages);
1343
1808
  if (nextPageParam === void 0) return;
1344
- dispatch({ type: "FETCH_START", direction: "next" });
1345
- const newPage = await fetchPageHelper(nextPageParam);
1346
- if (newPage) {
1347
- const updatedData = {
1348
- pages: [...data.pages, newPage],
1349
- pageParams: [...data.pageParams, nextPageParam]
1350
- };
1351
- let hasNext = false;
1352
- if (getNextPageParamRef.current) {
1353
- const nextParam = getNextPageParamRef.current(newPage, updatedData.pages);
1354
- hasNext = nextParam !== void 0;
1355
- }
1356
- clientRef.current.set(infiniteQueryKeyRef.current, updatedData, {
1357
- staleTime: staleTimeRef.current,
1358
- cacheTime: cacheTimeRef.current
1809
+ try {
1810
+ const updatedData = await this.client.fetch(infiniteKey, async () => {
1811
+ const newPage = await opts.queryFn({ pageParam: nextPageParam });
1812
+ const currentData = this.client.getSignal(infiniteKey).get()?.data;
1813
+ if (!currentData) throw new Error("Infinite query data missing");
1814
+ const updatedParams = [...currentData.pageParams, nextPageParam];
1815
+ const nextCursor = opts.getNextPageParam?.(newPage, [...currentData.pages, newPage]);
1816
+ if (nextCursor !== void 0) {
1817
+ updatedParams.push(nextCursor);
1818
+ }
1819
+ return {
1820
+ pages: [...currentData.pages, newPage],
1821
+ pageParams: updatedParams
1822
+ };
1823
+ }, { fetchDirection: "next", retry: opts.retry });
1824
+ this.client.set(infiniteKey, updatedData, {
1825
+ staleTime: opts.staleTime,
1826
+ cacheTime: opts.cacheTime
1359
1827
  });
1360
- dispatch({ type: "FETCH_SUCCESS", hasNextPage: hasNext });
1828
+ } catch (err) {
1829
+ getLogger().error("Fetch next page failed", err);
1361
1830
  }
1362
- }, [statusState.hasNextPage, statusState.isFetchingNextPage, data, fetchPageHelper]);
1363
- const fetchPreviousPage = useCallback5(async () => {
1364
- if (!statusState.hasPreviousPage || statusState.isFetchingPreviousPage || !data) return;
1365
- const firstPage = data.pages[0];
1366
- if (!firstPage || !getPreviousPageParamRef.current) return;
1367
- const previousPageParam = getPreviousPageParamRef.current(firstPage, data.pages);
1831
+ };
1832
+ fetchPreviousPage = async () => {
1833
+ const res = this.result$.get();
1834
+ const opts = this.options$.get();
1835
+ if (!res.hasPreviousPage || res.isFetching || !res.data) return;
1836
+ const infiniteKey = [...opts.queryKey, "__infinite__"];
1837
+ const firstPage = res.data.pages[0];
1838
+ const previousPageParam = opts.getPreviousPageParam?.(firstPage, res.data.pages);
1368
1839
  if (previousPageParam === void 0) return;
1369
- dispatch({ type: "FETCH_START", direction: "previous" });
1370
- const newPage = await fetchPageHelper(previousPageParam);
1371
- if (newPage) {
1372
- const updatedData = {
1373
- pages: [newPage, ...data.pages],
1374
- pageParams: [previousPageParam, ...data.pageParams]
1375
- };
1376
- let hasPrev = false;
1377
- if (getPreviousPageParamRef.current) {
1378
- const prevParam = getPreviousPageParamRef.current(newPage, updatedData.pages);
1379
- hasPrev = prevParam !== void 0;
1380
- }
1381
- clientRef.current.set(infiniteQueryKeyRef.current, updatedData, {
1382
- staleTime: staleTimeRef.current,
1383
- cacheTime: cacheTimeRef.current
1840
+ try {
1841
+ const updatedData = await this.client.fetch(infiniteKey, async () => {
1842
+ const newPage = await opts.queryFn({ pageParam: previousPageParam });
1843
+ const currentData = this.client.getSignal(infiniteKey).get()?.data;
1844
+ if (!currentData) throw new Error("Infinite query data missing");
1845
+ return {
1846
+ pages: [newPage, ...currentData.pages],
1847
+ pageParams: [previousPageParam, ...currentData.pageParams]
1848
+ };
1849
+ }, { fetchDirection: "previous", retry: opts.retry });
1850
+ this.client.set(infiniteKey, updatedData, {
1851
+ staleTime: opts.staleTime,
1852
+ cacheTime: opts.cacheTime
1384
1853
  });
1385
- dispatch({ type: "FETCH_SUCCESS", hasPreviousPage: hasPrev });
1854
+ } catch (err) {
1855
+ getLogger().error("Fetch previous page failed", err);
1386
1856
  }
1387
- }, [statusState.hasPreviousPage, statusState.isFetchingPreviousPage, data, fetchPageHelper]);
1388
- const refetch = useCallback5(async () => {
1389
- initialFetchDoneRef.current = false;
1390
- clientRef.current.invalidate(infiniteQueryKeyRef.current);
1391
- }, []);
1392
- return {
1393
- data,
1394
- fetchNextPage,
1395
- fetchPreviousPage,
1396
- hasNextPage: statusState.hasNextPage,
1397
- hasPreviousPage: statusState.hasPreviousPage,
1398
- isFetching: statusState.isFetching,
1399
- isFetchingNextPage: statusState.isFetchingNextPage,
1400
- isFetchingPreviousPage: statusState.isFetchingPreviousPage,
1401
- isLoading: data === void 0 && statusState.isFetching,
1402
- isError: !!statusState.error,
1403
- error: statusState.error,
1404
- refetch
1405
1857
  };
1858
+ refetch = async () => {
1859
+ const opts = this.options$.get();
1860
+ const infiniteKey = [...opts.queryKey, "__infinite__"];
1861
+ this.client.invalidate(infiniteKey);
1862
+ await this.fetchInitial({ force: true });
1863
+ };
1864
+ destroy() {
1865
+ if (this.unsubscribe) this.unsubscribe();
1866
+ }
1867
+ };
1868
+
1869
+ // src/query/infiniteQuery.ts
1870
+ function useInfiniteQuery(options) {
1871
+ const client = useQueryClient();
1872
+ const [observer] = useState5(() => new InfiniteQueryObserver(client, options));
1873
+ useEffect5(() => {
1874
+ observer.setOptions(options);
1875
+ }, [observer, options]);
1876
+ useEffect5(() => {
1877
+ return () => {
1878
+ observer.destroy();
1879
+ };
1880
+ }, [observer]);
1881
+ const subscribe = useCallback4((onStoreChange) => {
1882
+ return observer.result$.subscribe(() => onStoreChange());
1883
+ }, [observer]);
1884
+ const getSnapshot = useCallback4(() => {
1885
+ return observer.result$.get();
1886
+ }, [observer]);
1887
+ return useSyncExternalStore4(subscribe, getSnapshot);
1406
1888
  }
1407
1889
 
1408
- // src/addon/query/devtools.tsx
1409
- import { useState as useState5, useMemo, useRef as useRef5, useEffect as useEffect6 } from "react";
1890
+ // src/query/HydrationBoundary.tsx
1891
+ import { useRef as useRef3 } from "react";
1410
1892
 
1411
- // src/addon/query/useQueryCache.ts
1412
- import { useState as useState4, useEffect as useEffect5 } from "react";
1413
- function useQueryCache() {
1893
+ // src/query/hydration.ts
1894
+ function dehydrate(client) {
1895
+ const queries = [];
1896
+ const snapshot = client.getSnapshot();
1897
+ snapshot.forEach((state, hash) => {
1898
+ queries.push({
1899
+ queryKey: state.key,
1900
+ queryHash: hash,
1901
+ state
1902
+ });
1903
+ });
1904
+ return { queries };
1905
+ }
1906
+ function hydrate(client, state) {
1907
+ if (!state || !state.queries) return;
1908
+ state.queries.forEach(({ queryKey, state: queryState }) => {
1909
+ const key = queryKey;
1910
+ client.restore(queryKey, queryState);
1911
+ });
1912
+ }
1913
+
1914
+ // src/query/HydrationBoundary.tsx
1915
+ import { Fragment as Fragment2, jsx as jsx3 } from "react/jsx-runtime";
1916
+ function HydrationBoundary({ state, children }) {
1414
1917
  const client = useQueryClient();
1415
- const [cache, setCache] = useState4(client.getAll());
1416
- useEffect5(() => {
1918
+ const hydratedRef = useRef3(false);
1919
+ if (state && !hydratedRef.current) {
1920
+ hydrate(client, state);
1921
+ hydratedRef.current = true;
1922
+ }
1923
+ return /* @__PURE__ */ jsx3(Fragment2, { children });
1924
+ }
1925
+
1926
+ // src/query/useSuspenseQuery.ts
1927
+ function useSuspenseQuery(options) {
1928
+ const client = useQueryClient();
1929
+ const signal2 = client.getSignal(options.queryKey);
1930
+ const entry = signal2.get();
1931
+ const shouldSuspend = !entry || entry.status === "pending" && entry.data === void 0;
1932
+ if (shouldSuspend) {
1933
+ const fetchPromise = client.fetch(
1934
+ options.queryKey,
1935
+ (ctx) => options.queryFn({ ...ctx, signal: void 0 }),
1936
+ { signal: void 0 }
1937
+ ).then((data) => {
1938
+ client.set(options.queryKey, data);
1939
+ return data;
1940
+ });
1941
+ throw fetchPromise;
1942
+ }
1943
+ if (entry?.status === "error") {
1944
+ throw entry.error;
1945
+ }
1946
+ const query = useQuery(options);
1947
+ return {
1948
+ ...query,
1949
+ data: query.data
1950
+ };
1951
+ }
1952
+
1953
+ // src/query/useQueryStore.ts
1954
+ import { useState as useState6, useEffect as useEffect6 } from "react";
1955
+ function useQueryStore() {
1956
+ const client = useQueryClient();
1957
+ const [cache, setCache] = useState6(client.getAll());
1958
+ useEffect6(() => {
1417
1959
  const interval = setInterval(() => {
1418
1960
  setCache(new Map(client.getAll()));
1419
1961
  }, 500);
@@ -1422,430 +1964,45 @@ function useQueryCache() {
1422
1964
  return cache;
1423
1965
  }
1424
1966
 
1425
- // src/addon/query/devtools.tsx
1426
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
1427
- function QuantumDevTools() {
1428
- const [isOpen, setIsOpen] = useState5(false);
1429
- const [isMinimized, setIsMinimized] = useState5(false);
1430
- const [height, setHeight] = useState5(450);
1431
- const [filter, setFilter] = useState5("");
1432
- const cache = useQueryCache();
1967
+ // src/query/useQuerySignal.ts
1968
+ import { useEffect as useEffect7, useState as useState7 } from "react";
1969
+ function useQuery$(options) {
1433
1970
  const client = useQueryClient();
1434
- const isResizingRef = useRef5(false);
1435
- const containerRef = useRef5(null);
1436
- useEffect6(() => {
1437
- const handleMouseMove = (e) => {
1438
- if (!isResizingRef.current) return;
1439
- const newHeight = window.innerHeight - e.clientY;
1440
- if (newHeight > 200 && newHeight < window.innerHeight - 50) {
1441
- setHeight(newHeight);
1442
- }
1443
- };
1444
- const handleMouseUp = () => {
1445
- isResizingRef.current = false;
1446
- document.body.style.cursor = "default";
1447
- };
1448
- window.addEventListener("mousemove", handleMouseMove);
1449
- window.addEventListener("mouseup", handleMouseUp);
1971
+ const [observer] = useState7(() => new QueryObserver(client, options));
1972
+ useEffect7(() => {
1973
+ observer.setOptions(options);
1974
+ }, [observer, options.enabled, options.staleTime, options.cacheTime, options.queryKey, options.refetchInterval]);
1975
+ useEffect7(() => {
1976
+ const unsubscribe = observer.subscribe(() => {
1977
+ });
1450
1978
  return () => {
1451
- window.removeEventListener("mousemove", handleMouseMove);
1452
- window.removeEventListener("mouseup", handleMouseUp);
1979
+ unsubscribe();
1980
+ observer.destroy();
1453
1981
  };
1454
- }, []);
1455
- const entries = useMemo(() => Array.from(cache.entries()), [cache]);
1456
- const filteredEntries = useMemo(() => {
1457
- if (!filter) return entries;
1458
- const search = filter.toLowerCase();
1459
- return entries.filter(
1460
- ([_, entry]) => entry.key.some((k) => String(k).toLowerCase().includes(search))
1461
- );
1462
- }, [entries, filter]);
1463
- if (!isOpen) {
1464
- return /* @__PURE__ */ jsx2(
1465
- "button",
1466
- {
1467
- onClick: () => setIsOpen(true),
1468
- style: {
1469
- position: "fixed",
1470
- bottom: "20px",
1471
- right: "20px",
1472
- background: "#111",
1473
- color: "#b0fb5d",
1474
- // Quantum Green
1475
- border: "1px solid #333",
1476
- borderRadius: "50%",
1477
- width: "48px",
1478
- height: "48px",
1479
- cursor: "pointer",
1480
- zIndex: 9999,
1481
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
1482
- fontSize: "20px",
1483
- display: "flex",
1484
- alignItems: "center",
1485
- justifyContent: "center",
1486
- transition: "transform 0.2s",
1487
- fontFamily: "monospace"
1488
- },
1489
- onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.1)",
1490
- onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1491
- title: "Open Quantum DevTools",
1492
- children: "\u26A1\uFE0F"
1493
- }
1494
- );
1495
- }
1496
- if (isMinimized) {
1497
- return /* @__PURE__ */ jsxs("div", { style: {
1498
- position: "fixed",
1499
- bottom: "20px",
1500
- right: "20px",
1501
- background: "#111",
1502
- border: "1px solid #333",
1503
- borderRadius: "8px",
1504
- padding: "8px 12px",
1505
- zIndex: 9999,
1506
- display: "flex",
1507
- alignItems: "center",
1508
- gap: "10px",
1509
- boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
1510
- fontFamily: "monospace",
1511
- color: "#e0e0e0",
1512
- cursor: "pointer"
1513
- }, onClick: () => setIsMinimized(false), children: [
1514
- /* @__PURE__ */ jsx2("span", { style: { color: "#b0fb5d" }, children: "\u26A1\uFE0F" }),
1515
- /* @__PURE__ */ jsx2("span", { style: { fontSize: "12px" }, children: "DevTools" }),
1516
- /* @__PURE__ */ jsxs("span", { style: {
1517
- background: "#333",
1518
- padding: "2px 6px",
1519
- borderRadius: "4px",
1520
- fontSize: "10px"
1521
- }, children: [
1522
- entries.length,
1523
- " queries"
1524
- ] })
1525
- ] });
1526
- }
1527
- return /* @__PURE__ */ jsxs(
1528
- "div",
1529
- {
1530
- ref: containerRef,
1531
- style: {
1532
- position: "fixed",
1533
- bottom: 0,
1534
- right: 0,
1535
- width: "100%",
1536
- height: `${height}px`,
1537
- background: "#0a0a0a",
1538
- color: "#e0e0e0",
1539
- borderTopLeftRadius: "12px",
1540
- borderTopRightRadius: "12px",
1541
- boxShadow: "0 -10px 40px rgba(0,0,0,0.5)",
1542
- zIndex: 9999,
1543
- display: "flex",
1544
- flexDirection: "column",
1545
- fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
1546
- fontSize: "13px",
1547
- borderTop: "1px solid #333"
1548
- },
1549
- children: [
1550
- /* @__PURE__ */ jsx2(
1551
- "div",
1552
- {
1553
- onMouseDown: () => {
1554
- isResizingRef.current = true;
1555
- document.body.style.cursor = "ns-resize";
1556
- },
1557
- style: {
1558
- height: "6px",
1559
- width: "100%",
1560
- cursor: "ns-resize",
1561
- position: "absolute",
1562
- top: "-3px",
1563
- left: 0,
1564
- zIndex: 10
1565
- // debugging color: background: 'red', opacity: 0.2
1566
- }
1567
- }
1568
- ),
1569
- /* @__PURE__ */ jsxs("div", { style: {
1570
- padding: "12px 16px",
1571
- borderBottom: "1px solid #222",
1572
- display: "flex",
1573
- justifyContent: "space-between",
1574
- alignItems: "center",
1575
- background: "#111",
1576
- borderTopLeftRadius: "12px",
1577
- borderTopRightRadius: "12px",
1578
- userSelect: "none"
1579
- }, children: [
1580
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1581
- /* @__PURE__ */ jsx2("span", { style: { color: "#b0fb5d", fontSize: "16px" }, children: "\u26A1\uFE0F" }),
1582
- /* @__PURE__ */ jsx2("span", { style: { fontWeight: 600, letterSpacing: "-0.5px" }, children: "Quantum DevTools" }),
1583
- /* @__PURE__ */ jsx2("span", { style: {
1584
- background: "#222",
1585
- padding: "2px 6px",
1586
- borderRadius: "4px",
1587
- fontSize: "10px",
1588
- color: "#666"
1589
- }, children: "v1.2.3" })
1590
- ] }),
1591
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
1592
- /* @__PURE__ */ jsx2(
1593
- "button",
1594
- {
1595
- onClick: () => setIsMinimized(true),
1596
- title: "Minimize",
1597
- style: {
1598
- background: "transparent",
1599
- border: "none",
1600
- color: "#666",
1601
- cursor: "pointer",
1602
- fontSize: "16px",
1603
- padding: "4px",
1604
- lineHeight: 1
1605
- },
1606
- children: "_"
1607
- }
1608
- ),
1609
- /* @__PURE__ */ jsx2(
1610
- "button",
1611
- {
1612
- onClick: () => setIsOpen(false),
1613
- title: "Close",
1614
- style: {
1615
- background: "transparent",
1616
- border: "none",
1617
- color: "#666",
1618
- cursor: "pointer",
1619
- fontSize: "18px",
1620
- padding: "4px",
1621
- lineHeight: 1
1622
- },
1623
- children: "\xD7"
1624
- }
1625
- )
1626
- ] })
1627
- ] }),
1628
- /* @__PURE__ */ jsxs("div", { style: {
1629
- padding: "8px 16px",
1630
- borderBottom: "1px solid #222",
1631
- background: "#0f0f0f",
1632
- display: "flex",
1633
- gap: "12px"
1634
- }, children: [
1635
- /* @__PURE__ */ jsx2(
1636
- "input",
1637
- {
1638
- type: "text",
1639
- placeholder: "Filter queries...",
1640
- value: filter,
1641
- onChange: (e) => setFilter(e.target.value),
1642
- style: {
1643
- background: "#1a1a1a",
1644
- border: "1px solid #333",
1645
- color: "#fff",
1646
- padding: "6px 10px",
1647
- borderRadius: "4px",
1648
- flex: 1,
1649
- fontSize: "12px",
1650
- outline: "none"
1651
- }
1652
- }
1653
- ),
1654
- /* @__PURE__ */ jsx2(
1655
- "button",
1656
- {
1657
- onClick: () => client.invalidateAll(),
1658
- title: "Invalidate All Queries",
1659
- style: {
1660
- background: "#222",
1661
- border: "1px solid #333",
1662
- color: "#d69e2e",
1663
- borderRadius: "4px",
1664
- padding: "0 12px",
1665
- cursor: "pointer",
1666
- fontSize: "12px",
1667
- fontWeight: 500
1668
- },
1669
- children: "\u21BB Invalidate All"
1670
- }
1671
- )
1672
- ] }),
1673
- /* @__PURE__ */ jsx2("div", { style: {
1674
- flex: 1,
1675
- overflowY: "auto",
1676
- padding: "8px",
1677
- display: "flex",
1678
- flexDirection: "column",
1679
- gap: "8px",
1680
- background: "#050505"
1681
- }, children: entries.length === 0 ? /* @__PURE__ */ jsxs("div", { style: {
1682
- padding: "40px",
1683
- textAlign: "center",
1684
- color: "#444",
1685
- fontStyle: "italic"
1686
- }, children: [
1687
- "No active queries in cache.",
1688
- /* @__PURE__ */ jsx2("br", {}),
1689
- /* @__PURE__ */ jsx2("span", { style: { fontSize: "11px", opacity: 0.7 }, children: "(Note: Only `useQuery` calls appear here. Raw `api.get` calls are not cached globally.)" })
1690
- ] }) : filteredEntries.map(([keyHash, entry]) => /* @__PURE__ */ jsx2(
1691
- QueryItem,
1692
- {
1693
- entry,
1694
- client,
1695
- isStale: client.isStale(entry.key)
1696
- },
1697
- keyHash
1698
- )) })
1699
- ]
1700
- }
1701
- );
1702
- }
1703
- function QueryItem({ entry, client, isStale }) {
1704
- const [expanded, setExpanded] = useState5(false);
1705
- return /* @__PURE__ */ jsxs("div", { style: {
1706
- background: "#111",
1707
- borderRadius: "6px",
1708
- border: "1px solid #222",
1709
- overflow: "hidden"
1710
- }, children: [
1711
- /* @__PURE__ */ jsxs(
1712
- "div",
1713
- {
1714
- onClick: () => setExpanded(!expanded),
1715
- style: {
1716
- padding: "8px 12px",
1717
- display: "flex",
1718
- alignItems: "center",
1719
- justifyContent: "space-between",
1720
- cursor: "pointer",
1721
- background: expanded ? "#161616" : "transparent"
1722
- },
1723
- children: [
1724
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "10px", alignItems: "center", overflow: "hidden" }, children: [
1725
- /* @__PURE__ */ jsx2("span", { style: {
1726
- color: isStale ? "#d69e2e" : "#b0fb5d",
1727
- fontSize: "14px",
1728
- fontWeight: "bold",
1729
- minWidth: "10px"
1730
- }, children: isStale ? "\u2022" : "\u2022" }),
1731
- /* @__PURE__ */ jsxs("span", { style: {
1732
- color: "#e0e0e0",
1733
- fontWeight: 500,
1734
- whiteSpace: "nowrap",
1735
- overflow: "hidden",
1736
- textOverflow: "ellipsis"
1737
- }, children: [
1738
- "['",
1739
- entry.key.join("', '"),
1740
- "']"
1741
- ] })
1742
- ] }),
1743
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
1744
- /* @__PURE__ */ jsx2("span", { style: {
1745
- fontSize: "10px",
1746
- padding: "2px 6px",
1747
- borderRadius: "3px",
1748
- background: isStale ? "rgba(214, 158, 46, 0.15)" : "rgba(176, 251, 93, 0.15)",
1749
- color: isStale ? "#d69e2e" : "#b0fb5d",
1750
- border: `1px solid ${isStale ? "rgba(214, 158, 46, 0.3)" : "rgba(176, 251, 93, 0.3)"}`
1751
- }, children: isStale ? "STALE" : "FRESH" }),
1752
- /* @__PURE__ */ jsx2("span", { style: { color: "#666", fontSize: "10px" }, children: expanded ? "\u25BC" : "\u25B6" })
1753
- ] })
1754
- ]
1755
- }
1756
- ),
1757
- expanded && /* @__PURE__ */ jsxs("div", { style: {
1758
- padding: "10px",
1759
- borderTop: "1px solid #222",
1760
- background: "#0a0a0a"
1761
- }, children: [
1762
- /* @__PURE__ */ jsxs("div", { style: {
1763
- display: "flex",
1764
- gap: "8px",
1765
- marginBottom: "10px",
1766
- borderBottom: "1px solid #222",
1767
- paddingBottom: "8px"
1768
- }, children: [
1769
- /* @__PURE__ */ jsx2(
1770
- "button",
1771
- {
1772
- onClick: (e) => {
1773
- e.stopPropagation();
1774
- client.invalidate(entry.key);
1775
- },
1776
- style: {
1777
- background: "#222",
1778
- border: "1px solid #333",
1779
- color: "#d69e2e",
1780
- padding: "4px 10px",
1781
- borderRadius: "4px",
1782
- cursor: "pointer",
1783
- fontSize: "11px"
1784
- },
1785
- children: "Invalidate"
1786
- }
1787
- ),
1788
- /* @__PURE__ */ jsx2(
1789
- "button",
1790
- {
1791
- onClick: (e) => {
1792
- e.stopPropagation();
1793
- client.remove(entry.key);
1794
- },
1795
- style: {
1796
- background: "#222",
1797
- border: "1px solid #333",
1798
- color: "#ff4d4f",
1799
- padding: "4px 10px",
1800
- borderRadius: "4px",
1801
- cursor: "pointer",
1802
- fontSize: "11px"
1803
- },
1804
- children: "Remove"
1805
- }
1806
- )
1807
- ] }),
1808
- /* @__PURE__ */ jsx2("div", { style: { position: "relative" }, children: /* @__PURE__ */ jsx2("pre", { style: {
1809
- margin: 0,
1810
- fontSize: "11px",
1811
- color: "#a0a0a0",
1812
- overflowX: "auto",
1813
- fontFamily: "monospace"
1814
- }, children: JSON.stringify(entry.data, null, 2) }) }),
1815
- /* @__PURE__ */ jsxs("div", { style: {
1816
- marginTop: "8px",
1817
- fontSize: "10px",
1818
- color: "#444",
1819
- textAlign: "right"
1820
- }, children: [
1821
- "Updated: ",
1822
- new Date(entry.updatedAt).toLocaleTimeString()
1823
- ] })
1824
- ] })
1825
- ] });
1982
+ }, [observer]);
1983
+ return observer.result$;
1826
1984
  }
1827
1985
  export {
1828
- QuantumDevTools,
1829
- QueryCache,
1986
+ HydrationBoundary,
1987
+ QueryClient,
1830
1988
  QueryClientProvider,
1831
- computed,
1989
+ SignalValue,
1990
+ atom,
1832
1991
  createHttpClient,
1833
- createState,
1834
- defineModel,
1992
+ dehydrate,
1835
1993
  enableDevTools,
1836
1994
  getPromiseState,
1837
1995
  handlePromise,
1996
+ hydrate,
1838
1997
  isPromise,
1839
- optimisticHelpers,
1840
- queryCache,
1841
1998
  scheduleUpdate,
1842
- subscribe,
1843
1999
  unwrapPromise,
1844
2000
  useInfiniteQuery,
1845
2001
  useMutation,
1846
2002
  usePaginatedQuery,
1847
2003
  useQuery,
1848
- useQueryCache,
2004
+ useQuery$,
1849
2005
  useQueryClient,
1850
- useStore
2006
+ useQueryStore,
2007
+ useSuspenseQuery
1851
2008
  };