floppy-disk 3.7.0-beta.1 → 3.7.0-beta.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.
@@ -56,7 +56,7 @@ export type QueryState<TData, TError> = {
56
56
  } | {
57
57
  state: "SUCCESS_BUT_REVALIDATION_ERROR";
58
58
  isSuccess: true;
59
- isError: false;
59
+ isError: true;
60
60
  data: TData;
61
61
  dataUpdatedAt: number;
62
62
  dataStaleAt: undefined | number;
@@ -0,0 +1,88 @@
1
+ import { type InitStoreOptions, type StoreApi } from "../vanilla.mjs";
2
+ import type { StoreKey } from "./create-stores.mjs";
3
+ type StreamDataState<TData, TError> = {
4
+ state: "INITIAL";
5
+ isSuccess: false;
6
+ isError: false;
7
+ data: undefined;
8
+ dataUpdatedAt: undefined;
9
+ error: undefined;
10
+ errorUpdatedAt: undefined;
11
+ } | {
12
+ state: "SUCCESS";
13
+ isSuccess: true;
14
+ isError: false;
15
+ data: TData;
16
+ dataUpdatedAt: number;
17
+ error: undefined;
18
+ errorUpdatedAt: undefined;
19
+ } | {
20
+ state: "ERROR";
21
+ isSuccess: false;
22
+ isError: true;
23
+ data: undefined;
24
+ dataUpdatedAt: undefined;
25
+ error: TError;
26
+ errorUpdatedAt: number;
27
+ } | {
28
+ state: "SUCCESS_BUT_THEN_ERROR";
29
+ isSuccess: true;
30
+ isError: true;
31
+ data: TData;
32
+ dataUpdatedAt: number;
33
+ error: TError;
34
+ errorUpdatedAt: number;
35
+ };
36
+ export type StreamState<TData, TError> = ({
37
+ connectionState: "INITIAL";
38
+ connectingAt: undefined;
39
+ connectedAt: undefined;
40
+ disconnectedAt: undefined;
41
+ } & Extract<StreamDataState<TData, TError>, {
42
+ state: "INITIAL";
43
+ }>) | ({
44
+ connectionState: "CONNECTING";
45
+ connectingAt: number;
46
+ connectedAt: number | undefined;
47
+ disconnectedAt: number | undefined;
48
+ } & StreamDataState<TData, TError>) | ({
49
+ connectionState: "CONNECTED";
50
+ connectingAt: number;
51
+ connectedAt: number;
52
+ disconnectedAt: undefined;
53
+ } & StreamDataState<TData, TError>) | ({
54
+ connectionState: "DISCONNECTED";
55
+ connectingAt: number;
56
+ connectedAt: number | undefined;
57
+ disconnectedAt: number;
58
+ } & StreamDataState<TData, TError>);
59
+ type DisconnectTrigger = "last-unsubscribe" | "document-hidden" | "offline";
60
+ type ReconnectTrigger = "first-subscribe" | "document-visible" | "online";
61
+ type AdditionalStoreApi<TConnection> = {
62
+ variableHash: string;
63
+ connection: {
64
+ get: () => Readonly<TConnection> | undefined;
65
+ reconnect: () => void;
66
+ disconnect: () => void;
67
+ };
68
+ data: {
69
+ reset: () => void;
70
+ };
71
+ };
72
+ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions<StreamState<TData, TError>, AdditionalStoreApi<TConnection>> & {
73
+ connection?: {
74
+ disconnectOn?: (trigger: DisconnectTrigger, state: StreamState<TData, TError>) => false | number;
75
+ reconnectOn?: (trigger: ReconnectTrigger, state: StreamState<TData, TError>) => boolean;
76
+ };
77
+ data?: {
78
+ gcTime?: number;
79
+ };
80
+ };
81
+ export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
82
+ connected: () => void;
83
+ data: (reducer: (data: TData | undefined) => TData) => void;
84
+ error: (error: TError) => void;
85
+ }) => TConnection, disconnect: (connection: TConnection) => void, options?: StreamOptions<TConnection, TData, TError>) => (variable?: TVariable) => ((options?: {
86
+ initialData?: TData;
87
+ }) => StreamState<TData, TError>) & StoreApi<StreamState<TData, TError>> & AdditionalStoreApi<TConnection>;
88
+ export {};
package/esm/react.d.mts CHANGED
@@ -5,3 +5,4 @@ export * from "./react/create-stores.mjs";
5
5
  export * from "./react/create-query.mjs";
6
6
  export { createMutation, type MutationOptions, type MutationState, } from "./react/create-mutation.mjs";
7
7
  export * from "./react/use-mutation.mjs";
8
+ export * from "./react/create-stream.mjs";
package/esm/react.mjs CHANGED
@@ -119,7 +119,7 @@ const createStores = (initialState, options) => {
119
119
  return getStore;
120
120
  };
121
121
 
