floppy-disk 3.2.1 → 3.3.0-beta.1

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  A unified state model for **sync & async** data.
4
4
 
5
- If you know [Zustand](https://zustand.docs.pmnd.rs) & [TanStack-Query](https://tanstack.com/query), you already know FloppyDisk.\
5
+ If you know [Zustand](https://zustand.docs.pmnd.rs) & [TanStack-Query](https://tanstack.com/query), you already know FloppyDisk(.ts).\
6
6
  It keeps what works, removes unnecessary complexity, and unifies everything into a simpler API.\
7
7
  No relearning—just a better experience.
8
8
 
@@ -16,535 +16,80 @@ Demo: https://afiiif.github.io/floppy-disk/
16
16
  npm install floppy-disk
17
17
  ```
18
18
 
19
- ## Global Store
19
+ ## In short, it is:
20
20
 
21
- Here's how to create and use a store:
21
+ - **Like Zustand, but has additional capabilities:**
22
+ - No selectors: automatically optimizes re-renders
23
+ - Store events: `onFirstSubscribe`, `onSubscribe`, `onUnsubscribe`, `onLastUnsubscribe`
24
+ - Easier to set initial state on SSR/SSG
25
+ - Smaller bundle
26
+ - **Like TanStack Query, but:**
27
+ - DX is very similar to Zustand → One mental model for sync & async
28
+ - Much smaller bundle than TanStack Query → With nearly the same capabilities
22
29
 
23
- ```tsx
24
- import { createStore } from "floppy-disk/react";
30
+ **Docs: https://floppy-disk.vercel.app**
25
31
 
26
- const useDigimon = createStore({
27
- age: 7,
28
- level: "Rookie",
29
- });
30
- ```
32
+ ## Store (Global State)
31
33
 
32
- You can use the store both inside and outside of React components.
34
+ A store is a global state container that can be used both **inside and outside** React.\
35
+ With FloppyDisk, creating a store is simple:
33
36
 
34
37
  ```tsx
35
- function MyDigimon() {
36
- const { age } = useDigimon();
37
- return <div>Digimon age: {age}</div>;
38
- // This component will only re-render when `age` changes.
39
- // Changes to `level` will NOT trigger a re-render.
40
- }
41
-
42
- function Control() {
43
- return (
44
- <>
45
- <button
46
- onClick={() => {
47
- // You can setState directly
48
- useDigimon.setState((prev) => ({ age: prev.age + 1 }));
49
- }}
50
- >
51
- Increase digimon's age
52
- </button>
53
-
54
- <button onClick={evolve}>Evolve</button>
55
- </>
56
- );
57
- }
58
-
59
- // Create your own custom action
60
- const evolve = () => {
61
- const { level } = useDigimon.getState();
62
-
63
- const order = ["In-Training", "Rookie", "Champion", "Ultimate"];
64
- const nextLevel = order[order.indexOf(level) + 1];
65
-
66
- if (!nextLevel) return console.warn("Already at ultimate level");
67
-
68
- useDigimon.setState({ level: nextLevel });
69
- };
70
- ```
71
-
72
- ### Store Subscription
73
-
74
- At its core, FloppyDisk is a **pub-sub store**.
75
-
76
- You can subscribe manually:
38
+ import { createStore } from "floppy-disk/react";
77
39
 
78
- ```tsx
79
- const unsubscribe = useMyStore.subscribe((state, prev) => {
80
- console.log("New state:", state);
40
+ const useLawn = createStore({
41
+ plants: 3,
42
+ zombies: 1,
81
43
  });
82
-
83
- // Later
84
- unsubscribe();
85
44
  ```
86
45
 
87
- FloppyDisk also provides lifecycle hooks tied to subscription count:
88
-
89
- ```tsx
90
- const useTowerDefense = createStore(
91
- { archers: 3, mages: 1, barracks: 2, artillery: 1 },
92
- {
93
- onFirstSubscribe: () => {
94
- console.log("First subscriber! We’re officially popular 🎉");
95
- },
96
- onSubscribe: () => {
97
- console.log("New subscriber joined. Welcome aboard 🫡");
98
- },
99
- onUnsubscribe: () => {
100
- console.log("Subscriber left... was it something I said? 😭");
101
- },
102
- onLastUnsubscribe: () => {
103
- console.log("Everyone left. Guess I’ll just exist quietly now...");
104
- },
105
- },
106
- );
107
- ```
108
-
109
- ### Differences from Zustand
110
-
111
- If you're coming from Zustand, this should feel very familiar.\
112
- Key differences:
113
-
114
- 1. **No Selectors Needed**\
115
- You don't need selectors when using hooks.
116
- FloppyDisk automatically tracks which parts of the state are used and optimizes re-renders accordingly.
117
- 2. **Object-Only Store Initialization**\
118
- In FloppyDisk, stores **must** be initialized with an object. Primitive values or function initializers are not allowed.
119
-
120
- Zustand examples:
46
+ Use it inside a component:
121
47
 
122
48
  ```tsx
123
- const useDate = create(new Date(2021, 01, 11));
49
+ function MyPlants() {
50
+ const { plants } = useLawn(); // No selectors needed.
124
51
 
125
- const useCounter = create((set) => ({
126
- value: 1,
127
- increment: () => set((prev) => ({ value: prev.value + 1 })),
128
- }));
52
+ return <div>Plants: {plants}</div>; // Only re-render when plants state changes
53
+ }
129
54
  ```
130
55
 
131
- FloppyDisk equivalents:
56
+ Update the state **anywhere**:
132
57
 
133
58
  ```tsx
134
- const useDate = createStore({ value: new Date(2021, 01, 11) });
135
-
136
- const useCounter = createStore({ value: 1 });
137
- const increment = () => useCounter.setState((prev) => ({ value: prev.value + 1 }));
138
- // Unlike Zustand, defining actions inside the store is **discouraged** in FloppyDisk.
139
- // This improves tree-shakeability and keeps your store minimal.
140
-
141
- // However, it's still possible to mix actions with the state if you understand how closures work:
142
- const useCounterAlt = createStore({
143
- value: 1,
144
- increment: () => useCounterAlt.setState((prev) => ({ value: prev.value + 1 })),
145
- });
59
+ const addPlant = () => {
60
+ useLawn.setState(prev => ({ plants: prev.plants + 1 }));
61
+ };
146
62
  ```
147
63
 
148
- ## Async State (Query & Mutation)
149
-
150
- FloppyDisk also provides a powerful async state layer, inspired by [TanStack-Query](https://tanstack.com/query) but with a simpler API.
151
-
152
- It is agnostic to the type of async operation,
153
- it works with any Promise-based operation—whether it's a network request, local computation, storage access, or something else.
154
-
155
- Because of that, we intentionally avoid terms like "fetch" or "refetch".\
156
- Instead, we use:
64
+ ## Query Store for Async State
157
65
 
158
- - **execute** run the async operation (same as "fetch" in TanStack-Query)
159
- - **revalidate** → re-run while keeping existing data (same as "refetch" in TanStack-Query)
160
-
161
- ### Query vs Mutation
162
-
163
- <details>
164
-
165
- <summary>Query → Read Operations</summary>
166
-
167
- Queries are designed for reading data.\
168
- They assume:
169
-
170
- - no side effects
171
- - no data mutation
172
- - safe to run multiple times
173
-
174
- Because of this, queries come with helpful defaults:
175
-
176
- - ✅ Retry mechanism (for transient failures)
177
- - ✅ Revalidation (keep data fresh automatically)
178
- - ✅ Caching & staleness control
179
-
180
- Use queries when:
181
-
182
- - fetching data
183
- - reading from storage
184
- - running idempotent async logic
185
-
186
- </details>
187
-
188
- <details>
189
-
190
- <summary>Mutation → Write Operations</summary>
191
-
192
- Mutations are designed for changing data.\
193
- Examples:
194
-
195
- - insert
196
- - update
197
- - delete
198
- - triggering side effects
199
-
200
- Because mutations are **not safe to repeat blindly**, FloppyDisk does **not** include:
201
-
202
- - ❌ automatic retry
203
- - ❌ automatic revalidation
204
- - ❌ implicit re-execution
205
-
206
- This is intentional.\
207
- Mutations should be explicit and controlled, not automatic.
208
-
209
- If you need retry mechanism, then you can always add it manually.
210
-
211
- </details>
212
-
213
- ### Single Query
214
-
215
- Create a query using `createQuery`:
66
+ Create a query store for async data with `createQuery`:
216
67
 
217
68
  ```tsx
218
69
  import { createQuery } from "floppy-disk/react";
219
70
 
220
- const myQuery = createQuery(
221
- myAsyncFn,
222
- // { staleTime: 5000, revalidateOnFocus: false } <-- optional options
223
- );
224
-
225
- const useMyQuery = myQuery();
226
-
227
- // Use it inside your component:
228
-
229
- function MyComponent() {
230
- const { data, error } = useMyQuery();
231
-
232
- if (!data && !error) return <div>Loading...</div>;
233
- if (error) return <div>Error: {error.message}</div>;
234
-
235
- return <div>{data.foo} {data.bar}</div>;
236
- }
237
- ```
238
-
239
- ### Query State: Two Independent Dimensions
240
-
241
- FloppyDisk tracks two things separately:
242
-
243
- - Is it running? → `isPending`
244
- - What's the result? → `state`
245
-
246
- They are **independent**.
247
-
248
- ### Keyed Query (Dynamic Params)
249
-
250
- You can create parameterized queries:
251
-
252
- ```tsx
253
- import { createQuery } from "floppy-disk/react";
254
-
255
- import { getUserById, type GetUserByIdResponse } from "../utils"; // Your own module
256
-
257
- type MyQueryParam = { id: string };
258
-
259
- const userQuery = createQuery<GetUserByIdResponse, MyQueryParam>(
260
- getUserById,
261
- // { staleTime: 5000, revalidateOnFocus: false } <-- optional options
71
+ const plantDetailQuery = createQuery(
72
+ async ({ id }) => {
73
+ const res = await fetch(`/api/plants/${id}`);
74
+ return res.json();
75
+ },
262
76
  );
263
77
  ```
264
78
 
265
- Use it with parameters:
79
+ Use it in your component:
266
80
 
267
81
  ```tsx
268
- function UserDetail({ id }) {
269
- const useUserQuery = userQuery({ id });
270
- const { data, error } = useUserQuery();
82
+ function PlantDetail({ id }) {
83
+ const usePlantDetailQuery = plantDetailQuery({ id });
84
+ const { data, error } = usePlantDetailQuery();
271
85
 
272
86
  if (!data && !error) return <div>Loading...</div>;
273
87
  if (error) return <div>Error: {error.message}</div>;
274
88
 
275
- return <div>Name: {data.name}, email: {data.email}</div>;
89
+ return <div>Name: {data.name}, damage: {data.damage}</div>;
276
90
  }
277
91
  ```
278
92
 
279
- Each unique parameter creates its own cache entry.
280
-
281
- ### Store Inheritance
282
-
283
- Queries in FloppyDisk are built on top of the core store.
284
- This means every query inherits the same capabilities, such as `subscribe`, `getState`, and store events.
285
- It also gets **automatic reactivity** out of the box, so components rerender only when the state they use actually changes.
286
-
287
- ```tsx
288
- const { data } = useMyQuery();
289
- // ^Only data changes will trigger a re-render
290
-
291
- const value = useMyQuery().data?.foo.bar.baz;
292
- // ^Only data.foo.bar.baz changes will trigger a re-render
293
- ```
294
-
295
- Get state outside React:
296
-
297
- ```tsx
298
- const myQuery = createQuery<AsyncResponse>(myAsyncFn); // Query without paramerer
299
- const userQuery = createQuery<UserDetail, { id: string }>(getUserById); // Parameterized query
300
-
301
- const getMyQueryData = () => myQuery().getState().data;
302
- const getUserQueryData = ({ id }) => userQuery({ id }).getState().data;
303
- ```
304
-
305
- ### Infinite Query
93
+ ---
306
94
 
307
- FloppyDisk does **not provide** a dedicated "infinite query" API.\
308
- Instead, it embraces a simpler and more flexible approach:
309
-
310
- > Infinite queries are just **composition** + **recursion**.
311
-
312
- Why? Because async state is already powerful enough:
313
-
314
- - keyed queries handle parameters
315
- - components handle composition
316
- - recursion handles pagination
317
-
318
- No special abstraction needed.
319
-
320
- Here is the example on how to implement infinite query properly:
321
-
322
- ```tsx
323
- type GetPostParams = {
324
- cursor?: string; // For pagination
325
- };
326
- type GetPostsResponse = {
327
- posts: Post[];
328
- meta: { nextCursor: string };
329
- };
330
-
331
- const postsQuery = createQuery<GetPostsResponse, GetPostParams>(getPosts, {
332
- staleTime: Infinity,
333
- revalidateOnFocus: false,
334
- revalidateOnReconnect: false,
335
- });
336
-
337
- function Main() {
338
- return <Page cursor={undefined} />;
339
- }
340
-
341
- function Page({ cursor }: { cursor?: string }) {
342
- const usePostsQuery = postsQuery({ cursor });
343
- const { state, data, error } = usePostsQuery();
344
-
345
- if (!data && !error) return <div>Loading...</div>;
346
- if (error) return <div>Error</div>;
347
-
348
- return (
349
- <>
350
- {data.posts.map((post) => (
351
- <PostCard key={post.id} post={post} />
352
- ))}
353
- {data.meta.nextCursor && <LoadMore nextCursor={data.meta.nextCursor} />}
354
- </>
355
- );
356
- }
357
-
358
- function LoadMore({ nextCursor }: { nextCursor?: string }) {
359
- const [isNextPageRequested, setIsNextPageRequested] = useState(() => {
360
- const stateOfNextPageQuery = postsQuery({ cursor: nextCursor }).getState();
361
- return stateOfNextPageQuery.isPending || stateOfNextPageQuery.isSuccess;
362
- });
363
-
364
- if (isNextPageRequested) {
365
- return <Page cursor={nextCursor} />;
366
- }
367
-
368
- return <BottomObserver onReachBottom={() => setIsNextPageRequested(true)} />;
369
- }
370
- ```
371
-
372
- When implementing infinite queries, it is **highly recommended to disable automatic revalidation**.
373
-
374
- Why?\
375
- In an infinite list, users may scroll through many pages ("_doom-scrolling_").\
376
- If revalidation is triggered:
377
-
378
- - All previously loaded pages may re-execute
379
- - Content at the top may change without the user noticing
380
- - Layout shifts can occur unexpectedly
381
-
382
- This leads to a **confusing and unstable user experience**.\
383
- Revalidating dozens of previously viewed pages rarely provides value to the user.
384
-
385
- ## SSR Guidance
386
-
387
- Examples for using stores and queries in SSR with isolated data (no shared state between users).
388
-
389
- ### Initialize Store State from Server
390
-
391
- ```tsx
392
- const useCountStore = createStore({ count: 0 });
393
-
394
- function Page({ initialCount }) {
395
- const { count } = useCountStore({
396
- initialState: { count: initialCount }, // e.g. 3
397
- });
398
-
399
- return <>count is {count}</>; // Output: count is 3
400
- }
401
- ```
402
-
403
- ### Initialize Query Data from Server
404
-
405
- ```tsx
406
- async function MyServerComponent() {
407
- const data = await getData(); // e.g. { count: 3 }
408
- return <MyClientComponent initialData={data} />;
409
- }
410
-
411
- const myQuery = createQuery(getData);
412
- const useMyQuery = myQuery();
413
-
414
- function MyClientComponent({ initialData }) {
415
- const { data } = useMyQuery({
416
- initialData: initialData,
417
- // initialDataIsStale: true <-- Optional, default to false (no immediate revalidation)
418
- });
419
-
420
- return <>count is {data.count}</>; // Output: count is 3
421
- }
422
- ```
423
-
424
- ## Query State Machine
425
-
426
- This is how the query state transition flow looks like:
427
-
428
- ```
429
- initial failed, won't retry
430
- ┌────────────────────────────┐ ┌────────────────────────────┐
431
- │ isPending: false │ Δ│ isPending: false │
432
- │ isRevalidating: false │ │ isRevalidating: false │
433
- │ │ ┌──────────────────▶ │ │
434
- │ state: "INITIAL" │ │ Δ│ state: "ERROR" │
435
- │ isSuccess: false │ │ │ isSuccess: false │
436
- │ data: undefined │ │ │ data: undefined │
437
- │ dataUpdatedAt: undefined │ │ │ dataUpdatedAt: undefined │
438
- │ dataStaleAt: undefined │ │ │ dataStaleAt: undefined │
439
- │ isError: false │ │ Δ│ isError: true │
440
- │ error: undefined │ │ Δ│ error: <TError> │
441
- │ errorUpdatedAt: undefined │ │ Δ│ errorUpdatedAt: <number> │
442
- │ │ │ │ │
443
- │ willRetryAt: undefined │ │ │ willRetryAt: undefined │
444
- │ isRetrying: false │ │ •│ isRetrying: false │
445
- │ retryCount: 0 │ │ •│ retryCount: 0 (reset) │
446
- └─────────────┬──────────────┘ │ └────────────────────────────┘
447
- │ │
448
- │ execute │
449
- ▼ │ waiting retry delay
450
- ┌────────────────────────────┐ (N) ┌────────────────────────────┐
451
- Δ│ isPending: true [ƒ]│ │ Δ│ isPending: false │
452
- │ isRevalidating: false │ fail │ │ isRevalidating: false │
453
- │ ├──────────▶ Should retry? ────(Y)────▶ │ │
454
- │ state: "INITIAL" │ ▲ │ state: "INITIAL" │
455
- │ isSuccess: false │ │ │ isSuccess: false │
456
- │ data: undefined │ │ │ data: undefined │
457
- │ dataUpdatedAt: undefined │ │ │ dataUpdatedAt: undefined │
458
- │ dataStaleAt: undefined │ │ │ dataStaleAt: undefined │
459
- │ isError: false │ │ │ isError: false │
460
- │ error: undefined │ │ │ error: undefined │
461
- │ errorUpdatedAt: undefined │ │ │ errorUpdatedAt: undefined │
462
- │ │ │ │ │
463
- │ willRetryAt: undefined │ │ Δ│ willRetryAt: <number> │
464
- │ isRetrying: false │ │ │ isRetrying: false │
465
- │ retryCount: 0 │ │ │ retryCount: <number> │
466
- └─────────────┬──────────────┘ │ └─────────────┬──────────────┘
467
- │ │ │
468
- │ success │ │ retrying
469
- ▼ │ ▼
470
- ┌────────────────────────────┐ │ ┌────────────────────────────┐
471
- Δ│ isPending: false │ │ Δ│ isPending: true [ƒ]│
472
- │ isRevalidating: false │ │ fail │ isRevalidating: false │
473
- │ │ └────────────────────┤ │
474
- Δ│ state: "SUCCESS" │ │ state: "INITIAL" │
475
- Δ│ isSuccess: true │ │ isSuccess: false │
476
- Δ│ data: <TData> │ │ data: undefined │
477
- Δ│ dataUpdatedAt: <number> │ │ dataUpdatedAt: undefined │
478
- Δ│ dataStaleAt: <number> │ │ dataStaleAt: undefined │
479
- │ isError: false │ │ isError: false │
480
- │ error: undefined │ │ error: undefined │
481
- │ errorUpdatedAt: undefined │ success │ errorUpdatedAt: undefined │
482
- │ │ ◀─────────────────────────────────────┤ │
483
- │ willRetryAt: undefined │ Δ│ willRetryAt: undefined │
484
- •│ isRetrying: false │ Δ│ isRetrying: true │
485
- •│ retryCount: 0 (reset) │ Δ│ retryCount: <number> (+1) │
486
- └────────────────────────────┘ └────────────────────────────┘
487
- ```
488
-
489
- And then after success:
490
-
491
- ```
492
- success failed, won't retry
493
- ┌────────────────────────────┐ ┌─────────────────────────────────────────┐
494
- │ isPending: false │ Δ│ isPending: false │
495
- │ isRevalidating: false │ Δ│ isRevalidating: false │
496
- │ │ ┌──────────────────▶ │ │
497
- │ state: "SUCCESS" │ │ Δ│ state: "SUCCESS_BUT_REVALIDATION_ERROR" │
498
- │ isSuccess: true │ │ │ isSuccess: true │
499
- │ data: <TData> │ │ │ data: <TData> │
500
- │ dataUpdatedAt: <number> │ │ │ dataUpdatedAt: <number> │
501
- │ dataStaleAt: <number> │ │ │ dataStaleAt: <number> │
502
- │ isError: false │ │ │ isError: false │
503
- │ error: undefined │ │ Δ│ error: <TError> │
504
- │ errorUpdatedAt: undefined │ │ Δ│ errorUpdatedAt: <number> │
505
- │ │ │ │ │
506
- │ willRetryAt: undefined │ │ │ willRetryAt: undefined │
507
- │ isRetrying: false │ │ •│ isRetrying: false │
508
- │ retryCount: 0 │ │ •│ retryCount: 0 (reset) │
509
- └─────────────┬──────────────┘ │ └─────────────────────────────────────────┘
510
- │ │
511
- │ revalidate │
512
- ▼ │ waiting retry delay
513
- ┌────────────────────────────┐ (N) ┌────────────────────────────┐
514
- Δ│ isPending: true [ƒ]│ │ Δ│ isPending: false │
515
- Δ│ isRevalidating: true │ fail │ Δ│ isRevalidating: false │
516
- │ ├──────────▶ Should retry? ────(Y)────▶ │ │
517
- │ state: "SUCCESS" │ ▲ │ state: "SUCCESS" │
518
- │ isSuccess: true │ │ │ isSuccess: true │
519
- │ data: <TData> │ │ │ data: <TData> │
520
- │ dataUpdatedAt: <number> │ │ │ dataUpdatedAt: <number> │
521
- │ dataStaleAt: <number> │ │ │ dataStaleAt: <number> │
522
- │ isError: false │ │ │ isError: false │
523
- │ error: undefined │ │ │ error: undefined │
524
- │ errorUpdatedAt: undefined │ │ │ errorUpdatedAt: undefined │
525
- │ │ │ │ │
526
- │ willRetryAt: undefined │ │ Δ│ willRetryAt: <number> │
527
- │ isRetrying: false │ │ │ isRetrying: false │
528
- │ retryCount: 0 │ │ │ retryCount: <number> │
529
- └─────────────┬──────────────┘ │ └─────────────┬──────────────┘
530
- │ │ │
531
- │ success │ │ retrying
532
- ▼ │ ▼
533
- ┌────────────────────────────┐ │ ┌────────────────────────────┐
534
- Δ│ isPending: false │ │ Δ│ isPending: true [ƒ]│
535
- Δ│ isRevalidating: false │ │ fail Δ│ isRevalidating: true │
536
- │ │ └────────────────────┤ │
537
- │ state: "SUCCESS" │ │ state: "SUCCESS" │
538
- │ isSuccess: true │ │ isSuccess: true │
539
- Δ│ data: <TData> │ │ data: <TData> │
540
- Δ│ dataUpdatedAt: <number> │ │ dataUpdatedAt: <number> │
541
- Δ│ dataStaleAt: <number> │ │ dataStaleAt: <number> │
542
- │ isError: false │ │ isError: false │
543
- │ error: undefined │ │ error: undefined │
544
- │ errorUpdatedAt: undefined │ success │ errorUpdatedAt: undefined │
545
- │ │ ◀─────────────────────────────────────┤ │
546
- │ willRetryAt: undefined │ Δ│ willRetryAt: undefined │
547
- •│ isRetrying: false │ Δ│ isRetrying: true │
548
- •│ retryCount: 0 (reset) │ Δ│ retryCount: <number> (+1) │
549
- └────────────────────────────┘ └────────────────────────────┘
550
- ```
95
+ Read the docs https://floppy-disk.vercel.app
@@ -162,7 +162,7 @@ export type QueryOptions<TData, TVariable extends Record<string, any>, TError =
162
162
  * // ...
163
163
  * }
164
164
  */
165
- export declare const createQuery: <TData, TVariable extends Record<string, any> = never, TError = Error>(queryFn: (variable: TVariable, currentState: QueryState<TData, TError>) => Promise<TData>, options?: QueryOptions<TData, TVariable, TError>) => ((variable?: TVariable) => ((options?: {
165
+ export declare const createQuery: <TData, TVariable extends Record<string, any> = never, TError = Error>(queryFn: (variable: TVariable, currentState: QueryState<TData, TError>, variableHash: string) => Promise<TData>, options?: QueryOptions<TData, TVariable, TError>) => ((variable?: TVariable) => ((options?: {
166
166
  /**
167
167
  * Whether the query should be ravalidated automatically on mount.
168
168
  *
@@ -218,6 +218,7 @@ export declare const createQuery: <TData, TVariable extends Record<string, any>
218
218
  * Internal data, do not mutate!
219
219
  */
220
220
  metadata: {
221
+ variableHash: string;
221
222
  isInvalidated?: boolean;
222
223
  promise?: Promise<QueryState<TData, TError>> | undefined;
223
224
  promiseResolver?: ((value: QueryState<TData, TError> | PromiseLike<QueryState<TData, TError>>) => void) | undefined;
package/esm/react.mjs CHANGED
@@ -208,7 +208,7 @@ const createQuery = (queryFn, options = {}) => {
208
208
  });
209
209
  const internals = /* @__PURE__ */ new WeakMap();
210
210
  const configureInternals = (store, variable, variableHash) => ({
211
- metadata: {},
211
+ metadata: { variableHash },
212
212
  setInitialData: (data, revalidate2 = false) => {
213
213
  const state = store.getState();
214
214
  if (state.state === "INITIAL" && state.data === void 0) {
@@ -292,7 +292,7 @@ const createQuery = (queryFn, options = {}) => {
292
292
  isRetrying: !!metadata.retryResolver,
293
293
  retryCount: metadata.retryResolver ? stateBeforeExecute.retryCount + 1 : 0
294
294
  });
295
- queryFn(variable, stateBeforeExecute).then((data) => {
295
+ queryFn(variable, stateBeforeExecute, metadata.variableHash).then((data) => {
296
296
  var _a;
297
297
  if (data === void 0) {
298
298
  console.error(
package/package.json CHANGED
@@ -2,7 +2,10 @@
2
2
  "name": "floppy-disk",
3
3
  "description": "Lightweight unified state management for sync and async data.",
4
4
  "private": false,
5
- "version": "3.2.1",
5
+ "version": "3.3.0-beta.1",
6
+ "publishConfig": {
7
+ "tag": "beta"
8
+ },
6
9
  "keywords": [
7
10
  "utilities",
8
11
  "store",
@@ -68,6 +71,7 @@
68
71
  }
69
72
  }
70
73
  },
74
+ "packageManager": "pnpm@10.32.1",
71
75
  "peerDependencies": {
72
76
  "@types/react": ">=17.0",
73
77
  "react": ">=17.0"
@@ -80,4 +84,4 @@
80
84
  "optional": true
81
85
  }
82
86
  }
83
- }
87
+ }
@@ -162,7 +162,7 @@ export type QueryOptions<TData, TVariable extends Record<string, any>, TError =
162
162
  * // ...
163
163
  * }
164
164
  */
165
- export declare const createQuery: <TData, TVariable extends Record<string, any> = never, TError = Error>(queryFn: (variable: TVariable, currentState: QueryState<TData, TError>) => Promise<TData>, options?: QueryOptions<TData, TVariable, TError>) => ((variable?: TVariable) => ((options?: {
165
+ export declare const createQuery: <TData, TVariable extends Record<string, any> = never, TError = Error>(queryFn: (variable: TVariable, currentState: QueryState<TData, TError>, variableHash: string) => Promise<TData>, options?: QueryOptions<TData, TVariable, TError>) => ((variable?: TVariable) => ((options?: {
166
166
  /**
167
167
  * Whether the query should be ravalidated automatically on mount.
168
168
  *
@@ -218,6 +218,7 @@ export declare const createQuery: <TData, TVariable extends Record<string, any>
218
218
  * Internal data, do not mutate!
219
219
  */
220
220
  metadata: {
221
+ variableHash: string;
221
222
  isInvalidated?: boolean;
222
223
  promise?: Promise<QueryState<TData, TError>> | undefined;
223
224
  promiseResolver?: ((value: QueryState<TData, TError> | PromiseLike<QueryState<TData, TError>>) => void) | undefined;
package/react.js CHANGED
@@ -210,7 +210,7 @@ const createQuery = (queryFn, options = {}) => {
210
210
  });
211
211
  const internals = /* @__PURE__ */ new WeakMap();
212
212
  const configureInternals = (store, variable, variableHash) => ({
213
- metadata: {},
213
+ metadata: { variableHash },
214
214
  setInitialData: (data, revalidate2 = false) => {
215
215
  const state = store.getState();
216
216
  if (state.state === "INITIAL" && state.data === void 0) {
@@ -294,7 +294,7 @@ const createQuery = (queryFn, options = {}) => {
294
294
  isRetrying: !!metadata.retryResolver,
295
295
  retryCount: metadata.retryResolver ? stateBeforeExecute.retryCount + 1 : 0
296
296
  });
297
- queryFn(variable, stateBeforeExecute).then((data) => {
297
+ queryFn(variable, stateBeforeExecute, metadata.variableHash).then((data) => {
298
298
  var _a;
299
299
  if (data === void 0) {
300
300
  console.error(