floppy-disk 3.3.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,10 @@
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(.ts).\
6
- It keeps what works, removes unnecessary complexity, and unifies everything into a simpler API.\
7
- No relearning—just a better experience.
5
+ Built on the patterns you know. Refined into something simpler.\
6
+ Inspired by [Zustand](https://zustand.docs.pmnd.rs) & [TanStack-Query](https://tanstack.com/query).
8
7
 
9
- _Smaller bundle. Zero dependencies._
8
+ _Fine-grained reactivity, minimal boilerplate, zero dependencies._
10
9
 
11
10
  Demo: https://afiiif.github.io/floppy-disk/
12
11
 
@@ -16,18 +15,25 @@ Demo: https://afiiif.github.io/floppy-disk/
16
15
  npm install floppy-disk
17
16
  ```
18
17
 
19
- ## In short, it is:
18
+ **Read the docs https://floppy-disk.vercel.app**
20
19
 
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
20
+ <br>
29
21
 
30
- **Docs: https://floppy-disk.vercel.app**
22
+ ---
23
+
24
+ **Like Zustand, but has additional capabilities:**
25
+ - No selectors: automatically optimizes re-renders
26
+ - Store events: `onFirstSubscribe`, `onSubscribe`, `onUnsubscribe`, `onLastUnsubscribe`
27
+ - Easier to set initial state on SSR/SSG
28
+ - Smaller bundle
29
+
30
+ **Like TanStack Query, but:**
31
+ - DX is very similar to Zustand → One mental model for sync & async
32
+ - Much smaller bundle than TanStack Query → With nearly the same capabilities
33
+
34
+ ---
35
+
36
+ <br>
31
37
 
32
38
  ## Store (Global State)
33
39
 
@@ -68,7 +68,9 @@ export type QueryState<TData, TError> = {
68
68
  * @remarks
69
69
  * Controls caching, retry behavior, lifecycle, and side effects of an async operation.
70
70
  */
71
- export type QueryOptions<TData, TVariable extends Record<string, any>, TError = Error> = InitStoreOptions<QueryState<TData, TError>> & {
71
+ export type QueryOptions<TData, TVariable extends Record<string, any>, TError = Error> = InitStoreOptions<QueryState<TData, TError>, {
72
+ variableHash: string;
73
+ }> & {
72
74
  /**
73
75
  * Time (in milliseconds) that data is considered fresh.
74
76
  *
@@ -220,7 +222,6 @@ export declare const createQuery: <TData, TVariable extends Record<string, any>
220
222
  * Internal data, do not mutate!
221
223
  */
222
224
  metadata: {
223
- variableHash: string;
224
225
  isInvalidated?: boolean;
225
226
  promise?: Promise<QueryState<TData, TError>> | undefined;
226
227
  promiseResolver?: ((value: QueryState<TData, TError> | PromiseLike<QueryState<TData, TError>>) => void) | undefined;
@@ -32,7 +32,10 @@ import { type InitStoreOptions } from "../vanilla.mjs";
32
32
  *
33
33
  * @see https://floppy-disk.vercel.app/docs/sync/stores
34
34
  */
35
- export declare const createStores: <TState extends Record<string, any>, TKey extends Record<string, any>>(initialState: TState, options?: InitStoreOptions<TState>) => (key?: TKey) => ((options?: {
35
+ export declare const createStores: <TState extends Record<string, any>, TKey extends Record<string, any>>(initialState: TState, options?: InitStoreOptions<TState, {
36
+ key: TKey;
37
+ keyHash: string;
38
+ }>) => (key?: TKey) => ((options?: {
36
39
  /**
37
40
  * Initial state used on first render (and will also update the store state right after that)
38
41
  *
package/esm/react.mjs CHANGED
@@ -94,7 +94,11 @@ const createStores = (initialState, options) => {
94
94
  if (stores.has(keyHash)) {
95
95
  store = stores.get(keyHash);
96
96
  } else {
97
- store = initStore(initialState, options);
97
+ store = initStore(
98
+ initialState,
99
+ options
100
+ // Intentionally using as any: don't want to add generic on `initStore`
101
+ );
98
102
  store.key = key;
99
103
  store.keyHash = keyHash;
100
104
  stores.set(keyHash, store);
@@ -207,8 +211,8 @@ const createQuery = (queryFn, options = {}) => {
207
211
  }
208
212
  });
209
213
  const internals = /* @__PURE__ */ new WeakMap();
210
- const configureInternals = (store, variable, variableHash) => ({
211
- metadata: { variableHash },
214
+ const configureInternals = (store, variable) => ({
215
+ metadata: {},
212
216
  setInitialData: (data, revalidate2 = false) => {
213
217
  const state = store.getState();
214
218
  if (state.state === "INITIAL" && state.data === void 0) {
@@ -263,7 +267,7 @@ const createQuery = (queryFn, options = {}) => {
263
267
  return false;
264
268
  }
265
269
  internals.get(store).reset();
266
- return stores.delete(variableHash);
270
+ return stores.delete(store.variableHash);
267
271
  },
268
272
  optimisticUpdate: (optimisticData) => {
269
273
  const { metadata, revalidate: revalidate2, rollbackOptimisticUpdate } = internals.get(store);
@@ -292,7 +296,7 @@ const createQuery = (queryFn, options = {}) => {
292
296
  isRetrying: !!metadata.retryResolver,
293
297
  retryCount: metadata.retryResolver ? stateBeforeExecute.retryCount + 1 : 0
294
298
  });
295
- queryFn(variable, stateBeforeExecute, metadata.variableHash).then((data) => {
299
+ queryFn(variable, stateBeforeExecute, store.variableHash).then((data) => {
296
300
  var _a;
297
301
  if (data === void 0) {
298
302
  console.error(
@@ -396,9 +400,14 @@ const createQuery = (queryFn, options = {}) => {
396
400
  if (stores.has(variableHash)) {
397
401
  store = stores.get(variableHash);
398
402
  } else {
399
- store = initStore(initialState, configureStoreEvents(variableHash));
403
+ store = initStore(
404
+ initialState,
405
+ configureStoreEvents(variableHash)
406
+ // Intentionally using as any: don't want to add generic on `initStore`
407
+ );
408
+ store.variableHash = variableHash;
400
409
  stores.set(variableHash, store);
401
- internals.set(store, configureInternals(store, variable, variableHash));
410
+ internals.set(store, configureInternals(store, variable));
402
411
  }
403
412
  const useStore = (options2 = {}) => {
404
413
  const {
@@ -46,11 +46,11 @@ export type StoreApi<TState extends Record<string, any>> = {
46
46
  * - Cleanup (e.g. cancel timers, disconnect sockets)
47
47
  * - Resource management (e.g. garbage collection)
48
48
  */
49
- export type InitStoreOptions<TState extends Record<string, any>> = {
50
- onFirstSubscribe?: (state: TState, store: StoreApi<TState>) => void;
51
- onSubscribe?: (state: TState, store: StoreApi<TState>) => void;
52
- onUnsubscribe?: (state: TState, store: StoreApi<TState>) => void;
53
- onLastUnsubscribe?: (state: TState, store: StoreApi<TState>) => void;
49
+ export type InitStoreOptions<TState extends Record<string, any>, TStoreProps extends Record<string, any> = object> = {
50
+ onFirstSubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
51
+ onSubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
52
+ onUnsubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
53
+ onLastUnsubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
54
54
  /**
55
55
  * Called whenever the state changes, without counting as a subscriber.
56
56
  * Acts like a "spy" on state updates.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "floppy-disk",
3
3
  "description": "Lightweight unified state management for sync and async data.",
4
4
  "private": false,
5
- "version": "3.3.0",
5
+ "version": "3.4.0",
6
6
  "keywords": [
7
7
  "utilities",
8
8
  "store",
@@ -68,7 +68,6 @@
68
68
  }
69
69
  }
70
70
  },
71
- "packageManager": "pnpm@10.32.1",
72
71
  "peerDependencies": {
73
72
  "@types/react": ">=17.0",
74
73
  "react": ">=17.0"
@@ -81,4 +80,4 @@
81
80
  "optional": true
82
81
  }
83
82
  }
84
- }
83
+ }
@@ -68,7 +68,9 @@ export type QueryState<TData, TError> = {
68
68
  * @remarks
69
69
  * Controls caching, retry behavior, lifecycle, and side effects of an async operation.
70
70
  */
71
- export type QueryOptions<TData, TVariable extends Record<string, any>, TError = Error> = InitStoreOptions<QueryState<TData, TError>> & {
71
+ export type QueryOptions<TData, TVariable extends Record<string, any>, TError = Error> = InitStoreOptions<QueryState<TData, TError>, {
72
+ variableHash: string;
73
+ }> & {
72
74
  /**
73
75
  * Time (in milliseconds) that data is considered fresh.
74
76
  *
@@ -220,7 +222,6 @@ export declare const createQuery: <TData, TVariable extends Record<string, any>
220
222
  * Internal data, do not mutate!
221
223
  */
222
224
  metadata: {
223
- variableHash: string;
224
225
  isInvalidated?: boolean;
225
226
  promise?: Promise<QueryState<TData, TError>> | undefined;
226
227
  promiseResolver?: ((value: QueryState<TData, TError> | PromiseLike<QueryState<TData, TError>>) => void) | undefined;
@@ -32,7 +32,10 @@ import { type InitStoreOptions } from "../vanilla.ts";
32
32
  *
33
33
  * @see https://floppy-disk.vercel.app/docs/sync/stores
34
34
  */
35
- export declare const createStores: <TState extends Record<string, any>, TKey extends Record<string, any>>(initialState: TState, options?: InitStoreOptions<TState>) => (key?: TKey) => ((options?: {
35
+ export declare const createStores: <TState extends Record<string, any>, TKey extends Record<string, any>>(initialState: TState, options?: InitStoreOptions<TState, {
36
+ key: TKey;
37
+ keyHash: string;
38
+ }>) => (key?: TKey) => ((options?: {
36
39
  /**
37
40
  * Initial state used on first render (and will also update the store state right after that)
38
41
  *
package/react.js CHANGED
@@ -96,7 +96,11 @@ const createStores = (initialState, options) => {
96
96
  if (stores.has(keyHash)) {
97
97
  store = stores.get(keyHash);
98
98
  } else {
99
- store = vanilla.initStore(initialState, options);
99
+ store = vanilla.initStore(
100
+ initialState,
101
+ options
102
+ // Intentionally using as any: don't want to add generic on `initStore`
103
+ );
100
104
  store.key = key;
101
105
  store.keyHash = keyHash;
102
106
  stores.set(keyHash, store);
@@ -209,8 +213,8 @@ const createQuery = (queryFn, options = {}) => {
209
213
  }
210
214
  });
211
215
  const internals = /* @__PURE__ */ new WeakMap();
212
- const configureInternals = (store, variable, variableHash) => ({
213
- metadata: { variableHash },
216
+ const configureInternals = (store, variable) => ({
217
+ metadata: {},
214
218
  setInitialData: (data, revalidate2 = false) => {
215
219
  const state = store.getState();
216
220
  if (state.state === "INITIAL" && state.data === void 0) {
@@ -265,7 +269,7 @@ const createQuery = (queryFn, options = {}) => {
265
269
  return false;
266
270
  }
267
271
  internals.get(store).reset();
268
- return stores.delete(variableHash);
272
+ return stores.delete(store.variableHash);
269
273
  },
270
274
  optimisticUpdate: (optimisticData) => {
271
275
  const { metadata, revalidate: revalidate2, rollbackOptimisticUpdate } = internals.get(store);
@@ -294,7 +298,7 @@ const createQuery = (queryFn, options = {}) => {
294
298
  isRetrying: !!metadata.retryResolver,
295
299
  retryCount: metadata.retryResolver ? stateBeforeExecute.retryCount + 1 : 0
296
300
  });
297
- queryFn(variable, stateBeforeExecute, metadata.variableHash).then((data) => {
301
+ queryFn(variable, stateBeforeExecute, store.variableHash).then((data) => {
298
302
  var _a;
299
303
  if (data === void 0) {
300
304
  console.error(
@@ -398,9 +402,14 @@ const createQuery = (queryFn, options = {}) => {
398
402
  if (stores.has(variableHash)) {
399
403
  store = stores.get(variableHash);
400
404
  } else {
401
- store = vanilla.initStore(initialState, configureStoreEvents(variableHash));
405
+ store = vanilla.initStore(
406
+ initialState,
407
+ configureStoreEvents(variableHash)
408
+ // Intentionally using as any: don't want to add generic on `initStore`
409
+ );
410
+ store.variableHash = variableHash;
402
411
  stores.set(variableHash, store);
403
- internals.set(store, configureInternals(store, variable, variableHash));
412
+ internals.set(store, configureInternals(store, variable));
404
413
  }
405
414
  const useStore = (options2 = {}) => {
406
415
  const {
@@ -46,11 +46,11 @@ export type StoreApi<TState extends Record<string, any>> = {
46
46
  * - Cleanup (e.g. cancel timers, disconnect sockets)
47
47
  * - Resource management (e.g. garbage collection)
48
48
  */
49
- export type InitStoreOptions<TState extends Record<string, any>> = {
50
- onFirstSubscribe?: (state: TState, store: StoreApi<TState>) => void;
51
- onSubscribe?: (state: TState, store: StoreApi<TState>) => void;
52
- onUnsubscribe?: (state: TState, store: StoreApi<TState>) => void;
53
- onLastUnsubscribe?: (state: TState, store: StoreApi<TState>) => void;
49
+ export type InitStoreOptions<TState extends Record<string, any>, TStoreProps extends Record<string, any> = object> = {
50
+ onFirstSubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
51
+ onSubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
52
+ onUnsubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
53
+ onLastUnsubscribe?: (state: TState, store: StoreApi<TState> & TStoreProps) => void;
54
54
  /**
55
55
  * Called whenever the state changes, without counting as a subscriber.
56
56
  * Acts like a "spy" on state updates.