floppy-disk 3.7.3 → 3.9.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/esm/index.d.mts CHANGED
@@ -1 +1 @@
1
- export * from "./vanilla.mjs";
1
+ export * from "floppy-disk/vanilla";
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.mjs";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  /**
3
3
  * Represents the state of a mutation.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.mjs";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  import type { StoreKey } from "./create-stores.mjs";
3
3
  /**
4
4
  * Represents the state of a query.
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions } from "../vanilla.mjs";
1
+ import { type InitStoreOptions } from "floppy-disk/vanilla";
2
2
  /**
3
3
  * Creates a single store with a bound React hook.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type StoreApi } from "../vanilla.mjs";
1
+ import { type InitStoreOptions, type StoreApi } from "floppy-disk/vanilla";
2
2
  type GoodInputForHash = string | number | boolean | null | Date;
3
3
  export type StoreKey = GoodInputForHash | {
4
4
  [key: string | number]: StoreKey | StoreKey[];
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.mjs";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  import type { StoreKey } from "./create-stores.mjs";
3
3
  type StreamDataState<TData, TError> = {
4
4
  state: "INITIAL";
@@ -230,65 +230,23 @@ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions
230
230
  * @returns A function to retrieve or create a stream instance by variable
231
231
  *
232
232
  * @remarks
233
- * This utility is designed for **long-lived, push-based async sources**, such as:
234
- * - WebSocket
235
- * - Server-Sent Events (SSE)
236
- * - Firebase / realtime databases
237
- *
238
- * ---
239
- *
240
- * ## Key concepts
241
- *
242
- * ### 1. Connection lifecycle (managed automatically)
243
- *
244
- * - Connection is established when needed (e.g. first subscriber)
245
- * - Connection may be disconnected based on triggers:
246
- * - no subscribers
247
- * - tab hidden
248
- * - offline
249
- * - Reconnection is controlled via `reconnectOn`
250
- *
251
- * ---
252
- *
253
- * ### 2. Data flow (push-based)
254
- *
255
- * The `connect` function receives an `emit` API:
256
- *
257
- * - `emit.connected()` → mark connection as established
258
- * - `emit.data(fn)` → update data using reducer
259
- * - `emit.error(err)` → report error
260
- *
261
- * Data updates are **incremental** and controlled by the stream source.
262
- *
263
- * ---
264
- *
265
- * ### 3. Store-per-variable
266
- *
267
- * - Each unique `variable` creates a separate stream instance
268
- * - Variables are deterministically hashed for stable identity
269
- * - Each instance manages its own:
270
- * - connection
271
- * - state
272
- * - subscribers
273
- *
274
- * ---
275
- *
276
- * ### 4. React integration (Proxy-based)
277
- *
278
- * - The returned hook exposes the full state as a Proxy
279
- * - Components automatically subscribe to accessed properties
280
- * - No selector or memoization is required
233
+ * - Designed for **long-lived, push-based async sources**, such as: WebSocket, SSE, realtime databases
234
+ * - A stream consists of two independent concerns:
235
+ * 1. **Connection state** lifecycle of the underlying connection
236
+ * 2. **Data state** lifecycle of emitted data
237
+ * - Connection lifecycle:
238
+ * - Connection is established when needed (e.g. first subscriber)
239
+ * - Connection may be disconnected based on triggers: no subscribers, tab hidden, offline
240
+ * - Streams are **keyed by variable** (via deterministic hashing).
241
+ * - Each unique variable maps to its own stream instance.
281
242
  *
282
243
  * ---
283
244
  *
284
245
  * ## Execution model
285
246
  *
286
- * - Streams are **lazy**:
287
- * - No connection until there is a subscriber
288
- * - Streams are **shared**:
289
- * - Multiple subscribers reuse the same connection
290
- * - Streams are **stateful**:
291
- * - Data persists across reconnects (unless reset or GC)
247
+ * - Streams are **lazy**: No connection until there is a subscriber
248
+ * - Streams are **shared**: Multiple subscribers reuse the same connection
249
+ * - Streams are **stateful**: Data persists across reconnects (unless reset or GC)
292
250
  *
293
251
  * ---
294
252
  *
@@ -316,7 +274,7 @@ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions
316
274
  * return <div>{state.data?.length}</div>;
317
275
  * }
318
276
  */
