@braine/quantum-query 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,22 +1,23 @@
1
1
  # @braine/quantum-query
2
2
 
3
3
  **State Management at the Speed of Light.**
4
- > A unified architecture that merges Store, Actions, and API Logic into single, high-performance "Smart Models".
4
+ > A unified, signal-based architecture that merges Store, Actions, and API Logic into a single, high-performance ecosystem.
5
5
 
6
6
  [![npm version](https://img.shields.io/npm/v/@braine/quantum-query.svg)](https://www.npmjs.com/package/@braine/quantum-query)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
8
 
9
- ## Why "Quantum"?
10
- Existing libraries behave like "Buses". They stop at every station (component) to check if someone needs to get off (re-render).
9
+ ## ⚡️ Why "Quantum"?
10
+ Existing libraries (Redux, RTK Query) behave like "Buses". They stop at every station (component) to check if someone needs to get off (re-render via O(n) selectors).
11
+
11
12
  **Quantum-Query** behaves like a teleporter. It updates *only* the specific component listening to a specific property, instantly.
12
13
 
13
- * **O(1) Reactivity**: No selectors. No linear scans.
14
- * **Zero Boilerplate**: No reduces, no thunks, no providers.
15
- * **Enterprise Grade**: Built-in HTTP client with automatic deduplication, retries, and cancellation.
14
+ * **O(1) Reactivity**: Powered by Atomic Signals. Zero selectors. No "Top-Down" re-renders.
15
+ * **Zero Boilerplate**: No reducers, no providers, no slices, no thunks.
16
+ * **Enterprise Ecosystem**: Persistence, Plugins, Deduplication, and Validation included.
16
17
 
17
18
  ---
18
19
 
19
- ## Installation
20
+ ## 📦 Installation
20
21
 
21
22
  ```bash
22
23
  npm install @braine/quantum-query
@@ -24,13 +25,32 @@ npm install @braine/quantum-query
24
25
 
25
26
  ---
26
27
 
27
- ## The "Smart Model" Pattern
28
+ ## 🚀 Quick Start (React Hooks)
29
+
30
+ If you just want to fetch data, it works exactly like you expect.
31
+
32
+ ```typescript
33
+ import { useQuery } from '@braine/quantum-query';
34
+
35
+ function UserProfile({ id }) {
36
+ const { data, isLoading } = useQuery({
37
+ queryKey: ['user', id],
38
+ queryFn: () => fetch(`/api/user/${id}`).then(r => r.json()),
39
+ staleTime: 5000 // Auto-cache for 5s
40
+ });
41
+
42
+ if (isLoading) return <div>Loading...</div>;
43
+ return <div>Hello, {data.name}!</div>;
44
+ }
45
+ ```
46
+
47
+ ---
28
48
 
29
- Stop splitting your logic between Redux (Client) and React Query (Server). Use **Smart Models**.
49
+ ## 🧠 The "Smart Model" Pattern (Advanced)
30
50
 
31
- ### 1. Define It
32
- `defineModel` wraps your state, computed properties, and actions into one reactive entity.
51
+ Stop splitting your logic between Redux (Client State) and React Query (Server State). **Smart Models** combine state, computed properties, and actions into one reactive entity.
33
52
 
53
+ ### 1. Define Model
34
54
  ```typescript
35
55
  import { defineModel } from '@braine/quantum-query';
36
56
 
@@ -45,48 +65,47 @@ export const TodoModel = defineModel({
45
65
  computed: {
46
66
  activeCount() {
47
67
  return this.items.length;
68
+ },
69
+ isEmpty() {
70
+ return this.items.length === 0;
48
71
  }
49
72
  },
50
73
 
51
74
  // 3. Actions (Sync + Async + Optimistic)
52
75
  actions: {
53
- async add(text: string) {
54
- // Optimistic Update (Instant UI)
55
- this.items.push(text);
56
-
57
- try {
58
- await api.post('/todos', { text });
59
- } catch (err) {
60
- this.items.pop(); // Auto-Rollback
61
- }
76
+ add(text: string) {
77
+ this.items.push(text); // Direct mutation (proxied)
78
+ },
79
+ async save() {
80
+ await api.post('/todos', { items: this.items });
62
81
  }
63
82
  }
64
83
  });
65
84
  ```
66
85
 
67
- ### 2. Use It
68
- Just use it.
69
-
86
+ ### 2. Use Model
70
87
  ```tsx
71
88
  import { useStore } from '@braine/quantum-query';
72
89
  import { TodoModel } from './models/TodoModel';
73
90
 
74
91
  function TodoApp() {
75
- const model = useStore(TodoModel); // Auto-subscribes!
92
+ // auto-subscribes ONLY to properties accessed in this component
93
+ const model = useStore(TodoModel);
76
94
 
77
95
  return (
78
- <button onClick={() => model.add("Ship it")}>
79
- Active: {model.activeCount}
80
- </button>
96
+ <div>
97
+ <h1>Active: {model.activeCount}</h1>
98
+ <button onClick={() => model.add("Ship it")}>Add</button>
99
+ </div>
81
100
  );
82
101
  }
83
102
  ```
84
103
 
85
104
  ---
86
105
 
87
- ## Enterprise HTTP Client
106
+ ## 🌐 Enterprise HTTP Client
88
107
 
89
- We built a fetch wrapper that matches **RTK Query** in power but keeps **Axios** simplicity.
108
+ We built a fetch wrapper that matches **RTK Query** in power but keeps **Axios** simplicity. It includes **Automatic Deduplication** and **Retries**.
90
109
 
91
110
  ```typescript
92
111
  import { createHttpClient } from '@braine/quantum-query';
@@ -94,9 +113,9 @@ import { createHttpClient } from '@braine/quantum-query';
94
113
  export const api = createHttpClient({
95
114
  baseURL: 'https://api.myapp.com',
96
115
  timeout: 5000,
97
- retry: { retries: 3 }, // Exponential backoff for 5xx/Network errors
116
+ retry: { retries: 3 }, // Exponential backoff for Network errors
98
117
 
99
- // Auth Handling
118
+ // Auth Handling (Auto-Refresh)
100
119
  auth: {
101
120
  getToken: () => localStorage.getItem('token'),
102
121
  onTokenExpired: async () => {
@@ -107,97 +126,87 @@ export const api = createHttpClient({
107
126
  }
108
127
  });
109
128
 
110
- // Automatic Deduplication
111
- // If 5 components call this at once, only 1 request is sent!
112
- const users = await api.get('/users');
129
+ // data is strictly typed!
130
+ const user = await api.get<User>('/me');
113
131
  ```
114
132
 
115
133
  ---
116
134
 
117
- ## Data Integrity (Runtime Safety)
135
+ ## 🛡️ Data Integrity (Runtime Safety)
118
136
 
119
- Don't trust the backend. Validate it. We support **Zod**, **Valibot**, or **Yup** directly.
137
+ Don't trust the backend. Validate it. We support **Zod**, **Valibot**, or **Yup** schemas directly in the hook.
120
138
 
121
139
  ```typescript
122
140
  import { z } from 'zod';
123
141
 
124
142
  const UserSchema = z.object({
125
143
  id: z.string(),
126
- name: z.string()
144
+ name: z.string(),
145
+ role: z.enum(['admin', 'user'])
127
146
  });
128
147
 
129
- // 1. Runtime Validation: Throws error if API returns garbage
130
- // 2. Auto-Typing: 'user' is inferred as { id: string, name: string }
131
- const user = await api.get('/me', {
132
- schema: UserSchema
148
+ const { data } = useQuery({
149
+ queryKey: ['user'],
150
+ queryFn: fetchUser,
151
+ schema: UserSchema // Throws descriptive error if API returns garbage
133
152
  });
134
153
  ```
135
154
 
136
155
  ---
137
156
 
138
- ## Enterprise Query Features
157
+ ## 🔌 Plugin System (Middleware) 🆕
139
158
 
140
- TanStack Query-level features with simpler API:
159
+ Inject logic into every request lifecycle (Logging, Analytics, Performance Monitoring).
141
160
 
142
161
  ```typescript
143
- import { useQuery, usePaginatedQuery, useInfiniteQuery, useMutation } from '@braine/quantum-query';
144
-
145
- // Background refetch (stale-while-revalidate)
146
- const { data, isStale } = useQuery({
147
- queryKey: ['user'],
148
- queryFn: () => api.get('/me'),
149
- staleTime: 30000,
150
- refetchOnWindowFocus: true // Auto-refresh on tab return
151
- });
162
+ import { queryCache } from '@braine/quantum-query';
152
163
 
153
- // Pagination
154
- const { data, nextPage, hasNext } = usePaginatedQuery({
155
- queryKey: ['users'],
156
- queryFn: (page) => api.get(`/users?page=${page}`)
164
+ queryCache.use({
165
+ name: 'logger',
166
+ onFetchStart: (key) => console.log('Fetching', key),
167
+ onFetchError: (key, error) => console.error(`Fetch failed for ${key}:`, error)
157
168
  });
169
+ ```
158
170
 
159
- // Infinite Scroll
160
- const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
161
- queryKey: ['feed'],
162
- queryFn: ({ pageParam }) => api.get(`/feed?cursor=${pageParam}`),
163
- getNextPageParam: (last) => last.nextCursor
164
- });
171
+ ---
165
172
 
166
- // Optimistic Updates
167
- const addTodo = useMutation({
168
- mutationFn: (todo) => api.post('/todos', todo),
169
- onMutate: async (newTodo) => {
170
- // Instant UI update
171
- const prev = optimisticHelpers.getQueryData(['todos']);
172
- optimisticHelpers.setQueryData(['todos'], old => [...old, newTodo]);
173
- return { prev };
174
- },
175
- onError: (err, vars, ctx) => {
176
- // Auto-rollback on error
177
- optimisticHelpers.setQueryData(['todos'], ctx.prev);
178
- }
173
+ ## 💾 Persistence Adapter 🆕
174
+
175
+ Persist your cache to `localStorage` (or IndexedDB/AsyncStorage) automatically. Works offline.
176
+
177
+ ```typescript
178
+ import { persistQueryClient, createLocalStoragePersister } from '@braine/quantum-query/persist';
179
+
180
+ persistQueryClient({
181
+ queryClient: queryCache,
182
+ persister: createLocalStoragePersister(),
183
+ maxAge: 1000 * 60 * 60 * 24 // 24 hours
179
184
  });
180
185
  ```
181
186
 
182
187
  ---
183
188
 
184
- ## Comparison
189
+ ## 📚 Documentation
185
190
 
186
- | Feature | Redux Toolkit + RTK Query | TanStack Query | **Quantum-Query** |
187
- | :--- | :--- | :--- | :--- |
188
- | **State + Queries** | Separate (Redux + RTK) | Queries only | **Unified** |
189
- | **Boilerplate** | High | Medium | **Minimal** ✅ |
190
- | **Performance** | Good | Good | **O(1) Reactivity** ✅ |
191
- | **Pagination** | Yes | Yes | **Yes** ✅ |
192
- | **Infinite Scroll** | Yes | Yes | **Yes** ✅ |
193
- | **Optimistic Updates** | Manual | Yes | **Yes** ✅ |
194
- | **Bundle Size** | ~40kb | ~13kb | **~8kb** ✅ |
195
- | **Learning Curve** | Steep | Medium | **Gentle** ✅ |
191
+ * **[API Reference](docs/api.md)**: Full method signatures and options.
192
+ * **[Recipes](docs/recipes.md)**: Common patterns (Auth, Infinite Scroll, Optimistic UI).
193
+ * **[Migration Guide](docs/migration.md)**: Step-by-step guide from RTK Query / Redux.
196
194
 
197
- **Alpha Status:** Battle-testing in progress. Use for new projects, migrate carefully for production.
195
+ ---
196
+
197
+ ## 🆚 Comparison
198
+
199
+ | Feature | RTK Query | TanStack Query | **Quantum-Query** |
200
+ | :--- | :--- | :--- | :--- |
201
+ | **Architecture** | Redux (Store + Slices) | Observers | **Atomic Signals** ✅ |
202
+ | **Boilerplate** | High (Provider + Store) | Medium | **Zero** ✅ |
203
+ | **Re-Renders** | Selector-based (O(n)) | Observer-based | **Signal-based (O(1))** ✅ |
204
+ | **Smart Models** | ❌ (Requires Redux) | ❌ | **Built-in** ✅ |
205
+ | **Bundle Size** | ~17kb | ~13kb | **~3kb** ✅ |
206
+ | **Deduplication** | Yes | Yes | **Yes** ✅ |
207
+ | **Persistence** | `redux-persist` | Experimental | **Built-in First Class** ✅ |
198
208
 
199
209
  ---
200
210
 
201
211
  ## License
202
212
  MIT
203
- # -braine-quantum-query
package/dist/index.cjs CHANGED
@@ -686,16 +686,16 @@ var QueryCache = class {
686
686
  /**
687
687
  * Generate cache key from query key array
688
688
  */
689
- generateKey(queryKey) {
689
+ generateKey = (queryKey) => {
690
690
  if (Array.isArray(queryKey)) {
691
691
  return stableHash(queryKey);
692
692
  }
693
693
  return stableHash([queryKey.key, queryKey.params]);
694
- }
694
+ };
695
695
  /**
696
696
  * Get data (wrapper around signal.get)
697
697
  */
698
- get(queryKey) {
698
+ get = (queryKey) => {
699
699
  const key = this.generateKey(queryKey);
700
700
  const signal = this.signals.get(key);
701
701
  if (!signal) return void 0;
@@ -708,12 +708,12 @@ var QueryCache = class {
708
708
  return void 0;
709
709
  }
710
710
  return entry.data;
711
- }
711
+ };
712
712
  /**
713
713
  * Get Signal for a key (Low level API for hooks)
714
714
  * Automatically creates a signal if one doesn't exist
715
715
  */
716
- getSignal(queryKey) {
716
+ getSignal = (queryKey) => {
717
717
  const key = this.generateKey(queryKey);
718
718
  let signal = this.signals.get(key);
719
719
  if (!signal) {
@@ -721,11 +721,11 @@ var QueryCache = class {
721
721
  this.signals.set(key, signal);
722
722
  }
723
723
  return signal;
724
- }
724
+ };
725
725
  /**
726
726
  * Check if data is stale
727
727
  */
728
- isStale(queryKey) {
728
+ isStale = (queryKey) => {
729
729
  const key = this.generateKey(queryKey);
730
730
  const signal = this.signals.get(key);
731
731
  if (!signal) return true;
@@ -734,11 +734,11 @@ var QueryCache = class {
734
734
  const now = Date.now();
735
735
  const age = now - entry.timestamp;
736
736
  return age > entry.staleTime;
737
- }
737
+ };
738
738
  /**
739
739
  * Set cached data (updates signal)
740
740
  */
741
- set(queryKey, data, options) {
741
+ set = (queryKey, data, options) => {
742
742
  const key = this.generateKey(queryKey);
743
743
  const entry = {
744
744
  data,
@@ -755,7 +755,7 @@ var QueryCache = class {
755
755
  }
756
756
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
757
757
  this.plugins.forEach((p) => p.onQueryUpdated?.(normalizedKey, data));
758
- }
758
+ };
759
759
  // --- DEDUPLICATION ---
760
760
  deduplicationCache = /* @__PURE__ */ new Map();
761
761
  // --- MIDDLEWARE / PLUGINS ---
@@ -763,15 +763,15 @@ var QueryCache = class {
763
763
  /**
764
764
  * Register a middleware plugin
765
765
  */
766
- use(plugin) {
766
+ use = (plugin) => {
767
767
  this.plugins.push(plugin);
768
768
  return this;
769
- }
769
+ };
770
770
  /**
771
771
  * Fetch data with deduplication.
772
772
  * If a request for the same key is already in flight, returns the existing promise.
773
773
  */
774
- async fetch(queryKey, fn) {
774
+ fetch = async (queryKey, fn) => {
775
775
  const key = this.generateKey(queryKey);
776
776
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
777
777
  if (this.deduplicationCache.has(key)) {
@@ -792,12 +792,12 @@ var QueryCache = class {
792
792
  );
793
793
  this.deduplicationCache.set(key, promise);
794
794
  return promise;
795
- }
795
+ };
796
796
  /**
797
797
  * Invalidate queries matching the key prefix
798
798
  * Marks them as undefined to trigger refetches without breaking subscriptions
799
799
  */
800
- invalidate(queryKey) {
800
+ invalidate = (queryKey) => {
801
801
  const prefix = this.generateKey(queryKey);
802
802
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
803
803
  this.plugins.forEach((p) => p.onInvalidate?.(normalizedKey));
@@ -813,23 +813,23 @@ var QueryCache = class {
813
813
  invalidateKey(key);
814
814
  }
815
815
  }
816
- }
816
+ };
817
817
  /**
818
818
  * Remove all cache entries
819
819
  */
820
- clear() {
820
+ clear = () => {
821
821
  this.signals.clear();
822
- }
822
+ };
823
823
  /**
824
824
  * Prefetch data (same as set but explicit intent)
825
825
  */
826
- prefetch(queryKey, data, options) {
826
+ prefetch = (queryKey, data, options) => {
827
827
  this.set(queryKey, data, options);
828
- }
828
+ };
829
829
  /**
830
830
  * Garbage collection - remove expired entries
831
831
  */
832
- startGarbageCollection() {
832
+ startGarbageCollection = () => {
833
833
  this.gcInterval = setInterval(() => {
834
834
  const now = Date.now();
835
835
  for (const [key, signal] of this.signals.entries()) {
@@ -841,37 +841,37 @@ var QueryCache = class {
841
841
  }
842
842
  }
843
843
  }, 60 * 1e3);
844
- }
844
+ };
845
845
  /**
846
846
  * Stop garbage collection
847
847
  */
848
- destroy() {
848
+ destroy = () => {
849
849
  if (this.gcInterval) {
850
850
  clearInterval(this.gcInterval);
851
851
  this.gcInterval = null;
852
852
  }
853
853
  this.clear();
854
- }
854
+ };
855
855
  /**
856
856
  * Get cache stats (for debugging)
857
857
  */
858
- getStats() {
858
+ getStats = () => {
859
859
  return {
860
860
  size: this.signals.size,
861
861
  keys: Array.from(this.signals.keys())
862
862
  };
863
- }
863
+ };
864
864
  /**
865
865
  * Get all entries (wrapper for DevTools)
866
866
  */
867
- getAll() {
867
+ getAll = () => {
868
868
  const map = /* @__PURE__ */ new Map();
869
869
  for (const [key, signal] of this.signals.entries()) {
870
870
  const val = signal.get();
871
871
  if (val) map.set(key, val);
872
872
  }
873
873
  return map;
874
- }
874
+ };
875
875
  };
876
876
  var queryCache = new QueryCache();
877
877
 
package/dist/index.d.cts CHANGED
@@ -155,50 +155,50 @@ declare class QueryCache {
155
155
  /**
156
156
  * Get data (wrapper around signal.get)
157
157
  */
158
- get<T>(queryKey: QueryKeyInput): T | undefined;
158
+ get: <T>(queryKey: QueryKeyInput) => T | undefined;
159
159
  /**
160
160
  * Get Signal for a key (Low level API for hooks)
161
161
  * Automatically creates a signal if one doesn't exist
162
162
  */
163
- getSignal<T>(queryKey: QueryKeyInput): Signal<CacheEntry<T> | undefined>;
163
+ getSignal: <T>(queryKey: QueryKeyInput) => Signal<CacheEntry<T> | undefined>;
164
164
  /**
165
165
  * Check if data is stale
166
166
  */
167
- isStale(queryKey: QueryKeyInput): boolean;
167
+ isStale: (queryKey: QueryKeyInput) => boolean;
168
168
  /**
169
169
  * Set cached data (updates signal)
170
170
  */
171
- set<T>(queryKey: QueryKeyInput, data: T, options?: {
171
+ set: <T>(queryKey: QueryKeyInput, data: T, options?: {
172
172
  staleTime?: number;
173
173
  cacheTime?: number;
174
- }): void;
174
+ }) => void;
175
175
  private deduplicationCache;
176
176
  private plugins;
177
177
  /**
178
178
  * Register a middleware plugin
179
179
  */
180
- use(plugin: QueryPlugin): this;
180
+ use: (plugin: QueryPlugin) => this;
181
181
  /**
182
182
  * Fetch data with deduplication.
183
183
  * If a request for the same key is already in flight, returns the existing promise.
184
184
  */
185
- fetch<T>(queryKey: QueryKeyInput, fn: () => Promise<T>): Promise<T>;
185
+ fetch: <T>(queryKey: QueryKeyInput, fn: () => Promise<T>) => Promise<T>;
186
186
  /**
187
187
  * Invalidate queries matching the key prefix
188
188
  * Marks them as undefined to trigger refetches without breaking subscriptions
189
189
  */
190
- invalidate(queryKey: QueryKeyInput): void;
190
+ invalidate: (queryKey: QueryKeyInput) => void;
191
191
  /**
192
192
  * Remove all cache entries
193
193
  */
194
- clear(): void;
194
+ clear: () => void;
195
195
  /**
196
196
  * Prefetch data (same as set but explicit intent)
197
197
  */
198
- prefetch<T>(queryKey: QueryKeyInput, data: T, options?: {
198
+ prefetch: <T>(queryKey: QueryKeyInput, data: T, options?: {
199
199
  staleTime?: number;
200
200
  cacheTime?: number;
201
- }): void;
201
+ }) => void;
202
202
  /**
203
203
  * Garbage collection - remove expired entries
204
204
  */
@@ -206,18 +206,18 @@ declare class QueryCache {
206
206
  /**
207
207
  * Stop garbage collection
208
208
  */
209
- destroy(): void;
209
+ destroy: () => void;
210
210
  /**
211
211
  * Get cache stats (for debugging)
212
212
  */
213
- getStats(): {
213
+ getStats: () => {
214
214
  size: number;
215
215
  keys: string[];
216
216
  };
217
217
  /**
218
218
  * Get all entries (wrapper for DevTools)
219
219
  */
220
- getAll(): Map<string, CacheEntry>;
220
+ getAll: () => Map<string, CacheEntry>;
221
221
  }
222
222
  declare const queryCache: QueryCache;
223
223
 
package/dist/index.d.ts CHANGED
@@ -155,50 +155,50 @@ declare class QueryCache {
155
155
  /**
156
156
  * Get data (wrapper around signal.get)
157
157
  */
158
- get<T>(queryKey: QueryKeyInput): T | undefined;
158
+ get: <T>(queryKey: QueryKeyInput) => T | undefined;
159
159
  /**
160
160
  * Get Signal for a key (Low level API for hooks)
161
161
  * Automatically creates a signal if one doesn't exist
162
162
  */
163
- getSignal<T>(queryKey: QueryKeyInput): Signal<CacheEntry<T> | undefined>;
163
+ getSignal: <T>(queryKey: QueryKeyInput) => Signal<CacheEntry<T> | undefined>;
164
164
  /**
165
165
  * Check if data is stale
166
166
  */
167
- isStale(queryKey: QueryKeyInput): boolean;
167
+ isStale: (queryKey: QueryKeyInput) => boolean;
168
168
  /**
169
169
  * Set cached data (updates signal)
170
170
  */
171
- set<T>(queryKey: QueryKeyInput, data: T, options?: {
171
+ set: <T>(queryKey: QueryKeyInput, data: T, options?: {
172
172
  staleTime?: number;
173
173
  cacheTime?: number;
174
- }): void;
174
+ }) => void;
175
175
  private deduplicationCache;
176
176
  private plugins;
177
177
  /**
178
178
  * Register a middleware plugin
179
179
  */
180
- use(plugin: QueryPlugin): this;
180
+ use: (plugin: QueryPlugin) => this;
181
181
  /**
182
182
  * Fetch data with deduplication.
183
183
  * If a request for the same key is already in flight, returns the existing promise.
184
184
  */
185
- fetch<T>(queryKey: QueryKeyInput, fn: () => Promise<T>): Promise<T>;
185
+ fetch: <T>(queryKey: QueryKeyInput, fn: () => Promise<T>) => Promise<T>;
186
186
  /**
187
187
  * Invalidate queries matching the key prefix
188
188
  * Marks them as undefined to trigger refetches without breaking subscriptions
189
189
  */
190
- invalidate(queryKey: QueryKeyInput): void;
190
+ invalidate: (queryKey: QueryKeyInput) => void;
191
191
  /**
192
192
  * Remove all cache entries
193
193
  */
194
- clear(): void;
194
+ clear: () => void;
195
195
  /**
196
196
  * Prefetch data (same as set but explicit intent)
197
197
  */
198
- prefetch<T>(queryKey: QueryKeyInput, data: T, options?: {
198
+ prefetch: <T>(queryKey: QueryKeyInput, data: T, options?: {
199
199
  staleTime?: number;
200
200
  cacheTime?: number;
201
- }): void;
201
+ }) => void;
202
202
  /**
203
203
  * Garbage collection - remove expired entries
204
204
  */
@@ -206,18 +206,18 @@ declare class QueryCache {
206
206
  /**
207
207
  * Stop garbage collection
208
208
  */
209
- destroy(): void;
209
+ destroy: () => void;
210
210
  /**
211
211
  * Get cache stats (for debugging)
212
212
  */
213
- getStats(): {
213
+ getStats: () => {
214
214
  size: number;
215
215
  keys: string[];
216
216
  };
217
217
  /**
218
218
  * Get all entries (wrapper for DevTools)
219
219
  */
220
- getAll(): Map<string, CacheEntry>;
220
+ getAll: () => Map<string, CacheEntry>;
221
221
  }
222
222
  declare const queryCache: QueryCache;
223
223
 
package/dist/index.js CHANGED
@@ -638,16 +638,16 @@ var QueryCache = class {
638
638
  /**
639
639
  * Generate cache key from query key array
640
640
  */
641
- generateKey(queryKey) {
641
+ generateKey = (queryKey) => {
642
642
  if (Array.isArray(queryKey)) {
643
643
  return stableHash(queryKey);
644
644
  }
645
645
  return stableHash([queryKey.key, queryKey.params]);
646
- }
646
+ };
647
647
  /**
648
648
  * Get data (wrapper around signal.get)
649
649
  */
650
- get(queryKey) {
650
+ get = (queryKey) => {
651
651
  const key = this.generateKey(queryKey);
652
652
  const signal = this.signals.get(key);
653
653
  if (!signal) return void 0;
@@ -660,12 +660,12 @@ var QueryCache = class {
660
660
  return void 0;
661
661
  }
662
662
  return entry.data;
663
- }
663
+ };
664
664
  /**
665
665
  * Get Signal for a key (Low level API for hooks)
666
666
  * Automatically creates a signal if one doesn't exist
667
667
  */
668
- getSignal(queryKey) {
668
+ getSignal = (queryKey) => {
669
669
  const key = this.generateKey(queryKey);
670
670
  let signal = this.signals.get(key);
671
671
  if (!signal) {
@@ -673,11 +673,11 @@ var QueryCache = class {
673
673
  this.signals.set(key, signal);
674
674
  }
675
675
  return signal;
676
- }
676
+ };
677
677
  /**
678
678
  * Check if data is stale
679
679
  */
680
- isStale(queryKey) {
680
+ isStale = (queryKey) => {
681
681
  const key = this.generateKey(queryKey);
682
682
  const signal = this.signals.get(key);
683
683
  if (!signal) return true;
@@ -686,11 +686,11 @@ var QueryCache = class {
686
686
  const now = Date.now();
687
687
  const age = now - entry.timestamp;
688
688
  return age > entry.staleTime;
689
- }
689
+ };
690
690
  /**
691
691
  * Set cached data (updates signal)
692
692
  */
693
- set(queryKey, data, options) {
693
+ set = (queryKey, data, options) => {
694
694
  const key = this.generateKey(queryKey);
695
695
  const entry = {
696
696
  data,
@@ -707,7 +707,7 @@ var QueryCache = class {
707
707
  }
708
708
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
709
709
  this.plugins.forEach((p) => p.onQueryUpdated?.(normalizedKey, data));
710
- }
710
+ };
711
711
  // --- DEDUPLICATION ---
712
712
  deduplicationCache = /* @__PURE__ */ new Map();
713
713
  // --- MIDDLEWARE / PLUGINS ---
@@ -715,15 +715,15 @@ var QueryCache = class {
715
715
  /**
716
716
  * Register a middleware plugin
717
717
  */
718
- use(plugin) {
718
+ use = (plugin) => {
719
719
  this.plugins.push(plugin);
720
720
  return this;
721
- }
721
+ };
722
722
  /**
723
723
  * Fetch data with deduplication.
724
724
  * If a request for the same key is already in flight, returns the existing promise.
725
725
  */
726
- async fetch(queryKey, fn) {
726
+ fetch = async (queryKey, fn) => {
727
727
  const key = this.generateKey(queryKey);
728
728
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
729
729
  if (this.deduplicationCache.has(key)) {
@@ -744,12 +744,12 @@ var QueryCache = class {
744
744
  );
745
745
  this.deduplicationCache.set(key, promise);
746
746
  return promise;
747
- }
747
+ };
748
748
  /**
749
749
  * Invalidate queries matching the key prefix
750
750
  * Marks them as undefined to trigger refetches without breaking subscriptions
751
751
  */
752
- invalidate(queryKey) {
752
+ invalidate = (queryKey) => {
753
753
  const prefix = this.generateKey(queryKey);
754
754
  const normalizedKey = Array.isArray(queryKey) ? queryKey : [queryKey.key, queryKey.params];
755
755
  this.plugins.forEach((p) => p.onInvalidate?.(normalizedKey));
@@ -765,23 +765,23 @@ var QueryCache = class {
765
765
  invalidateKey(key);
766
766
  }
767
767
  }
768
- }
768
+ };
769
769
  /**
770
770
  * Remove all cache entries
771
771
  */
772
- clear() {
772
+ clear = () => {
773
773
  this.signals.clear();
774
- }
774
+ };
775
775
  /**
776
776
  * Prefetch data (same as set but explicit intent)
777
777
  */
778
- prefetch(queryKey, data, options) {
778
+ prefetch = (queryKey, data, options) => {
779
779
  this.set(queryKey, data, options);
780
- }
780
+ };
781
781
  /**
782
782
  * Garbage collection - remove expired entries
783
783
  */
784
- startGarbageCollection() {
784
+ startGarbageCollection = () => {
785
785
  this.gcInterval = setInterval(() => {
786
786
  const now = Date.now();
787
787
  for (const [key, signal] of this.signals.entries()) {
@@ -793,37 +793,37 @@ var QueryCache = class {
793
793
  }
794
794
  }
795
795
  }, 60 * 1e3);
796
- }
796
+ };
797
797
  /**
798
798
  * Stop garbage collection
799
799
  */
800
- destroy() {
800
+ destroy = () => {
801
801
  if (this.gcInterval) {
802
802
  clearInterval(this.gcInterval);
803
803
  this.gcInterval = null;
804
804
  }
805
805
  this.clear();
806
- }
806
+ };
807
807
  /**
808
808
  * Get cache stats (for debugging)
809
809
  */
810
- getStats() {
810
+ getStats = () => {
811
811
  return {
812
812
  size: this.signals.size,
813
813
  keys: Array.from(this.signals.keys())
814
814
  };
815
- }
815
+ };
816
816
  /**
817
817
  * Get all entries (wrapper for DevTools)
818
818
  */
819
- getAll() {
819
+ getAll = () => {
820
820
  const map = /* @__PURE__ */ new Map();
821
821
  for (const [key, signal] of this.signals.entries()) {
822
822
  const val = signal.get();
823
823
  if (val) map.set(key, val);
824
824
  }
825
825
  return map;
826
- }
826
+ };
827
827
  };
828
828
  var queryCache = new QueryCache();
829
829
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@braine/quantum-query",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "scripts": {