reactjrx 1.34.0 → 1.35.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/dist/index.cjs CHANGED
@@ -147,8 +147,8 @@ function trigger(mapper = rxjs.identity) {
147
147
  }
148
148
  const SIGNAL_RESET = Symbol("SIGNAL_RESET");
149
149
  function signal(options) {
150
- const { default: defaultValue } = options ?? {};
151
- const subject = new rxjs.BehaviorSubject(defaultValue);
150
+ const { default: defaultValue2 } = options ?? {};
151
+ const subject = new rxjs.BehaviorSubject(defaultValue2);
152
152
  const setValue = (arg) => {
153
153
  if (arg === subject.getValue())
154
154
  return;
@@ -160,7 +160,7 @@ function signal(options) {
160
160
  return;
161
161
  }
162
162
  if (arg === SIGNAL_RESET) {
163
- subject.next(defaultValue ?? void 0);
163
+ subject.next(defaultValue2 ?? void 0);
164
164
  return;
165
165
  }
166
166
  subject.next(arg);
@@ -519,6 +519,7 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
519
519
  isLastMutationCalled
520
520
  ]).pipe(
521
521
  rxjs.map(([{ data, isError }, isLastMutationCalled2]) => {
522
+ console.log("success", { data, isLastMutationCalled: isLastMutationCalled2 });
522
523
  if (!isError) {
523
524
  if (optionsRef.current.onSuccess != null)
524
525
  optionsRef.current.onSuccess(data, args);
@@ -546,12 +547,12 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
546
547
  })
547
548
  )
548
549
  ).pipe(
550
+ rxjs.filter((state) => !!state && !!Object.keys(state).length),
549
551
  /**
550
552
  * @important
551
553
  * state update optimization
552
554
  */
553
- rxjs.distinctUntilChanged(shallowEqual),
554
- rxjs.filter((state) => !!state && !!Object.keys(state).length)
555
+ rxjs.distinctUntilChanged(shallowEqual)
555
556
  ).subscribe((state) => {
556
557
  data$.current.next({
557
558
  ...data$.current.getValue(),
@@ -579,7 +580,6 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
579
580
  }, []);
580
581
  return { ...result, isLoading: result.status === "loading", mutate, reset };
581
582
  }
582
- const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
583
583
  const useCreateCacheStore = () => {
584
584
  const cacheStore = useBehaviorSubject({});
585
585
  useSubscribe(
@@ -641,21 +641,17 @@ const useQueryStore = () => {
641
641
  const [store] = react.useState(/* @__PURE__ */ new Map());
642
642
  return store;
643
643
  };
644
- const Context = react.createContext(void 0);
645
- const Provider = react.memo(({ children }) => {
646
- const cacheStore = useCreateCacheStore();
647
- const queryStore = useQueryStore();
648
- const value = react.useMemo(() => ({ cacheStore, queryStore }), []);
649
- return /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
650
- });
651
- const useProvider = () => {
652
- const context = react.useContext(Context);
653
- return { ...context };
654
- };
655
- const serializeKey = (key) => {
656
- if (key.length === 0)
657
- return "";
658
- return JSON.stringify(key);
644
+ const DEFAULT_STALE = 9999;
645
+ const autoRefetch = (options$ = rxjs.of({})) => (source) => {
646
+ return source.pipe(
647
+ rxjs.repeat({
648
+ delay: () => options$.pipe(
649
+ rxjs.switchMap(
650
+ (options) => options.staleTime === Infinity ? rxjs.EMPTY : rxjs.timer(options.staleTime ?? DEFAULT_STALE)
651
+ )
652
+ )
653
+ })
654
+ );
659
655
  };
660
656
  const deduplicate = (key, queryStore) => (source) => {
661
657
  if (!key)
@@ -675,54 +671,151 @@ const deduplicate = (key, queryStore) => (source) => {
675
671
  }
676
672
  return finalSource;
677
673
  };
678
- const autoRefetch = (options = {}) => (source) => {
679
- if (options.staleTime === Infinity)
680
- return source;
681
- return source.pipe(
682
- rxjs.repeat({
683
- delay: options.staleTime ?? 999999
684
- })
685
- );
674
+ const serializeKey = (key) => {
675
+ if (key.length === 0)
676
+ return "";
677
+ return JSON.stringify(key);
686
678
  };
687
- function isDefined(arg) {
688
- return arg !== null && arg !== void 0;
689
- }
690
- const withKeyComparison = (stream) => stream.pipe(
691
- rxjs.startWith(void 0),
679
+ const notifyQueryResult = (options$) => (stream$) => stream$.pipe(
680
+ rxjs.withLatestFrom(options$),
681
+ rxjs.map(([result, options]) => {
682
+ var _a, _b;
683
+ if (result.error) {
684
+ (_a = options.onError) == null ? void 0 : _a.call(options, result.error);
685
+ } else {
686
+ (_b = options.onSuccess) == null ? void 0 : _b.call(options, result);
687
+ }
688
+ return result;
689
+ })
690
+ );
691
+ const mergeResults = (stream$) => stream$.pipe(
692
+ rxjs.startWith({ isLoading: false, data: void 0, error: void 0 }),
692
693
  rxjs.pairwise(),
693
- rxjs.map(([previous, current]) => {
694
- if (current) {
695
- if (!previous) {
696
- return {
697
- ...current,
698
- previousKey: void 0,
699
- isUsingDifferentKey: true
700
- };
701
- }
702
- const serializedKey = serializeKey(current.key);
703
- const serializedPreviousKey = serializeKey(previous.key);
704
- return {
705
- ...current,
706
- previousKey: (previous == null ? void 0 : previous.key) ?? current.key,
707
- isUsingDifferentKey: serializedPreviousKey !== serializedKey
708
- };
694
+ rxjs.map(([previous, current]) => ({
695
+ isLoading: false,
696
+ data: void 0,
697
+ error: void 0,
698
+ ...previous,
699
+ ...current
700
+ })),
701
+ rxjs.distinctUntilChanged(shallowEqual)
702
+ );
703
+ const createClient = () => {
704
+ const queryStore = /* @__PURE__ */ new Map();
705
+ const refetch$ = new rxjs.Subject();
706
+ const query$ = ({
707
+ key,
708
+ fn$,
709
+ options$
710
+ }) => {
711
+ const refetch$2 = new rxjs.Subject();
712
+ const enabled$ = options$.pipe(rxjs.map(({ enabled = true }) => enabled));
713
+ const disabled$ = enabled$.pipe(
714
+ rxjs.distinctUntilChanged(),
715
+ rxjs.filter((enabled) => !enabled)
716
+ );
717
+ const triggers = [
718
+ refetch$2.pipe(rxjs.startWith(null)),
719
+ enabled$.pipe(
720
+ rxjs.distinctUntilChanged(),
721
+ rxjs.filter((enabled) => enabled)
722
+ )
723
+ ];
724
+ const serializedKey = serializeKey(key);
725
+ const query$2 = rxjs.combineLatest(triggers).pipe(
726
+ rxjs.tap((params) => {
727
+ console.log("query$ trigger", { key, params });
728
+ }),
729
+ rxjs.withLatestFrom(fn$),
730
+ rxjs.switchMap(([, query]) => {
731
+ const deferredQuery = rxjs.defer(() => {
732
+ const queryOrResponse = typeof query === "function" ? query() : query;
733
+ return rxjs.from(queryOrResponse);
734
+ });
735
+ return rxjs.merge(
736
+ disabled$.pipe(
737
+ rxjs.take(1),
738
+ rxjs.map(() => ({
739
+ isLoading: false
740
+ }))
741
+ ),
742
+ rxjs.merge(
743
+ rxjs.of({ isLoading: true, error: void 0 }),
744
+ deferredQuery.pipe(
745
+ deduplicate(serializedKey, queryStore),
746
+ rxjs.map((result) => ({
747
+ isLoading: false,
748
+ data: { result },
749
+ error: void 0
750
+ })),
751
+ rxjs.catchError(
752
+ (error) => rxjs.of({
753
+ isLoading: false,
754
+ data: void 0,
755
+ error
756
+ })
757
+ ),
758
+ notifyQueryResult(options$)
759
+ )
760
+ ).pipe(autoRefetch(options$), rxjs.takeUntil(disabled$))
761
+ );
762
+ }),
763
+ mergeResults,
764
+ rxjs.tap((data) => {
765
+ console.log("query$ return", data);
766
+ })
767
+ );
768
+ return {
769
+ query$: query$2,
770
+ refetch$: refetch$2,
771
+ enabled$
772
+ };
773
+ };
774
+ return {
775
+ query$,
776
+ refetch$,
777
+ queryStore,
778
+ destroy: () => {
709
779
  }
710
- return void 0;
711
- }),
712
- rxjs.filter(isDefined)
780
+ };
781
+ };
782
+ const Context = react.createContext({
783
+ cacheStore: {},
784
+ client: createClient()
785
+ });
786
+ const Provider = react.memo(
787
+ ({
788
+ children,
789
+ client
790
+ }) => {
791
+ const cacheStore = useCreateCacheStore();
792
+ const queryStore = useQueryStore();
793
+ const value = react.useMemo(() => ({ cacheStore, queryStore, client }), [client]);
794
+ react.useEffect(
795
+ () => () => {
796
+ client.destroy();
797
+ },
798
+ [client]
799
+ );
800
+ return /* @__PURE__ */ jsxRuntime.jsx(Context.Provider, { value, children });
801
+ }
713
802
  );
803
+ const useProvider = () => {
804
+ const context = react.useContext(Context);
805
+ return { ...context };
806
+ };
807
+ const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
808
+ function isDefined(arg) {
809
+ return arg !== null && arg !== void 0;
810
+ }
811
+ const defaultValue = { data: void 0, isLoading: true, error: void 0 };
714
812
  function useQuery(keyOrQuery, queryOrOptionOrNothing, optionsOrNothing) {
715
813
  const query = Array.isArray(keyOrQuery) ? queryOrOptionOrNothing : keyOrQuery;
716
814
  const options = optionsOrNothing ?? (queryOrOptionOrNothing !== query ? queryOrOptionOrNothing : void 0) ?? {};
815
+ const internalRefresh$ = useSubject();
816
+ const { client } = useProvider();
717
817
  const key = Array.isArray(keyOrQuery) ? keyOrQuery : void 0;
718
818
  const params$ = useBehaviorSubject({ key, options, query });
719
- const refetch$ = useSubject();
720
- const data$ = useBehaviorSubject({
721
- data: void 0,
722
- error: void 0,
723
- isLoading: true
724
- });
725
- const { queryStore } = useProvider();
726
819
  react.useEffect(() => {
727
820
  params$.current.next({
728
821
  key,
@@ -730,104 +823,81 @@ function useQuery(keyOrQuery, queryOrOptionOrNothing, optionsOrNothing) {
730
823
  query
731
824
  });
732
825
  }, [key, options, query]);
733
- useSubscribe(() => {
734
- const options$ = params$.current.pipe(
735
- rxjs.map(({ options: options2 }) => options2),
736
- rxjs.distinctUntilChanged(shallowEqual)
737
- );
738
- const newKeyReceived$ = params$.current.pipe(
739
- rxjs.map(({ key: key2 }) => key2 ?? []),
740
- rxjs.distinctUntilChanged(arrayEqual)
741
- );
742
- const newQuery$ = params$.current.pipe(
743
- rxjs.map(({ query: query2 }) => query2 ?? (() => rxjs.of(void 0)))
744
- );
745
- const queryAsObservableObjectChanged$ = newQuery$.pipe(
746
- rxjs.filter((query2) => typeof query2 !== "function"),
747
- rxjs.distinctUntilChanged(shallowEqual)
748
- );
749
- const enabledOptionChanged$ = options$.pipe(
750
- rxjs.map(({ enabled = true }) => enabled),
751
- rxjs.distinctUntilChanged(),
752
- rxjs.share()
753
- );
754
- const queryTrigger$ = rxjs.combineLatest([
755
- newKeyReceived$,
756
- enabledOptionChanged$,
757
- queryAsObservableObjectChanged$.pipe(rxjs.startWith(void 0)),
758
- refetch$.current.pipe(rxjs.startWith(void 0))
759
- ]);
760
- const disabled$ = enabledOptionChanged$.pipe(
761
- rxjs.filter((enabled) => !enabled),
762
- rxjs.tap(() => {
763
- data$.current.next({
764
- ...data$.current.getValue(),
765
- isLoading: false
766
- });
767
- })
768
- );
769
- return queryTrigger$.pipe(
770
- rxjs.withLatestFrom(options$, newQuery$),
771
- rxjs.map(([[key2, enabled], options2, query2]) => ({
772
- key: key2,
773
- enabled,
774
- options: options2,
775
- query: query2
776
- })),
777
- withKeyComparison,
778
- rxjs.filter(({ enabled }) => enabled),
779
- rxjs.switchMap(({ key: key2, options: options2, isUsingDifferentKey, query: query2 }) => {
780
- const serializedKey = serializeKey(key2);
781
- return rxjs.of(null).pipe(
782
- rxjs.tap(() => {
783
- data$.current.next({
784
- ...data$.current.getValue(),
785
- data: isUsingDifferentKey ? void 0 : data$.current.getValue().data,
786
- error: void 0,
787
- isLoading: true
788
- });
789
- }),
790
- rxjs.switchMap(() => {
791
- const query$ = rxjs.defer(() => {
792
- const queryOrResponse = typeof query2 === "function" ? query2() : query2;
793
- return rxjs.from(queryOrResponse);
794
- });
795
- return query$.pipe(
796
- querx(options2),
797
- deduplicate(serializedKey, queryStore),
798
- // key.length > 0 ? withCache(key) : identity,
799
- rxjs.map((response) => [response]),
800
- rxjs.catchError((error) => {
801
- return rxjs.of([void 0, error]);
802
- }),
803
- rxjs.tap(([response, error]) => {
804
- if (response) {
805
- if (options2.onSuccess != null)
806
- options2.onSuccess(response);
807
- }
808
- data$.current.next({
809
- ...data$.current.getValue(),
810
- isLoading: false,
811
- error,
812
- data: response
813
- });
814
- })
815
- );
816
- }),
817
- autoRefetch(options2),
818
- rxjs.takeUntil(disabled$)
819
- );
820
- })
821
- );
822
- }, []);
823
826
  const result = useObserve(
824
- () => data$.current,
825
- { defaultValue: data$.current.getValue() },
826
- []
827
+ () => {
828
+ const computedDefaultValue = {
829
+ ...defaultValue,
830
+ isLoading: params$.current.getValue().options.enabled !== false
831
+ };
832
+ const newKeyReceived$ = params$.current.pipe(
833
+ rxjs.map(({ key: key2 }) => key2 ?? []),
834
+ rxjs.distinctUntilChanged(arrayEqual)
835
+ );
836
+ const newObservableObjectQuery$ = params$.current.pipe(
837
+ rxjs.map(({ query: query2 }) => query2),
838
+ rxjs.distinctUntilChanged(shallowEqual),
839
+ rxjs.skip(1),
840
+ rxjs.filter((query2) => !!query2 && typeof query2 !== "function"),
841
+ rxjs.startWith(params$.current.getValue().query),
842
+ rxjs.filter(isDefined)
843
+ );
844
+ const fn$ = params$.current.pipe(
845
+ rxjs.map(({ query: query2 }) => query2),
846
+ rxjs.filter(isDefined)
847
+ );
848
+ const options$ = params$.current.pipe(rxjs.map(({ options: options2 }) => options2));
849
+ const triggers$ = rxjs.combineLatest([
850
+ newKeyReceived$,
851
+ newObservableObjectQuery$
852
+ ]);
853
+ return triggers$.pipe(
854
+ rxjs.switchMap(([key2]) => {
855
+ const { query$, refetch$ } = client.query$({
856
+ key: key2,
857
+ fn$,
858
+ options$
859
+ });
860
+ const subscriptions = [internalRefresh$.current.subscribe(refetch$)];
861
+ return query$.pipe(
862
+ rxjs.finalize(() => {
863
+ subscriptions.forEach((sub) => {
864
+ sub.unsubscribe();
865
+ });
866
+ }),
867
+ rxjs.startWith(computedDefaultValue),
868
+ rxjs.pairwise(),
869
+ rxjs.map(
870
+ ([
871
+ { data: previousData, ...restPrevious },
872
+ { data: currentData, ...restCurrent }
873
+ ]) => ({
874
+ ...restPrevious,
875
+ ...restCurrent,
876
+ data: currentData && "result" in currentData ? currentData.result : previousData == null ? void 0 : previousData.result
877
+ })
878
+ )
879
+ );
880
+ }),
881
+ /**
882
+ * @important
883
+ * We skip the first result as it is comparable to default passed value.
884
+ * This is assuming all query are async and does not return a result right away.
885
+ * This is a design choice.
886
+ */
887
+ params$.current.getValue().options.enabled !== false ? rxjs.skip(1) : rxjs.identity
888
+ );
889
+ },
890
+ {
891
+ defaultValue: {
892
+ ...defaultValue,
893
+ isLoading: params$.current.getValue().options.enabled !== false
894
+ }
895
+ },
896
+ [client]
827
897
  );
828
- const refetch = react.useCallback((arg) => {
829
- refetch$.current.next(arg);
830
- }, []);
898
+ const refetch = react.useCallback(() => {
899
+ internalRefresh$.current.next();
900
+ }, [client]);
831
901
  return { ...result, refetch };
832
902
  }
833
903
  function useSubscribeEffect(source, unsafeOptions, deps = []) {
@@ -853,6 +923,7 @@ function useSubscribeEffect(source, unsafeOptions, deps = []) {
853
923
  exports.PersistSignals = PersistSignals;
854
924
  exports.ReactjrxQueryProvider = Provider;
855
925
  exports.SIGNAL_RESET = SIGNAL_RESET;
926
+ exports.createClient = createClient;
856
927
  exports.createLocalforageAdapter = createLocalforageAdapter;
857
928
  exports.createSharedStoreAdapter = createSharedStoreAdapter;
858
929
  exports.exponentialBackoffDelay = exponentialBackoffDelay;
@@ -870,6 +941,7 @@ exports.useScopeSignals = useScopeSignals;
870
941
  exports.useSetSignal = useSetSignal;
871
942
  exports.useSignal = useSignal;
872
943
  exports.useSignalValue = useSignalValue;
944
+ exports.useSubject = useSubject;
873
945
  exports.useSubscribe = useSubscribe;
874
946
  exports.useSubscribeEffect = useSubscribeEffect;
875
947
  exports.useUnmountObservable = useUnmountObservable;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./lib/binding/useObserve";
2
2
  export * from "./lib/binding/useSubscribe";
3
3
  export * from "./lib/binding/useObserveCallback";
4
4
  export * from "./lib/binding/trigger";
5
+ export * from "./lib/binding/useSubject";
5
6
  export * from "./lib/state/signal";
6
7
  export * from "./lib/state/useSignal";
7
8
  export * from "./lib/state/useSetSignal";
@@ -18,4 +19,5 @@ export * from "./lib/utils/useLiveRef";
18
19
  export * from "./lib/queries/useAsyncQuery";
19
20
  export * from "./lib/queries/useQuery";
20
21
  export * from "./lib/queries/useSubscribeEffect";
22
+ export * from "./lib/queries/client/createClient";
21
23
  export { Provider as ReactjrxQueryProvider } from "./lib/queries/Provider";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useRef, useMemo, useCallback, useSyncExternalStore, useEffect, createContext, memo, useContext, useState } from "react";
2
- import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, of, zip, from, map, merge, throttleTime, switchMap, defer, iif, timer, throwError, take, startWith, combineLatest, first, takeUntil, filter, concatMap as concatMap$1, mergeMap, skip, interval, withLatestFrom, shareReplay, repeat, pairwise, share, retry } from "rxjs";
2
+ import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, of, zip, from, map, merge, throttleTime, switchMap, defer, iif, timer, throwError, take, startWith, combineLatest, first, takeUntil, filter, concatMap as concatMap$1, mergeMap, skip, interval, withLatestFrom, repeat, shareReplay, pairwise, retry } from "rxjs";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { retryWhen, concatMap, tap as tap$1 } from "rxjs/operators";
5
5
  const useLiveRef = (value) => {
@@ -145,8 +145,8 @@ function trigger(mapper = identity) {
145
145
  }
146
146
  const SIGNAL_RESET = Symbol("SIGNAL_RESET");
147
147
  function signal(options) {
148
- const { default: defaultValue } = options ?? {};
149
- const subject = new BehaviorSubject(defaultValue);
148
+ const { default: defaultValue2 } = options ?? {};
149
+ const subject = new BehaviorSubject(defaultValue2);
150
150
  const setValue = (arg) => {
151
151
  if (arg === subject.getValue())
152
152
  return;
@@ -158,7 +158,7 @@ function signal(options) {
158
158
  return;
159
159
  }
160
160
  if (arg === SIGNAL_RESET) {
161
- subject.next(defaultValue ?? void 0);
161
+ subject.next(defaultValue2 ?? void 0);
162
162
  return;
163
163
  }
164
164
  subject.next(arg);
@@ -517,6 +517,7 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
517
517
  isLastMutationCalled
518
518
  ]).pipe(
519
519
  map(([{ data, isError }, isLastMutationCalled2]) => {
520
+ console.log("success", { data, isLastMutationCalled: isLastMutationCalled2 });
520
521
  if (!isError) {
521
522
  if (optionsRef.current.onSuccess != null)
522
523
  optionsRef.current.onSuccess(data, args);
@@ -544,12 +545,12 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
544
545
  })
545
546
  )
546
547
  ).pipe(
548
+ filter((state) => !!state && !!Object.keys(state).length),
547
549
  /**
548
550
  * @important
549
551
  * state update optimization
550
552
  */
551
- distinctUntilChanged(shallowEqual),
552
- filter((state) => !!state && !!Object.keys(state).length)
553
+ distinctUntilChanged(shallowEqual)
553
554
  ).subscribe((state) => {
554
555
  data$.current.next({
555
556
  ...data$.current.getValue(),
@@ -577,7 +578,6 @@ function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
577
578
  }, []);
578
579
  return { ...result, isLoading: result.status === "loading", mutate, reset };
579
580
  }
580
- const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
581
581
  const useCreateCacheStore = () => {
582
582
  const cacheStore = useBehaviorSubject({});
583
583
  useSubscribe(
@@ -639,21 +639,17 @@ const useQueryStore = () => {
639
639
  const [store] = useState(/* @__PURE__ */ new Map());
640
640
  return store;
641
641
  };
642
- const Context = createContext(void 0);
643
- const Provider = memo(({ children }) => {
644
- const cacheStore = useCreateCacheStore();
645
- const queryStore = useQueryStore();
646
- const value = useMemo(() => ({ cacheStore, queryStore }), []);
647
- return /* @__PURE__ */ jsx(Context.Provider, { value, children });
648
- });
649
- const useProvider = () => {
650
- const context = useContext(Context);
651
- return { ...context };
652
- };
653
- const serializeKey = (key) => {
654
- if (key.length === 0)
655
- return "";
656
- return JSON.stringify(key);
642
+ const DEFAULT_STALE = 9999;
643
+ const autoRefetch = (options$ = of({})) => (source) => {
644
+ return source.pipe(
645
+ repeat({
646
+ delay: () => options$.pipe(
647
+ switchMap(
648
+ (options) => options.staleTime === Infinity ? EMPTY : timer(options.staleTime ?? DEFAULT_STALE)
649
+ )
650
+ )
651
+ })
652
+ );
657
653
  };
658
654
  const deduplicate = (key, queryStore) => (source) => {
659
655
  if (!key)
@@ -673,54 +669,151 @@ const deduplicate = (key, queryStore) => (source) => {
673
669
  }
674
670
  return finalSource;
675
671
  };
676
- const autoRefetch = (options = {}) => (source) => {
677
- if (options.staleTime === Infinity)
678
- return source;
679
- return source.pipe(
680
- repeat({
681
- delay: options.staleTime ?? 999999
682
- })
683
- );
672
+ const serializeKey = (key) => {
673
+ if (key.length === 0)
674
+ return "";
675
+ return JSON.stringify(key);
684
676
  };
685
- function isDefined(arg) {
686
- return arg !== null && arg !== void 0;
687
- }
688
- const withKeyComparison = (stream) => stream.pipe(
689
- startWith(void 0),
677
+ const notifyQueryResult = (options$) => (stream$) => stream$.pipe(
678
+ withLatestFrom(options$),
679
+ map(([result, options]) => {
680
+ var _a, _b;
681
+ if (result.error) {
682
+ (_a = options.onError) == null ? void 0 : _a.call(options, result.error);
683
+ } else {
684
+ (_b = options.onSuccess) == null ? void 0 : _b.call(options, result);
685
+ }
686
+ return result;
687
+ })
688
+ );
689
+ const mergeResults = (stream$) => stream$.pipe(
690
+ startWith({ isLoading: false, data: void 0, error: void 0 }),
690
691
  pairwise(),
691
- map(([previous, current]) => {
692
- if (current) {
693
- if (!previous) {
694
- return {
695
- ...current,
696
- previousKey: void 0,
697
- isUsingDifferentKey: true
698
- };
699
- }
700
- const serializedKey = serializeKey(current.key);
701
- const serializedPreviousKey = serializeKey(previous.key);
702
- return {
703
- ...current,
704
- previousKey: (previous == null ? void 0 : previous.key) ?? current.key,
705
- isUsingDifferentKey: serializedPreviousKey !== serializedKey
706
- };
692
+ map(([previous, current]) => ({
693
+ isLoading: false,
694
+ data: void 0,
695
+ error: void 0,
696
+ ...previous,
697
+ ...current
698
+ })),
699
+ distinctUntilChanged(shallowEqual)
700
+ );
701
+ const createClient = () => {
702
+ const queryStore = /* @__PURE__ */ new Map();
703
+ const refetch$ = new Subject();
704
+ const query$ = ({
705
+ key,
706
+ fn$,
707
+ options$
708
+ }) => {
709
+ const refetch$2 = new Subject();
710
+ const enabled$ = options$.pipe(map(({ enabled = true }) => enabled));
711
+ const disabled$ = enabled$.pipe(
712
+ distinctUntilChanged(),
713
+ filter((enabled) => !enabled)
714
+ );
715
+ const triggers = [
716
+ refetch$2.pipe(startWith(null)),
717
+ enabled$.pipe(
718
+ distinctUntilChanged(),
719
+ filter((enabled) => enabled)
720
+ )
721
+ ];
722
+ const serializedKey = serializeKey(key);
723
+ const query$2 = combineLatest(triggers).pipe(
724
+ tap((params) => {
725
+ console.log("query$ trigger", { key, params });
726
+ }),
727
+ withLatestFrom(fn$),
728
+ switchMap(([, query]) => {
729
+ const deferredQuery = defer(() => {
730
+ const queryOrResponse = typeof query === "function" ? query() : query;
731
+ return from(queryOrResponse);
732
+ });
733
+ return merge(
734
+ disabled$.pipe(
735
+ take(1),
736
+ map(() => ({
737
+ isLoading: false
738
+ }))
739
+ ),
740
+ merge(
741
+ of({ isLoading: true, error: void 0 }),
742
+ deferredQuery.pipe(
743
+ deduplicate(serializedKey, queryStore),
744
+ map((result) => ({
745
+ isLoading: false,
746
+ data: { result },
747
+ error: void 0
748
+ })),
749
+ catchError(
750
+ (error) => of({
751
+ isLoading: false,
752
+ data: void 0,
753
+ error
754
+ })
755
+ ),
756
+ notifyQueryResult(options$)
757
+ )
758
+ ).pipe(autoRefetch(options$), takeUntil(disabled$))
759
+ );
760
+ }),
761
+ mergeResults,
762
+ tap((data) => {
763
+ console.log("query$ return", data);
764
+ })
765
+ );
766
+ return {
767
+ query$: query$2,
768
+ refetch$: refetch$2,
769
+ enabled$
770
+ };
771
+ };
772
+ return {
773
+ query$,
774
+ refetch$,
775
+ queryStore,
776
+ destroy: () => {
707
777
  }
708
- return void 0;
709
- }),
710
- filter(isDefined)
778
+ };
779
+ };
780
+ const Context = createContext({
781
+ cacheStore: {},
782
+ client: createClient()
783
+ });
784
+ const Provider = memo(
785
+ ({
786
+ children,
787
+ client
788
+ }) => {
789
+ const cacheStore = useCreateCacheStore();
790
+ const queryStore = useQueryStore();
791
+ const value = useMemo(() => ({ cacheStore, queryStore, client }), [client]);
792
+ useEffect(
793
+ () => () => {
794
+ client.destroy();
795
+ },
796
+ [client]
797
+ );
798
+ return /* @__PURE__ */ jsx(Context.Provider, { value, children });
799
+ }
711
800
  );
801
+ const useProvider = () => {
802
+ const context = useContext(Context);
803
+ return { ...context };
804
+ };
805
+ const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
806
+ function isDefined(arg) {
807
+ return arg !== null && arg !== void 0;
808
+ }
809
+ const defaultValue = { data: void 0, isLoading: true, error: void 0 };
712
810
  function useQuery(keyOrQuery, queryOrOptionOrNothing, optionsOrNothing) {
713
811
  const query = Array.isArray(keyOrQuery) ? queryOrOptionOrNothing : keyOrQuery;
714
812
  const options = optionsOrNothing ?? (queryOrOptionOrNothing !== query ? queryOrOptionOrNothing : void 0) ?? {};
813
+ const internalRefresh$ = useSubject();
814
+ const { client } = useProvider();
715
815
  const key = Array.isArray(keyOrQuery) ? keyOrQuery : void 0;
716
816
  const params$ = useBehaviorSubject({ key, options, query });
717
- const refetch$ = useSubject();
718
- const data$ = useBehaviorSubject({
719
- data: void 0,
720
- error: void 0,
721
- isLoading: true
722
- });
723
- const { queryStore } = useProvider();
724
817
  useEffect(() => {
725
818
  params$.current.next({
726
819
  key,
@@ -728,104 +821,81 @@ function useQuery(keyOrQuery, queryOrOptionOrNothing, optionsOrNothing) {
728
821
  query
729
822
  });
730
823
  }, [key, options, query]);
731
- useSubscribe(() => {
732
- const options$ = params$.current.pipe(
733
- map(({ options: options2 }) => options2),
734
- distinctUntilChanged(shallowEqual)
735
- );
736
- const newKeyReceived$ = params$.current.pipe(
737
- map(({ key: key2 }) => key2 ?? []),
738
- distinctUntilChanged(arrayEqual)
739
- );
740
- const newQuery$ = params$.current.pipe(
741
- map(({ query: query2 }) => query2 ?? (() => of(void 0)))
742
- );
743
- const queryAsObservableObjectChanged$ = newQuery$.pipe(
744
- filter((query2) => typeof query2 !== "function"),
745
- distinctUntilChanged(shallowEqual)
746
- );
747
- const enabledOptionChanged$ = options$.pipe(
748
- map(({ enabled = true }) => enabled),
749
- distinctUntilChanged(),
750
- share()
751
- );
752
- const queryTrigger$ = combineLatest([
753
- newKeyReceived$,
754
- enabledOptionChanged$,
755
- queryAsObservableObjectChanged$.pipe(startWith(void 0)),
756
- refetch$.current.pipe(startWith(void 0))
757
- ]);
758
- const disabled$ = enabledOptionChanged$.pipe(
759
- filter((enabled) => !enabled),
760
- tap(() => {
761
- data$.current.next({
762
- ...data$.current.getValue(),
763
- isLoading: false
764
- });
765
- })
766
- );
767
- return queryTrigger$.pipe(
768
- withLatestFrom(options$, newQuery$),
769
- map(([[key2, enabled], options2, query2]) => ({
770
- key: key2,
771
- enabled,
772
- options: options2,
773
- query: query2
774
- })),
775
- withKeyComparison,
776
- filter(({ enabled }) => enabled),
777
- switchMap(({ key: key2, options: options2, isUsingDifferentKey, query: query2 }) => {
778
- const serializedKey = serializeKey(key2);
779
- return of(null).pipe(
780
- tap(() => {
781
- data$.current.next({
782
- ...data$.current.getValue(),
783
- data: isUsingDifferentKey ? void 0 : data$.current.getValue().data,
784
- error: void 0,
785
- isLoading: true
786
- });
787
- }),
788
- switchMap(() => {
789
- const query$ = defer(() => {
790
- const queryOrResponse = typeof query2 === "function" ? query2() : query2;
791
- return from(queryOrResponse);
792
- });
793
- return query$.pipe(
794
- querx(options2),
795
- deduplicate(serializedKey, queryStore),
796
- // key.length > 0 ? withCache(key) : identity,
797
- map((response) => [response]),
798
- catchError((error) => {
799
- return of([void 0, error]);
800
- }),
801
- tap(([response, error]) => {
802
- if (response) {
803
- if (options2.onSuccess != null)
804
- options2.onSuccess(response);
805
- }
806
- data$.current.next({
807
- ...data$.current.getValue(),
808
- isLoading: false,
809
- error,
810
- data: response
811
- });
812
- })
813
- );
814
- }),
815
- autoRefetch(options2),
816
- takeUntil(disabled$)
817
- );
818
- })
819
- );
820
- }, []);
821
824
  const result = useObserve(
822
- () => data$.current,
823
- { defaultValue: data$.current.getValue() },
824
- []
825
+ () => {
826
+ const computedDefaultValue = {
827
+ ...defaultValue,
828
+ isLoading: params$.current.getValue().options.enabled !== false
829
+ };
830
+ const newKeyReceived$ = params$.current.pipe(
831
+ map(({ key: key2 }) => key2 ?? []),
832
+ distinctUntilChanged(arrayEqual)
833
+ );
834
+ const newObservableObjectQuery$ = params$.current.pipe(
835
+ map(({ query: query2 }) => query2),
836
+ distinctUntilChanged(shallowEqual),
837
+ skip(1),
838
+ filter((query2) => !!query2 && typeof query2 !== "function"),
839
+ startWith(params$.current.getValue().query),
840
+ filter(isDefined)
841
+ );
842
+ const fn$ = params$.current.pipe(
843
+ map(({ query: query2 }) => query2),
844
+ filter(isDefined)
845
+ );
846
+ const options$ = params$.current.pipe(map(({ options: options2 }) => options2));
847
+ const triggers$ = combineLatest([
848
+ newKeyReceived$,
849
+ newObservableObjectQuery$
850
+ ]);
851
+ return triggers$.pipe(
852
+ switchMap(([key2]) => {
853
+ const { query$, refetch$ } = client.query$({
854
+ key: key2,
855
+ fn$,
856
+ options$
857
+ });
858
+ const subscriptions = [internalRefresh$.current.subscribe(refetch$)];
859
+ return query$.pipe(
860
+ finalize(() => {
861
+ subscriptions.forEach((sub) => {
862
+ sub.unsubscribe();
863
+ });
864
+ }),
865
+ startWith(computedDefaultValue),
866
+ pairwise(),
867
+ map(
868
+ ([
869
+ { data: previousData, ...restPrevious },
870
+ { data: currentData, ...restCurrent }
871
+ ]) => ({
872
+ ...restPrevious,
873
+ ...restCurrent,
874
+ data: currentData && "result" in currentData ? currentData.result : previousData == null ? void 0 : previousData.result
875
+ })
876
+ )
877
+ );
878
+ }),
879
+ /**
880
+ * @important
881
+ * We skip the first result as it is comparable to default passed value.
882
+ * This is assuming all query are async and does not return a result right away.
883
+ * This is a design choice.
884
+ */
885
+ params$.current.getValue().options.enabled !== false ? skip(1) : identity
886
+ );
887
+ },
888
+ {
889
+ defaultValue: {
890
+ ...defaultValue,
891
+ isLoading: params$.current.getValue().options.enabled !== false
892
+ }
893
+ },
894
+ [client]
825
895
  );
826
- const refetch = useCallback((arg) => {
827
- refetch$.current.next(arg);
828
- }, []);
896
+ const refetch = useCallback(() => {
897
+ internalRefresh$.current.next();
898
+ }, [client]);
829
899
  return { ...result, refetch };
830
900
  }
831
901
  function useSubscribeEffect(source, unsafeOptions, deps = []) {
@@ -852,6 +922,7 @@ export {
852
922
  PersistSignals,
853
923
  Provider as ReactjrxQueryProvider,
854
924
  SIGNAL_RESET,
925
+ createClient,
855
926
  createLocalforageAdapter,
856
927
  createSharedStoreAdapter,
857
928
  exponentialBackoffDelay,
@@ -869,6 +940,7 @@ export {
869
940
  useSetSignal,
870
941
  useSignal,
871
942
  useSignalValue,
943
+ useSubject,
872
944
  useSubscribe,
873
945
  useSubscribeEffect,
874
946
  useUnmountObservable,
@@ -10,6 +10,6 @@ interface Option<R = undefined> {
10
10
  export declare function useObserve<T>(source: BehaviorSubject<T>): T;
11
11
  export declare function useObserve<T>(source: Observable<T>): T | undefined;
12
12
  export declare function useObserve<T>(source: () => Observable<T>, deps: DependencyList): T | undefined;
13
- export declare function useObserve<T, R = undefined>(source: Observable<T>, options: Option<R>): T | R;
14
- export declare function useObserve<T, R = undefined>(source: () => Observable<T>, options: Option<R>, deps: DependencyList): T | R;
13
+ export declare function useObserve<T>(source: Observable<T>, options: Option<T>): T;
14
+ export declare function useObserve<T>(source: () => Observable<T>, options: Option<T>, deps: DependencyList): T;
15
15
  export {};
@@ -1,5 +1,6 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { type BehaviorSubject } from "rxjs";
3
+ import { createClient } from "./client/createClient";
3
4
  type CacheStore = Record<string, {
4
5
  value: any;
5
6
  date: number;
@@ -9,15 +10,16 @@ export declare const Context: import("react").Context<{
9
10
  cacheStore: {
10
11
  current: BehaviorSubject<CacheStore>;
11
12
  };
12
- queryStore: import("./deduplication/useQueryStore").QueryStore;
13
- } | undefined>;
14
- export declare const Provider: import("react").MemoExoticComponent<({ children }: {
13
+ client: ReturnType<typeof createClient>;
14
+ }>;
15
+ export declare const Provider: import("react").MemoExoticComponent<({ children, client }: {
15
16
  children: ReactNode;
17
+ client: ReturnType<typeof createClient>;
16
18
  }) => import("react/jsx-runtime").JSX.Element>;
17
19
  export declare const useProvider: () => {
18
- cacheStore?: {
20
+ cacheStore: {
19
21
  current: BehaviorSubject<CacheStore>;
20
- } | undefined;
21
- queryStore?: import("./deduplication/useQueryStore").QueryStore | undefined;
22
+ };
23
+ client: ReturnType<typeof createClient>;
22
24
  };
23
25
  export {};
@@ -0,0 +1,21 @@
1
+ import { type Observable, Subject } from "rxjs";
2
+ import { type QuerxOptions } from "../types";
3
+ import { type QueryResult } from "./types";
4
+ type Query<T> = (() => Promise<T>) | (() => Observable<T>) | Observable<T>;
5
+ export declare const createClient: () => {
6
+ query$: <T>({ key, fn$, options$ }: {
7
+ key: any[];
8
+ fn$: Observable<Query<T>>;
9
+ options$: Observable<QuerxOptions<T>>;
10
+ }) => {
11
+ query$: Observable<QueryResult<T>>;
12
+ refetch$: Subject<void>;
13
+ enabled$: Observable<boolean>;
14
+ };
15
+ refetch$: Subject<{
16
+ key: any[];
17
+ }>;
18
+ queryStore: Map<any, any>;
19
+ destroy: () => void;
20
+ };
21
+ export {};
@@ -0,0 +1,5 @@
1
+ import { type Observable } from "rxjs";
2
+ import { type QuerxOptions } from "../types";
3
+ import { type QueryResult } from "./types";
4
+ export declare const notifyQueryResult: <T>(options$: Observable<QuerxOptions<T>>) => (stream$: Observable<Partial<QueryResult<T>>>) => Observable<Partial<QueryResult<T>>>;
5
+ export declare const mergeResults: <T>(stream$: Observable<Partial<QueryResult<T>>>) => Observable<QueryResult<T>>;
@@ -0,0 +1,7 @@
1
+ export interface QueryResult<T> {
2
+ data: {
3
+ result: T;
4
+ } | undefined;
5
+ isLoading: boolean;
6
+ error: unknown;
7
+ }
@@ -1,4 +1,4 @@
1
1
  import { type Observable } from "rxjs";
2
- export declare const autoRefetch: <T>(options?: {
2
+ export declare const autoRefetch: <T>(options$?: Observable<{
3
3
  staleTime?: number;
4
- }) => (source: Observable<T>) => Observable<T>;
4
+ }>) => (source: Observable<T>) => Observable<T>;
@@ -1,2 +1,2 @@
1
1
  import { type Signal } from "./signal";
2
- export declare const useSignal: <S, R>(signal: Signal<S, R>) => readonly [S | R, (stateOrUpdater: typeof import("./constants").SIGNAL_RESET | S | ((prev: S) => S)) => void];
2
+ export declare const useSignal: <S>(signal: Signal<S, S>) => readonly [S, (stateOrUpdater: typeof import("./constants").SIGNAL_RESET | S | ((prev: S) => S)) => void];
@@ -1,2 +1,2 @@
1
1
  import { type Signal } from "./signal";
2
- export declare const useSignalValue: <S, R>(signal: Signal<S, R>, key?: string) => S | R;
2
+ export declare const useSignalValue: <S>(signal: Signal<S, S>, key?: string) => S;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reactjrx",
3
3
  "private": false,
4
- "version": "1.34.0",
4
+ "version": "1.35.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"
@@ -1,16 +0,0 @@
1
- import type { Observable } from "rxjs";
2
- export declare const withKeyComparison: <T extends {
3
- key: any[];
4
- }>(stream: Observable<T>) => Observable<(T & {
5
- previousKey: undefined;
6
- isUsingDifferentKey: boolean;
7
- } extends infer T_1 ? T_1 extends T & {
8
- previousKey: undefined;
9
- isUsingDifferentKey: boolean;
10
- } ? T_1 extends null | undefined ? never : T_1 : never : never) | (T & {
11
- previousKey: any[];
12
- isUsingDifferentKey: boolean;
13
- } extends infer T_2 ? T_2 extends T & {
14
- previousKey: any[];
15
- isUsingDifferentKey: boolean;
16
- } ? T_2 extends null | undefined ? never : T_2 : never : never)>;