319
- export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
277
+ export declare const createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
320
278
  connected: () => void;
321
279
  data: (reducer: (data: TData | undefined) => TData) => void;
322
280
  error: (error: TError) => void;
@@ -1,4 +1,4 @@
1
- import { type StoreApi } from "../vanilla.mjs";
1
+ import { type StoreApi } from "floppy-disk/vanilla";
2
2
  type Path = Array<string | number | symbol>;
3
3
  export declare const getValueByPath: (obj: any, path: Path) => any;
4
4
  export declare const isPrefixPath: (candidatePrefix: Path, targetPath: Path) => boolean;
package/esm/react.mjs CHANGED
@@ -52,7 +52,7 @@ const useStoreStateProxy = (storeState) => {
52
52
  const NO_INITIAL_VALUE = {};
53
53
  const useStoreStateWithInitializer = (store, initialState = NO_INITIAL_VALUE) => {
54
54
  const initiatedAt = useRef(new WeakMap([[store, 0]]));
55
- useIsomorphicLayoutEffect(() => {
55
+ useEffect(() => {
56
56
  if (initialState === NO_INITIAL_VALUE || initiatedAt.current.get(store)) return;
57
57
  store.setState(initialState);
58
58
  initiatedAt.current.set(store, Date.now());
@@ -65,7 +65,7 @@ const useStoreState = (store, options = {}) => {
65
65
  const [state] = useStoreStateWithInitializer(store, options.initialState);
66
66
  const [trackedState, usedPathsRef] = useStoreStateProxy(state);
67
67
  const [, reRender] = useState({});
68
- useIsomorphicLayoutEffect(() => {
68
+ useEffect(() => {
69
69
  return store.subscribe((nextState, prevState, changedKeys) => {
70
70
  const paths = compressPaths(usedPathsRef.current);
71
71
  for (const path of paths) {
@@ -445,7 +445,7 @@ const createQuery = (queryFn, options = {}) => {
445
445
  ) : storeStateToBeUsed
446
446
  );
447
447
  const [, reRender] = useState({});
448
- useIsomorphicLayoutEffect(() => {
448
+ useEffect(() => {
449
449
  return store.subscribe((nextState, prevState2, changedKeys) => {
450
450
  if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
451
451
  return;
@@ -460,7 +460,7 @@ const createQuery = (queryFn, options = {}) => {
460
460
  }
461
461
  });
462
462
  }, [store]);
463
- useIsomorphicLayoutEffect(() => {
463
+ useEffect(() => {
464
464
  if (revalidateOnMount !== false) {
465
465
  if (!initialDataIsStale) {
466
466
  const dataInitiatedAt = initialDataInitiatedAt.current.get(store);
@@ -769,7 +769,7 @@ const INITIAL_STATE = {
769
769
  error: void 0,
770
770
  errorUpdatedAt: void 0
771
771
  };
772
- const experimental_createStream = (connect, disconnect, options = {}) => {
772
+ const createStream = (connect, disconnect, options = {}) => {
773
773
  const {
774
774
  disconnectOn = () => 5e3,
775
775
  // 5 seconds after any `DisconnectTrigger`
@@ -1002,4 +1002,4 @@ const offlineListeners = /* @__PURE__ */ new Set();
1002
1002
  const onWindowOffline = () => [...offlineListeners].forEach((fn) => fn());
1003
1003
  let listenersAdded = false;
1004
1004
 
1005
- export { createMutation, createQuery, createStore, createStores, experimental_createStream, useIsomorphicLayoutEffect, useMutation, useStoreState };
1005
+ export { createMutation, createQuery, createStore, createStores, createStream, useIsomorphicLayoutEffect, useMutation, useStoreState };
package/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * from "./vanilla.ts";
1
+ export * from "floppy-disk/vanilla";
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.7.3",
5
+ "version": "3.9.0-beta.1",
6
+ "publishConfig": {
7
+ "tag": "beta"
8
+ },
6
9
  "keywords": [
7
10
  "utilities",
8
11
  "store",
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.ts";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  /**
3
3
  * Represents the state of a mutation.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.ts";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  import type { StoreKey } from "./create-stores.ts";
3
3
  /**
4
4
  * Represents the state of a query.
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions } from "../vanilla.ts";
1
+ import { type InitStoreOptions } from "floppy-disk/vanilla";
2
2
  /**
3
3
  * Creates a single store with a bound React hook.
4
4
  *
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type StoreApi } from "../vanilla.ts";
1
+ import { type InitStoreOptions, type StoreApi } from "floppy-disk/vanilla";
2
2
  type GoodInputForHash = string | number | boolean | null | Date;
3
3
  export type StoreKey = GoodInputForHash | {
4
4
  [key: string | number]: StoreKey | StoreKey[];
@@ -1,4 +1,4 @@
1
- import { type InitStoreOptions, type SetStateInput } from "../vanilla.ts";
1
+ import { type InitStoreOptions, type SetStateInput } from "floppy-disk/vanilla";
2
2
  import type { StoreKey } from "./create-stores.ts";
3
3
  type StreamDataState<TData, TError> = {
4
4
  state: "INITIAL";
@@ -230,65 +230,23 @@ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions
230
230
  * @returns A function to retrieve or create a stream instance by variable
231
231
  *
232
232
  * @remarks
233
- * This utility is designed for **long-lived, push-based async sources**, such as:
234
- * - WebSocket
235
- * - Server-Sent Events (SSE)
236
- * - Firebase / realtime databases
237
- *
238
- * ---
239
- *
240
- * ## Key concepts
241
- *
242
- * ### 1. Connection lifecycle (managed automatically)
243
- *
244
- * - Connection is established when needed (e.g. first subscriber)
245
- * - Connection may be disconnected based on triggers:
246
- * - no subscribers
247
- * - tab hidden
248
- * - offline
249
- * - Reconnection is controlled via `reconnectOn`
250
- *
251
- * ---
252
- *
253
- * ### 2. Data flow (push-based)
254
- *
255
- * The `connect` function receives an `emit` API:
256
- *
257
- * - `emit.connected()` → mark connection as established
258
- * - `emit.data(fn)` → update data using reducer
259
- * - `emit.error(err)` → report error
260
- *
261
- * Data updates are **incremental** and controlled by the stream source.
262
- *
263
- * ---
264
- *
265
- * ### 3. Store-per-variable
266
- *
267
- * - Each unique `variable` creates a separate stream instance
268
- * - Variables are deterministically hashed for stable identity
269
- * - Each instance manages its own:
270
- * - connection
271
- * - state
272
- * - subscribers
273
- *
274
- * ---
275
- *
276
- * ### 4. React integration (Proxy-based)
277
- *
278
- * - The returned hook exposes the full state as a Proxy
279
- * - Components automatically subscribe to accessed properties
280
- * - No selector or memoization is required
233
+ * - Designed for **long-lived, push-based async sources**, such as: WebSocket, SSE, realtime databases
234
+ * - A stream consists of two independent concerns:
235
+ * 1. **Connection state** lifecycle of the underlying connection
236
+ * 2. **Data state** lifecycle of emitted data
237
+ * - Connection lifecycle:
238
+ * - Connection is established when needed (e.g. first subscriber)
239
+ * - Connection may be disconnected based on triggers: no subscribers, tab hidden, offline
240
+ * - Streams are **keyed by variable** (via deterministic hashing).
241
+ * - Each unique variable maps to its own stream instance.
281
242
  *
282
243
  * ---
283
244
  *
284
245
  * ## Execution model
285
246
  *
286
- * - Streams are **lazy**:
287
- * - No connection until there is a subscriber
288
- * - Streams are **shared**:
289
- * - Multiple subscribers reuse the same connection
290
- * - Streams are **stateful**:
291
- * - Data persists across reconnects (unless reset or GC)
247
+ * - Streams are **lazy**: No connection until there is a subscriber
248
+ * - Streams are **shared**: Multiple subscribers reuse the same connection
249
+ * - Streams are **stateful**: Data persists across reconnects (unless reset or GC)
292
250
  *
293
251
  * ---
294
252
  *
@@ -316,7 +274,7 @@ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions
316
274
  * return <div>{state.data?.length}</div>;
317
275
  * }
318
276
  */
319
- export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
277
+ export declare const createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
320
278
  connected: () => void;
321
279
  data: (reducer: (data: TData | undefined) => TData) => void;
322
280
  error: (error: TError) => void;
@@ -1,4 +1,4 @@
1
- import { type StoreApi } from "../vanilla.ts";
1
+ import { type StoreApi } from "floppy-disk/vanilla";
2
2
  type Path = Array<string | number | symbol>;
3
3
  export declare const getValueByPath: (obj: any, path: Path) => any;
4
4
  export declare const isPrefixPath: (candidatePrefix: Path, targetPath: Path) => boolean;
package/react.js CHANGED
@@ -54,7 +54,7 @@ const useStoreStateProxy = (storeState) => {
54
54
  const NO_INITIAL_VALUE = {};
55
55
  const useStoreStateWithInitializer = (store, initialState = NO_INITIAL_VALUE) => {
56
56
  const initiatedAt = react.useRef(new WeakMap([[store, 0]]));
57
- useIsomorphicLayoutEffect(() => {
57
+ react.useEffect(() => {
58
58
  if (initialState === NO_INITIAL_VALUE || initiatedAt.current.get(store)) return;
59
59
  store.setState(initialState);
60
60
  initiatedAt.current.set(store, Date.now());
@@ -67,7 +67,7 @@ const useStoreState = (store, options = {}) => {
67
67
  const [state] = useStoreStateWithInitializer(store, options.initialState);
68
68
  const [trackedState, usedPathsRef] = useStoreStateProxy(state);
69
69
  const [, reRender] = react.useState({});
70
- useIsomorphicLayoutEffect(() => {
70
+ react.useEffect(() => {
71
71
  return store.subscribe((nextState, prevState, changedKeys) => {
72
72
  const paths = compressPaths(usedPathsRef.current);
73
73
  for (const path of paths) {
@@ -447,7 +447,7 @@ const createQuery = (queryFn, options = {}) => {
447
447
  ) : storeStateToBeUsed
448
448
  );
449
449
  const [, reRender] = react.useState({});
450
- useIsomorphicLayoutEffect(() => {
450
+ react.useEffect(() => {
451
451
  return store.subscribe((nextState, prevState2, changedKeys) => {
452
452
  if (prevState2.state === "INITIAL" && !prevState2.isPending && nextState.isPending && !nextState.isRetrying) {
453
453
  return;
@@ -462,7 +462,7 @@ const createQuery = (queryFn, options = {}) => {
462
462
  }
463
463
  });
464
464
  }, [store]);
465
- useIsomorphicLayoutEffect(() => {
465
+ react.useEffect(() => {
466
466
  if (revalidateOnMount !== false) {
467
467
  if (!initialDataIsStale) {
468
468
  const dataInitiatedAt = initialDataInitiatedAt.current.get(store);
@@ -771,7 +771,7 @@ const INITIAL_STATE = {
771
771
  error: void 0,
772
772
  errorUpdatedAt: void 0
773
773
  };
774
- const experimental_createStream = (connect, disconnect, options = {}) => {
774
+ const createStream = (connect, disconnect, options = {}) => {
775
775
  const {
776
776
  disconnectOn = () => 5e3,
777
777
  // 5 seconds after any `DisconnectTrigger`
@@ -1008,7 +1008,7 @@ exports.createMutation = createMutation;
1008
1008
  exports.createQuery = createQuery;
1009
1009
  exports.createStore = createStore;
1010
1010
  exports.createStores = createStores;
1011
- exports.experimental_createStream = experimental_createStream;
1011
+ exports.createStream = createStream;
1012
1012
  exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
1013
1013
  exports.useMutation = useMutation;
1014
1014
  exports.useStoreState = useStoreState;