122
- const INITIAL_STATE$1 = {
122
+ const INITIAL_STATE$2 = {
123
123
  isPending: false,
124
124
  isRevalidating: false,
125
125
  willRetryAt: void 0,
@@ -147,7 +147,7 @@ const createQuery = (queryFn, options = {}) => {
147
147
  onSettled = noop,
148
148
  shouldRetry: shouldRetryFn = (_, s) => s.retryCount === 0 ? [true, 1500] : [false]
149
149
  } = options;
150
- const initialState = { ...INITIAL_STATE$1 };
150
+ const initialState = { ...INITIAL_STATE$2 };
151
151
  const stores = /* @__PURE__ */ new Map();
152
152
  const configureStoreEvents = (variableHash) => ({
153
153
  ...options,
@@ -165,9 +165,9 @@ const createQuery = (queryFn, options = {}) => {
165
165
  }
166
166
  }
167
167
  if (revalidateOnReconnect) {
168
- onlineListeners.add(revalidate2);
168
+ onlineListeners$1.add(revalidate2);
169
169
  if (!onlineListenersAdded) {
170
- window.addEventListener("online", onWindowOnline);
170
+ window.addEventListener("online", onWindowOnline$1);
171
171
  onlineListenersAdded = true;
172
172
  }
173
173
  }
@@ -199,9 +199,9 @@ const createQuery = (queryFn, options = {}) => {
199
199
  }
200
200
  }
201
201
  if (revalidateOnReconnect) {
202
- onlineListeners.delete(revalidate2);
203
- if (onlineListeners.size === 0) {
204
- window.removeEventListener("online", onWindowOnline);
202
+ onlineListeners$1.delete(revalidate2);
203
+ if (onlineListeners$1.size === 0) {
204
+ window.removeEventListener("online", onWindowOnline$1);
205
205
  onlineListenersAdded = false;
206
206
  }
207
207
  }
@@ -352,15 +352,10 @@ const createQuery = (queryFn, options = {}) => {
352
352
  isRevalidating: false,
353
353
  isRetrying: false,
354
354
  retryCount: 0,
355
+ state: store.getState().state === "SUCCESS" ? "SUCCESS_BUT_REVALIDATION_ERROR" : "ERROR",
356
+ isError: true,
355
357
  error,
356
- errorUpdatedAt: Date.now(),
357
- ...store.getState().data ? {
358
- state: "SUCCESS_BUT_REVALIDATION_ERROR",
359
- isError: false
360
- } : {
361
- state: "ERROR",
362
- isError: true
363
- }
358
+ errorUpdatedAt: Date.now()
364
359
  });
365
360
  const state = store.getState();
366
361
  resolve(state);
@@ -530,10 +525,10 @@ let focusListenersAdded = false;
530
525
  const focusListeners = /* @__PURE__ */ new Set();
531
526
  const onWindowFocus = () => [...focusListeners].forEach((fn) => fn());
532
527
  let onlineListenersAdded = false;
533
- const onlineListeners = /* @__PURE__ */ new Set();
534
- const onWindowOnline = () => [...onlineListeners].forEach((fn) => fn());
528
+ const onlineListeners$1 = /* @__PURE__ */ new Set();
529
+ const onWindowOnline$1 = () => [...onlineListeners$1].forEach((fn) => fn());
535
530
 
536
- const INITIAL_STATE = {
531
+ const INITIAL_STATE$1 = {
537
532
  state: "INITIAL",
538
533
  isPending: false,
539
534
  isSuccess: false,
@@ -546,7 +541,7 @@ const INITIAL_STATE = {
546
541
  };
547
542
  const createMutation = (mutationFn, options = {}) => {
548
543
  const { onSuccess = noop, onError, onSettled = noop } = options;
549
- const initialState = { ...INITIAL_STATE };
544
+ const initialState = { ...INITIAL_STATE$1 };
550
545
  let ongoingPromise;
551
546
  const resolveFns = /* @__PURE__ */ new Set([]);
552
547
  const store = initStore(initialState, options);
@@ -664,7 +659,7 @@ const useMutation = (mutationFn, options = {}) => {
664
659
  callbackRef.current.onSuccess = onSuccess;
665
660
  callbackRef.current.onError = onError;
666
661
  callbackRef.current.onSettled = onSettled;
667
- const stateRef = useRef({ ...INITIAL_STATE });
662
+ const stateRef = useRef({ ...INITIAL_STATE$1 });
668
663
  const [, reRender] = useState({});
669
664
  const refs = useRef({
670
665
  mutationFn,
@@ -747,7 +742,7 @@ const useMutation = (mutationFn, options = {}) => {
747
742
  "Mutation state was reset while a request is still pending. The request will continue, but its result may override the reset state."
748
743
  );
749
744
  }
750
- stateRef.current = { ...INITIAL_STATE };
745
+ stateRef.current = { ...INITIAL_STATE$1 };
751
746
  reRender({});
752
747
  }, []);
753
748
  const r = [
@@ -761,4 +756,223 @@ const useMutation = (mutationFn, options = {}) => {
761
756
  return r;
762
757
  };
763
758
 
764
- export { createMutation, createQuery, createStore, createStores, useIsomorphicLayoutEffect, useMutation, useStoreState };
759
+ const INITIAL_STATE = {
760
+ connectionState: "INITIAL",
761
+ connectingAt: void 0,
762
+ connectedAt: void 0,
763
+ disconnectedAt: void 0,
764
+ state: "INITIAL",
765
+ isSuccess: false,
766
+ isError: false,
767
+ data: void 0,
768
+ dataUpdatedAt: void 0,
769
+ error: void 0,
770
+ errorUpdatedAt: void 0
771
+ };
772
+ const experimental_createStream = (connect, disconnect, options = {}) => {
773
+ const {
774
+ disconnectOn = () => 5e3,
775
+ // 5 seconds after any `DisconnectTrigger`
776
+ reconnectOn = () => false
777
+ // no need reconnect if still connected
778
+ } = options.connection || {};
779
+ const {
780
+ gcTime = 5 * 60 * 1e3
781
+ // 5 minutes
782
+ } = options.data || {};
783
+ const initialState = { ...INITIAL_STATE };
784
+ const stores = /* @__PURE__ */ new Map();
785
+ const connections = /* @__PURE__ */ new WeakMap();
786
+ const disconnectFns = /* @__PURE__ */ new WeakMap();
787
+ const disconnectTimeoutIds = /* @__PURE__ */ new WeakMap();
788
+ const clearDataTimeoutIds = /* @__PURE__ */ new WeakMap();
789
+ const configureStoreEvents = () => ({
790
+ ...options,
791
+ onFirstSubscribe: (state, store) => {
792
+ var _a, _b;
793
+ clearTimeout((_a = disconnectTimeoutIds.get(store)) == null ? void 0 : _a["last-unsubscribe"]);
794
+ (_b = options.onFirstSubscribe) == null ? void 0 : _b.call(options, state, store);
795
+ triggerReconnect(store, "first-subscribe");
796
+ if (isClient) {
797
+ visibilityChangeListeners.add(triggers.visibilityChange);
798
+ onlineListeners.add(triggers.online);
799
+ offlineListeners.add(triggers.offline);
800
+ if (!listenersAdded) {
801
+ document.addEventListener("visibilitychange", onVisibilityChange);
802
+ window.addEventListener("online", onWindowOnline);
803
+ window.addEventListener("offline", onWindowOffline);
804
+ listenersAdded = true;
805
+ }
806
+ }
807
+ },
808
+ onLastUnsubscribe: (state, store) => {
809
+ var _a;
810
+ (_a = options.onLastUnsubscribe) == null ? void 0 : _a.call(options, state, store);
811
+ triggerDisconnect(store, "last-unsubscribe");
812
+ if (isClient) {
813
+ visibilityChangeListeners.delete(triggers.visibilityChange);
814
+ onlineListeners.delete(triggers.online);
815
+ offlineListeners.delete(triggers.offline);
816
+ if (visibilityChangeListeners.size === 0) {
817
+ document.removeEventListener("visibilitychange", onVisibilityChange);
818
+ window.removeEventListener("online", onWindowOnline);
819
+ window.removeEventListener("offline", onWindowOffline);
820
+ listenersAdded = false;
821
+ }
822
+ }
823
+ }
824
+ });
825
+ const getStore = (variable = {}) => {
826
+ const variableHash = getHash(variable);
827
+ let store;
828
+ if (stores.has(variableHash)) {
829
+ store = stores.get(variableHash);
830
+ } else {
831
+ store = initStore(
832
+ initialState,
833
+ configureStoreEvents()
834
+ // Intentionally using as any: don't want to add generic on `initStore`
835
+ );
836
+ store.variableHash = variableHash;
837
+ stores.set(variableHash, store);
838
+ store.connection = {};
839
+ store.connection.get = () => connections.get(store);
840
+ store.connection.reconnect = () => {
841
+ var _a;
842
+ const { connectionState } = store.getState();
843
+ if (connectionState === "CONNECTING") return;
844
+ (_a = disconnectFns.get(store)) == null ? void 0 : _a();
845
+ store.setState({
846
+ connectionState: "CONNECTING",
847
+ connectingAt: Date.now()
848
+ });
849
+ const connection = connect(variable, {
850
+ connected: () => {
851
+ store.setState({
852
+ connectionState: "CONNECTED",
853
+ connectedAt: Date.now(),
854
+ disconnectedAt: void 0
855
+ });
856
+ },
857
+ data: (reducer) => {
858
+ store.setState((prev) => {
859
+ var _a2;
860
+ return {
861
+ connectionState: "CONNECTED",
862
+ connectedAt: (_a2 = prev.connectedAt) != null ? _a2 : Date.now(),
863
+ state: "SUCCESS",
864
+ isSuccess: true,
865
+ isError: false,
866
+ data: reducer(prev.data),
867
+ dataUpdatedAt: Date.now(),
868
+ error: void 0,
869
+ errorUpdatedAt: void 0
870
+ };
871
+ });
872
+ },
873
+ error: (error) => {
874
+ store.setState((prev) => ({
875
+ state: prev.state === "SUCCESS" ? "SUCCESS_BUT_THEN_ERROR" : "ERROR",
876
+ isError: true,
877
+ error,
878
+ errorUpdatedAt: Date.now()
879
+ }));
880
+ }
881
+ });
882
+ connections.set(store, connection);
883
+ disconnectFns.set(store, () => disconnect(connection));
884
+ };
885
+ store.connection.disconnect = () => {
886
+ var _a;
887
+ if (store.getSubscriberCount()) {
888
+ console.warn("Stream disconnected while there is subscriber");
889
+ }
890
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
891
+ if (disconnectTimeoutIds_) {
892
+ clearTimeout(disconnectTimeoutIds_["last-unsubscribe"]);
893
+ clearTimeout(disconnectTimeoutIds_["document-hidden"]);
894
+ clearTimeout(disconnectTimeoutIds_.offline);
895
+ }
896
+ (_a = disconnectFns.get(store)) == null ? void 0 : _a();
897
+ if (store.getState().connectionState !== "INITIAL") {
898
+ store.setState({
899
+ connectionState: "DISCONNECTED",
900
+ disconnectedAt: Date.now()
901
+ });
902
+ }
903
+ connections.delete(store);
904
+ disconnectFns.delete(store);
905
+ clearDataTimeoutIds.set(
906
+ store,
907
+ setTimeout(() => {
908
+ store.data.reset();
909
+ }, gcTime)
910
+ );
911
+ };
912
+ store.data = {};
913
+ store.data.reset = () => {
914
+ store.setState({
915
+ state: "INITIAL",
916
+ isSuccess: false,
917
+ isError: false,
918
+ data: void 0,
919
+ dataUpdatedAt: void 0,
920
+ error: void 0,
921
+ errorUpdatedAt: void 0
922
+ });
923
+ };
924
+ }
925
+ const useStore = (options2) => useStoreState(store, {
926
+ initialState: { data: options2 == null ? void 0 : options2.initialData }
927
+ });
928
+ return Object.assign(useStore, store);
929
+ };
930
+ const triggerReconnect = (store, trigger) => {
931
+ clearTimeout(clearDataTimeoutIds.get(store));
932
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
933
+ if (disconnectTimeoutIds_) {
934
+ clearTimeout(disconnectTimeoutIds_["last-unsubscribe"]);
935
+ clearTimeout(disconnectTimeoutIds_["document-hidden"]);
936
+ clearTimeout(disconnectTimeoutIds_.offline);
937
+ }
938
+ const { connectionState } = store.getState();
939
+ console.info("triggerReconnect", connectionState);
940
+ if (connectionState === "INITIAL" || connectionState === "DISCONNECTED") {
941
+ return store.connection.reconnect();
942
+ }
943
+ const shouldReconnect = reconnectOn(trigger, store.getState());
944
+ console.log({ shouldReconnect });
945
+ if (shouldReconnect) store.connection.reconnect();
946
+ };
947
+ const triggerDisconnect = (store, trigger) => {
948
+ const disconnectDelay = disconnectOn(trigger, store.getState());
949
+ if (typeof disconnectDelay !== "number") return;
950
+ if (!disconnectTimeoutIds.has(store)) disconnectTimeoutIds.set(store, {});
951
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
952
+ disconnectTimeoutIds_[trigger] = setTimeout(() => {
953
+ store.connection.disconnect();
954
+ }, disconnectDelay);
955
+ };
956
+ const triggers = {
957
+ visibilityChange: () => {
958
+ console.info("visibilityChange", document.visibilityState);
959
+ if (document.visibilityState === "visible") {
960
+ stores.forEach((store) => triggerReconnect(store, "document-visible"));
961
+ } else {
962
+ stores.forEach((store) => triggerDisconnect(store, "document-hidden"));
963
+ }
964
+ },
965
+ online: () => stores.forEach((store) => triggerReconnect(store, "online")),
966
+ offline: () => stores.forEach((store) => triggerDisconnect(store, "offline"))
967
+ };
968
+ return getStore;
969
+ };
970
+ const visibilityChangeListeners = /* @__PURE__ */ new Set();
971
+ const onVisibilityChange = () => [...visibilityChangeListeners].forEach((fn) => fn());
972
+ const onlineListeners = /* @__PURE__ */ new Set();
973
+ const onWindowOnline = () => [...onlineListeners].forEach((fn) => fn());
974
+ const offlineListeners = /* @__PURE__ */ new Set();
975
+ const onWindowOffline = () => [...offlineListeners].forEach((fn) => fn());
976
+ let listenersAdded = false;
977
+
978
+ export { createMutation, createQuery, createStore, createStores, experimental_createStream, useIsomorphicLayoutEffect, useMutation, useStoreState };
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.0-beta.1",
5
+ "version": "3.7.0-beta.2",
6
+ "publishConfig": {
7
+ "tag": "beta"
8
+ },
6
9
  "keywords": [
7
10
  "utilities",
8
11
  "store",
@@ -56,7 +56,7 @@ export type QueryState<TData, TError> = {
56
56
  } | {
57
57
  state: "SUCCESS_BUT_REVALIDATION_ERROR";
58
58
  isSuccess: true;
59
- isError: false;
59
+ isError: true;
60
60
  data: TData;
61
61
  dataUpdatedAt: number;
62
62
  dataStaleAt: undefined | number;
@@ -0,0 +1,88 @@
1
+ import { type InitStoreOptions, type StoreApi } from "../vanilla.ts";
2
+ import type { StoreKey } from "./create-stores.ts";
3
+ type StreamDataState<TData, TError> = {
4
+ state: "INITIAL";
5
+ isSuccess: false;
6
+ isError: false;
7
+ data: undefined;
8
+ dataUpdatedAt: undefined;
9
+ error: undefined;
10
+ errorUpdatedAt: undefined;
11
+ } | {
12
+ state: "SUCCESS";
13
+ isSuccess: true;
14
+ isError: false;
15
+ data: TData;
16
+ dataUpdatedAt: number;
17
+ error: undefined;
18
+ errorUpdatedAt: undefined;
19
+ } | {
20
+ state: "ERROR";
21
+ isSuccess: false;
22
+ isError: true;
23
+ data: undefined;
24
+ dataUpdatedAt: undefined;
25
+ error: TError;
26
+ errorUpdatedAt: number;
27
+ } | {
28
+ state: "SUCCESS_BUT_THEN_ERROR";
29
+ isSuccess: true;
30
+ isError: true;
31
+ data: TData;
32
+ dataUpdatedAt: number;
33
+ error: TError;
34
+ errorUpdatedAt: number;
35
+ };
36
+ export type StreamState<TData, TError> = ({
37
+ connectionState: "INITIAL";
38
+ connectingAt: undefined;
39
+ connectedAt: undefined;
40
+ disconnectedAt: undefined;
41
+ } & Extract<StreamDataState<TData, TError>, {
42
+ state: "INITIAL";
43
+ }>) | ({
44
+ connectionState: "CONNECTING";
45
+ connectingAt: number;
46
+ connectedAt: number | undefined;
47
+ disconnectedAt: number | undefined;
48
+ } & StreamDataState<TData, TError>) | ({
49
+ connectionState: "CONNECTED";
50
+ connectingAt: number;
51
+ connectedAt: number;
52
+ disconnectedAt: undefined;
53
+ } & StreamDataState<TData, TError>) | ({
54
+ connectionState: "DISCONNECTED";
55
+ connectingAt: number;
56
+ connectedAt: number | undefined;
57
+ disconnectedAt: number;
58
+ } & StreamDataState<TData, TError>);
59
+ type DisconnectTrigger = "last-unsubscribe" | "document-hidden" | "offline";
60
+ type ReconnectTrigger = "first-subscribe" | "document-visible" | "online";
61
+ type AdditionalStoreApi<TConnection> = {
62
+ variableHash: string;
63
+ connection: {
64
+ get: () => Readonly<TConnection> | undefined;
65
+ reconnect: () => void;
66
+ disconnect: () => void;
67
+ };
68
+ data: {
69
+ reset: () => void;
70
+ };
71
+ };
72
+ export type StreamOptions<TConnection, TData, TError = Error> = InitStoreOptions<StreamState<TData, TError>, AdditionalStoreApi<TConnection>> & {
73
+ connection?: {
74
+ disconnectOn?: (trigger: DisconnectTrigger, state: StreamState<TData, TError>) => false | number;
75
+ reconnectOn?: (trigger: ReconnectTrigger, state: StreamState<TData, TError>) => boolean;
76
+ };
77
+ data?: {
78
+ gcTime?: number;
79
+ };
80
+ };
81
+ export declare const experimental_createStream: <TConnection, TData, TVariable extends StoreKey, TError = Error>(connect: (variable: TVariable, emit: {
82
+ connected: () => void;
83
+ data: (reducer: (data: TData | undefined) => TData) => void;
84
+ error: (error: TError) => void;
85
+ }) => TConnection, disconnect: (connection: TConnection) => void, options?: StreamOptions<TConnection, TData, TError>) => (variable?: TVariable) => ((options?: {
86
+ initialData?: TData;
87
+ }) => StreamState<TData, TError>) & StoreApi<StreamState<TData, TError>> & AdditionalStoreApi<TConnection>;
88
+ export {};
package/react.d.ts CHANGED
@@ -5,3 +5,4 @@ export * from "./react/create-stores.ts";
5
5
  export * from "./react/create-query.ts";
6
6
  export { createMutation, type MutationOptions, type MutationState, } from "./react/create-mutation.ts";
7
7
  export * from "./react/use-mutation.ts";
8
+ export * from "./react/create-stream.ts";
package/react.js CHANGED
@@ -121,7 +121,7 @@ const createStores = (initialState, options) => {
121
121
  return getStore;
122
122
  };
123
123
 
124
- const INITIAL_STATE$1 = {
124
+ const INITIAL_STATE$2 = {
125
125
  isPending: false,
126
126
  isRevalidating: false,
127
127
  willRetryAt: void 0,
@@ -149,7 +149,7 @@ const createQuery = (queryFn, options = {}) => {
149
149
  onSettled = vanilla.noop,
150
150
  shouldRetry: shouldRetryFn = (_, s) => s.retryCount === 0 ? [true, 1500] : [false]
151
151
  } = options;
152
- const initialState = { ...INITIAL_STATE$1 };
152
+ const initialState = { ...INITIAL_STATE$2 };
153
153
  const stores = /* @__PURE__ */ new Map();
154
154
  const configureStoreEvents = (variableHash) => ({
155
155
  ...options,
@@ -167,9 +167,9 @@ const createQuery = (queryFn, options = {}) => {
167
167
  }
168
168
  }
169
169
  if (revalidateOnReconnect) {
170
- onlineListeners.add(revalidate2);
170
+ onlineListeners$1.add(revalidate2);
171
171
  if (!onlineListenersAdded) {
172
- window.addEventListener("online", onWindowOnline);
172
+ window.addEventListener("online", onWindowOnline$1);
173
173
  onlineListenersAdded = true;
174
174
  }
175
175
  }
@@ -201,9 +201,9 @@ const createQuery = (queryFn, options = {}) => {
201
201
  }
202
202
  }
203
203
  if (revalidateOnReconnect) {
204
- onlineListeners.delete(revalidate2);
205
- if (onlineListeners.size === 0) {
206
- window.removeEventListener("online", onWindowOnline);
204
+ onlineListeners$1.delete(revalidate2);
205
+ if (onlineListeners$1.size === 0) {
206
+ window.removeEventListener("online", onWindowOnline$1);
207
207
  onlineListenersAdded = false;
208
208
  }
209
209
  }
@@ -354,15 +354,10 @@ const createQuery = (queryFn, options = {}) => {
354
354
  isRevalidating: false,
355
355
  isRetrying: false,
356
356
  retryCount: 0,
357
+ state: store.getState().state === "SUCCESS" ? "SUCCESS_BUT_REVALIDATION_ERROR" : "ERROR",
358
+ isError: true,
357
359
  error,
358
- errorUpdatedAt: Date.now(),
359
- ...store.getState().data ? {
360
- state: "SUCCESS_BUT_REVALIDATION_ERROR",
361
- isError: false
362
- } : {
363
- state: "ERROR",
364
- isError: true
365
- }
360
+ errorUpdatedAt: Date.now()
366
361
  });
367
362
  const state = store.getState();
368
363
  resolve(state);
@@ -532,10 +527,10 @@ let focusListenersAdded = false;
532
527
  const focusListeners = /* @__PURE__ */ new Set();
533
528
  const onWindowFocus = () => [...focusListeners].forEach((fn) => fn());
534
529
  let onlineListenersAdded = false;
535
- const onlineListeners = /* @__PURE__ */ new Set();
536
- const onWindowOnline = () => [...onlineListeners].forEach((fn) => fn());
530
+ const onlineListeners$1 = /* @__PURE__ */ new Set();
531
+ const onWindowOnline$1 = () => [...onlineListeners$1].forEach((fn) => fn());
537
532
 
538
- const INITIAL_STATE = {
533
+ const INITIAL_STATE$1 = {
539
534
  state: "INITIAL",
540
535
  isPending: false,
541
536
  isSuccess: false,
@@ -548,7 +543,7 @@ const INITIAL_STATE = {
548
543
  };
549
544
  const createMutation = (mutationFn, options = {}) => {
550
545
  const { onSuccess = vanilla.noop, onError, onSettled = vanilla.noop } = options;
551
- const initialState = { ...INITIAL_STATE };
546
+ const initialState = { ...INITIAL_STATE$1 };
552
547
  let ongoingPromise;
553
548
  const resolveFns = /* @__PURE__ */ new Set([]);
554
549
  const store = vanilla.initStore(initialState, options);
@@ -666,7 +661,7 @@ const useMutation = (mutationFn, options = {}) => {
666
661
  callbackRef.current.onSuccess = onSuccess;
667
662
  callbackRef.current.onError = onError;
668
663
  callbackRef.current.onSettled = onSettled;
669
- const stateRef = react.useRef({ ...INITIAL_STATE });
664
+ const stateRef = react.useRef({ ...INITIAL_STATE$1 });
670
665
  const [, reRender] = react.useState({});
671
666
  const refs = react.useRef({
672
667
  mutationFn,
@@ -749,7 +744,7 @@ const useMutation = (mutationFn, options = {}) => {
749
744
  "Mutation state was reset while a request is still pending. The request will continue, but its result may override the reset state."
750
745
  );
751
746
  }
752
- stateRef.current = { ...INITIAL_STATE };
747
+ stateRef.current = { ...INITIAL_STATE$1 };
753
748
  reRender({});
754
749
  }, []);
755
750
  const r = [
@@ -763,10 +758,230 @@ const useMutation = (mutationFn, options = {}) => {
763
758
  return r;
764
759
  };
765
760
 
761
+ const INITIAL_STATE = {
762
+ connectionState: "INITIAL",
763
+ connectingAt: void 0,
764
+ connectedAt: void 0,
765
+ disconnectedAt: void 0,
766
+ state: "INITIAL",
767
+ isSuccess: false,
768
+ isError: false,
769
+ data: void 0,
770
+ dataUpdatedAt: void 0,
771
+ error: void 0,
772
+ errorUpdatedAt: void 0
773
+ };
774
+ const experimental_createStream = (connect, disconnect, options = {}) => {
775
+ const {
776
+ disconnectOn = () => 5e3,
777
+ // 5 seconds after any `DisconnectTrigger`
778
+ reconnectOn = () => false
779
+ // no need reconnect if still connected
780
+ } = options.connection || {};
781
+ const {
782
+ gcTime = 5 * 60 * 1e3
783
+ // 5 minutes
784
+ } = options.data || {};
785
+ const initialState = { ...INITIAL_STATE };
786
+ const stores = /* @__PURE__ */ new Map();
787
+ const connections = /* @__PURE__ */ new WeakMap();
788
+ const disconnectFns = /* @__PURE__ */ new WeakMap();
789
+ const disconnectTimeoutIds = /* @__PURE__ */ new WeakMap();
790
+ const clearDataTimeoutIds = /* @__PURE__ */ new WeakMap();
791
+ const configureStoreEvents = () => ({
792
+ ...options,
793
+ onFirstSubscribe: (state, store) => {
794
+ var _a, _b;
795
+ clearTimeout((_a = disconnectTimeoutIds.get(store)) == null ? void 0 : _a["last-unsubscribe"]);
796
+ (_b = options.onFirstSubscribe) == null ? void 0 : _b.call(options, state, store);
797
+ triggerReconnect(store, "first-subscribe");
798
+ if (vanilla.isClient) {
799
+ visibilityChangeListeners.add(triggers.visibilityChange);
800
+ onlineListeners.add(triggers.online);
801
+ offlineListeners.add(triggers.offline);
802
+ if (!listenersAdded) {
803
+ document.addEventListener("visibilitychange", onVisibilityChange);
804
+ window.addEventListener("online", onWindowOnline);
805
+ window.addEventListener("offline", onWindowOffline);
806
+ listenersAdded = true;
807
+ }
808
+ }
809
+ },
810
+ onLastUnsubscribe: (state, store) => {
811
+ var _a;
812
+ (_a = options.onLastUnsubscribe) == null ? void 0 : _a.call(options, state, store);
813
+ triggerDisconnect(store, "last-unsubscribe");
814
+ if (vanilla.isClient) {
815
+ visibilityChangeListeners.delete(triggers.visibilityChange);
816
+ onlineListeners.delete(triggers.online);
817
+ offlineListeners.delete(triggers.offline);
818
+ if (visibilityChangeListeners.size === 0) {
819
+ document.removeEventListener("visibilitychange", onVisibilityChange);
820
+ window.removeEventListener("online", onWindowOnline);
821
+ window.removeEventListener("offline", onWindowOffline);
822
+ listenersAdded = false;
823
+ }
824
+ }
825
+ }
826
+ });
827
+ const getStore = (variable = {}) => {
828
+ const variableHash = vanilla.getHash(variable);
829
+ let store;
830
+ if (stores.has(variableHash)) {
831
+ store = stores.get(variableHash);
832
+ } else {
833
+ store = vanilla.initStore(
834
+ initialState,
835
+ configureStoreEvents()
836
+ // Intentionally using as any: don't want to add generic on `initStore`
837
+ );
838
+ store.variableHash = variableHash;
839
+ stores.set(variableHash, store);
840
+ store.connection = {};
841
+ store.connection.get = () => connections.get(store);
842
+ store.connection.reconnect = () => {
843
+ var _a;
844
+ const { connectionState } = store.getState();
845
+ if (connectionState === "CONNECTING") return;
846
+ (_a = disconnectFns.get(store)) == null ? void 0 : _a();
847
+ store.setState({
848
+ connectionState: "CONNECTING",
849
+ connectingAt: Date.now()
850
+ });
851
+ const connection = connect(variable, {
852
+ connected: () => {
853
+ store.setState({
854
+ connectionState: "CONNECTED",
855
+ connectedAt: Date.now(),
856
+ disconnectedAt: void 0
857
+ });
858
+ },
859
+ data: (reducer) => {
860
+ store.setState((prev) => {
861
+ var _a2;
862
+ return {
863
+ connectionState: "CONNECTED",
864
+ connectedAt: (_a2 = prev.connectedAt) != null ? _a2 : Date.now(),
865
+ state: "SUCCESS",
866
+ isSuccess: true,
867
+ isError: false,
868
+ data: reducer(prev.data),
869
+ dataUpdatedAt: Date.now(),
870
+ error: void 0,
871
+ errorUpdatedAt: void 0
872
+ };
873
+ });
874
+ },
875
+ error: (error) => {
876
+ store.setState((prev) => ({
877
+ state: prev.state === "SUCCESS" ? "SUCCESS_BUT_THEN_ERROR" : "ERROR",
878
+ isError: true,
879
+ error,
880
+ errorUpdatedAt: Date.now()
881
+ }));
882
+ }
883
+ });
884
+ connections.set(store, connection);
885
+ disconnectFns.set(store, () => disconnect(connection));
886
+ };
887
+ store.connection.disconnect = () => {
888
+ var _a;
889
+ if (store.getSubscriberCount()) {
890
+ console.warn("Stream disconnected while there is subscriber");
891
+ }
892
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
893
+ if (disconnectTimeoutIds_) {
894
+ clearTimeout(disconnectTimeoutIds_["last-unsubscribe"]);
895
+ clearTimeout(disconnectTimeoutIds_["document-hidden"]);
896
+ clearTimeout(disconnectTimeoutIds_.offline);
897
+ }
898
+ (_a = disconnectFns.get(store)) == null ? void 0 : _a();
899
+ if (store.getState().connectionState !== "INITIAL") {
900
+ store.setState({
901
+ connectionState: "DISCONNECTED",
902
+ disconnectedAt: Date.now()
903
+ });
904
+ }
905
+ connections.delete(store);
906
+ disconnectFns.delete(store);
907
+ clearDataTimeoutIds.set(
908
+ store,
909
+ setTimeout(() => {
910
+ store.data.reset();
911
+ }, gcTime)
912
+ );
913
+ };
914
+ store.data = {};
915
+ store.data.reset = () => {
916
+ store.setState({
917
+ state: "INITIAL",
918
+ isSuccess: false,
919
+ isError: false,
920
+ data: void 0,
921
+ dataUpdatedAt: void 0,
922
+ error: void 0,
923
+ errorUpdatedAt: void 0
924
+ });
925
+ };
926
+ }
927
+ const useStore = (options2) => useStoreState(store, {
928
+ initialState: { data: options2 == null ? void 0 : options2.initialData }
929
+ });
930
+ return Object.assign(useStore, store);
931
+ };
932
+ const triggerReconnect = (store, trigger) => {
933
+ clearTimeout(clearDataTimeoutIds.get(store));
934
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
935
+ if (disconnectTimeoutIds_) {
936
+ clearTimeout(disconnectTimeoutIds_["last-unsubscribe"]);
937
+ clearTimeout(disconnectTimeoutIds_["document-hidden"]);
938
+ clearTimeout(disconnectTimeoutIds_.offline);
939
+ }
940
+ const { connectionState } = store.getState();
941
+ console.info("triggerReconnect", connectionState);
942
+ if (connectionState === "INITIAL" || connectionState === "DISCONNECTED") {
943
+ return store.connection.reconnect();
944
+ }
945
+ const shouldReconnect = reconnectOn(trigger, store.getState());
946
+ console.log({ shouldReconnect });
947
+ if (shouldReconnect) store.connection.reconnect();
948
+ };
949
+ const triggerDisconnect = (store, trigger) => {
950
+ const disconnectDelay = disconnectOn(trigger, store.getState());
951
+ if (typeof disconnectDelay !== "number") return;
952
+ if (!disconnectTimeoutIds.has(store)) disconnectTimeoutIds.set(store, {});
953
+ const disconnectTimeoutIds_ = disconnectTimeoutIds.get(store);
954
+ disconnectTimeoutIds_[trigger] = setTimeout(() => {
955
+ store.connection.disconnect();
956
+ }, disconnectDelay);
957
+ };
958
+ const triggers = {
959
+ visibilityChange: () => {
960
+ console.info("visibilityChange", document.visibilityState);
961
+ if (document.visibilityState === "visible") {
962
+ stores.forEach((store) => triggerReconnect(store, "document-visible"));
963
+ } else {
964
+ stores.forEach((store) => triggerDisconnect(store, "document-hidden"));
965
+ }
966
+ },
967
+ online: () => stores.forEach((store) => triggerReconnect(store, "online")),
968
+ offline: () => stores.forEach((store) => triggerDisconnect(store, "offline"))
969
+ };
970
+ return getStore;
971
+ };
972
+ const visibilityChangeListeners = /* @__PURE__ */ new Set();
973
+ const onVisibilityChange = () => [...visibilityChangeListeners].forEach((fn) => fn());
974
+ const onlineListeners = /* @__PURE__ */ new Set();
975
+ const onWindowOnline = () => [...onlineListeners].forEach((fn) => fn());
976
+ const offlineListeners = /* @__PURE__ */ new Set();
977
+ const onWindowOffline = () => [...offlineListeners].forEach((fn) => fn());
978
+ let listenersAdded = false;
979
+
766
980
  exports.createMutation = createMutation;
767
981
  exports.createQuery = createQuery;
768
982
  exports.createStore = createStore;
769
983
  exports.createStores = createStores;
984
+ exports.experimental_createStream = experimental_createStream;
770
985
  exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
771
986
  exports.useMutation = useMutation;
772
987
  exports.useStoreState = useStoreState;