@tanstack/angular-query-experimental 5.89.0 → 5.90.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.
@@ -5,4 +5,166 @@ import { CreateBaseQueryOptions } from './types.js';
5
5
  * @param optionsFn
6
6
  * @param Observer
7
7
  */
8
- export declare function createBaseQuery<TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey>(optionsFn: () => CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>, Observer: typeof QueryObserver): import('./signal-proxy.js').MapToSignals<QueryObserverResult<TData, TError>>;
8
+ export declare function createBaseQuery<TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey>(optionsFn: () => CreateBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>, Observer: typeof QueryObserver): import('./signal-proxy.js').MapToSignals<{
9
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
10
+ data: TData;
11
+ error: TError;
12
+ isError: true;
13
+ isPending: false;
14
+ isLoading: false;
15
+ isLoadingError: false;
16
+ isRefetchError: true;
17
+ isSuccess: false;
18
+ isPlaceholderData: false;
19
+ status: "error";
20
+ dataUpdatedAt: number;
21
+ errorUpdatedAt: number;
22
+ failureCount: number;
23
+ failureReason: TError | null;
24
+ errorUpdateCount: number;
25
+ isFetched: boolean;
26
+ isFetchedAfterMount: boolean;
27
+ isFetching: boolean;
28
+ isInitialLoading: boolean;
29
+ isPaused: boolean;
30
+ isRefetching: boolean;
31
+ isStale: boolean;
32
+ isEnabled: boolean;
33
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
34
+ promise: Promise<TData>;
35
+ } | {
36
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
37
+ data: TData;
38
+ error: null;
39
+ isError: false;
40
+ isPending: false;
41
+ isLoading: false;
42
+ isLoadingError: false;
43
+ isRefetchError: false;
44
+ isSuccess: true;
45
+ isPlaceholderData: false;
46
+ status: "success";
47
+ dataUpdatedAt: number;
48
+ errorUpdatedAt: number;
49
+ failureCount: number;
50
+ failureReason: TError | null;
51
+ errorUpdateCount: number;
52
+ isFetched: boolean;
53
+ isFetchedAfterMount: boolean;
54
+ isFetching: boolean;
55
+ isInitialLoading: boolean;
56
+ isPaused: boolean;
57
+ isRefetching: boolean;
58
+ isStale: boolean;
59
+ isEnabled: boolean;
60
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
61
+ promise: Promise<TData>;
62
+ } | {
63
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
64
+ data: undefined;
65
+ error: TError;
66
+ isError: true;
67
+ isPending: false;
68
+ isLoading: false;
69
+ isLoadingError: true;
70
+ isRefetchError: false;
71
+ isSuccess: false;
72
+ isPlaceholderData: false;
73
+ status: "error";
74
+ dataUpdatedAt: number;
75
+ errorUpdatedAt: number;
76
+ failureCount: number;
77
+ failureReason: TError | null;
78
+ errorUpdateCount: number;
79
+ isFetched: boolean;
80
+ isFetchedAfterMount: boolean;
81
+ isFetching: boolean;
82
+ isInitialLoading: boolean;
83
+ isPaused: boolean;
84
+ isRefetching: boolean;
85
+ isStale: boolean;
86
+ isEnabled: boolean;
87
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
88
+ promise: Promise<TData>;
89
+ } | {
90
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
91
+ data: undefined;
92
+ error: null;
93
+ isError: false;
94
+ isPending: true;
95
+ isLoading: true;
96
+ isLoadingError: false;
97
+ isRefetchError: false;
98
+ isSuccess: false;
99
+ isPlaceholderData: false;
100
+ status: "pending";
101
+ dataUpdatedAt: number;
102
+ errorUpdatedAt: number;
103
+ failureCount: number;
104
+ failureReason: TError | null;
105
+ errorUpdateCount: number;
106
+ isFetched: boolean;
107
+ isFetchedAfterMount: boolean;
108
+ isFetching: boolean;
109
+ isInitialLoading: boolean;
110
+ isPaused: boolean;
111
+ isRefetching: boolean;
112
+ isStale: boolean;
113
+ isEnabled: boolean;
114
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
115
+ promise: Promise<TData>;
116
+ } | {
117
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
118
+ data: undefined;
119
+ error: null;
120
+ isError: false;
121
+ isPending: true;
122
+ isLoadingError: false;
123
+ isRefetchError: false;
124
+ isSuccess: false;
125
+ isPlaceholderData: false;
126
+ status: "pending";
127
+ dataUpdatedAt: number;
128
+ errorUpdatedAt: number;
129
+ failureCount: number;
130
+ failureReason: TError | null;
131
+ errorUpdateCount: number;
132
+ isFetched: boolean;
133
+ isFetchedAfterMount: boolean;
134
+ isFetching: boolean;
135
+ isLoading: boolean;
136
+ isInitialLoading: boolean;
137
+ isPaused: boolean;
138
+ isRefetching: boolean;
139
+ isStale: boolean;
140
+ isEnabled: boolean;
141
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
142
+ promise: Promise<TData>;
143
+ } | {
144
+ refetch: (options?: import('@tanstack/query-core').RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
145
+ data: TData;
146
+ isError: false;
147
+ error: null;
148
+ isPending: false;
149
+ isLoading: false;
150
+ isLoadingError: false;
151
+ isRefetchError: false;
152
+ isSuccess: true;
153
+ isPlaceholderData: true;
154
+ status: "success";
155
+ dataUpdatedAt: number;
156
+ errorUpdatedAt: number;
157
+ failureCount: number;
158
+ failureReason: TError | null;
159
+ errorUpdateCount: number;
160
+ isFetched: boolean;
161
+ isFetchedAfterMount: boolean;
162
+ isFetching: boolean;
163
+ isInitialLoading: boolean;
164
+ isPaused: boolean;
165
+ isRefetching: boolean;
166
+ isStale: boolean;
167
+ isEnabled: boolean;
168
+ fetchStatus: import('@tanstack/query-core').FetchStatus;
169
+ promise: Promise<TData>;
170
+ }>;
@@ -2,8 +2,10 @@ import { inject, NgZone, computed, signal, effect, untracked, VERSION } from "@a
2
2
  import { QueryClient, notifyManager, shouldThrowError } from "@tanstack/query-core";
3
3
  import { signalProxy } from "./signal-proxy.mjs";
4
4
  import { injectIsRestoring } from "./inject-is-restoring.mjs";
5
+ import { PENDING_TASKS } from "./pending-tasks-compat.mjs";
5
6
  function createBaseQuery(optionsFn, Observer) {
6
7
  const ngZone = inject(NgZone);
8
+ const pendingTasks = inject(PENDING_TASKS);
7
9
  const queryClient = inject(QueryClient);
8
10
  const isRestoring = injectIsRestoring();
9
11
  const defaultedOptionsSignal = computed(() => {
@@ -40,11 +42,19 @@ function createBaseQuery(optionsFn, Observer) {
40
42
  );
41
43
  effect((onCleanup) => {
42
44
  const observer = observerSignal();
45
+ let pendingTaskRef = null;
43
46
  const unsubscribe = isRestoring() ? () => void 0 : untracked(
44
- () => ngZone.runOutsideAngular(
45
- () => observer.subscribe(
47
+ () => ngZone.runOutsideAngular(() => {
48
+ return observer.subscribe(
46
49
  notifyManager.batchCalls((state) => {
47
50
  ngZone.run(() => {
51
+ if (state.fetchStatus === "fetching" && !pendingTaskRef) {
52
+ pendingTaskRef = pendingTasks.add();
53
+ }
54
+ if (state.fetchStatus === "idle" && pendingTaskRef) {
55
+ pendingTaskRef();
56
+ pendingTaskRef = null;
57
+ }
48
58
  if (state.isError && !state.isFetching && shouldThrowError(observer.options.throwOnError, [
49
59
  state.error,
50
60
  observer.getCurrentQuery()
@@ -55,16 +65,31 @@ function createBaseQuery(optionsFn, Observer) {
55
65
  resultFromSubscriberSignal.set(state);
56
66
  });
57
67
  })
58
- )
59
- )
68
+ );
69
+ })
60
70
  );
61
- onCleanup(unsubscribe);
71
+ onCleanup(() => {
72
+ if (pendingTaskRef) {
73
+ pendingTaskRef();
74
+ pendingTaskRef = null;
75
+ }
76
+ unsubscribe();
77
+ });
62
78
  });
63
79
  return signalProxy(
64
80
  computed(() => {
65
81
  const subscriberResult = resultFromSubscriberSignal();
66
82
  const optimisticResult = optimisticResultSignal();
67
- return subscriberResult ?? optimisticResult;
83
+ const result = subscriberResult ?? optimisticResult;
84
+ const observer = observerSignal();
85
+ const originalRefetch = result.refetch;
86
+ return {
87
+ ...result,
88
+ refetch: (...args) => {
89
+ observer.setOptions(defaultedOptionsSignal());
90
+ return originalRefetch(...args);
91
+ }
92
+ };
68
93
  })
69
94
  );
70
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-base-query.mjs","sources":["../src/create-base-query.ts"],"sourcesContent":["import {\n NgZone,\n VERSION,\n computed,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core'\nimport {\n QueryClient,\n notifyManager,\n shouldThrowError,\n} from '@tanstack/query-core'\nimport { signalProxy } from './signal-proxy'\nimport { injectIsRestoring } from './inject-is-restoring'\nimport type {\n QueryKey,\n QueryObserver,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport type { CreateBaseQueryOptions } from './types'\n\n/**\n * Base implementation for `injectQuery` and `injectInfiniteQuery`.\n * @param optionsFn\n * @param Observer\n */\nexport function createBaseQuery<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey extends QueryKey,\n>(\n optionsFn: () => CreateBaseQueryOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n Observer: typeof QueryObserver,\n) {\n const ngZone = inject(NgZone)\n const queryClient = inject(QueryClient)\n const isRestoring = injectIsRestoring()\n\n /**\n * Signal that has the default options from query client applied\n * computed() is used so signals can be inserted into the options\n * making it reactive. Wrapping options in a function ensures embedded expressions\n * are preserved and can keep being applied after signal changes\n */\n const defaultedOptionsSignal = computed(() => {\n const defaultedOptions = queryClient.defaultQueryOptions(optionsFn())\n defaultedOptions._optimisticResults = isRestoring()\n ? 'isRestoring'\n : 'optimistic'\n return defaultedOptions\n })\n\n const observerSignal = (() => {\n let instance: QueryObserver<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n > | null = null\n\n return computed(() => {\n return (instance ||= new Observer(queryClient, defaultedOptionsSignal()))\n })\n })()\n\n const optimisticResultSignal = computed(() =>\n observerSignal().getOptimisticResult(defaultedOptionsSignal()),\n )\n\n const resultFromSubscriberSignal = signal<QueryObserverResult<\n TData,\n TError\n > | null>(null)\n\n effect(\n (onCleanup) => {\n const observer = observerSignal()\n const defaultedOptions = defaultedOptionsSignal()\n\n untracked(() => {\n observer.setOptions(defaultedOptions)\n })\n onCleanup(() => {\n ngZone.run(() => resultFromSubscriberSignal.set(null))\n })\n },\n {\n // Set allowSignalWrites to support Angular < v19\n // Set to undefined to avoid warning on newer versions\n allowSignalWrites: VERSION.major < '19' || undefined,\n },\n )\n\n effect((onCleanup) => {\n // observer.trackResult is not used as this optimization is not needed for Angular\n const observer = observerSignal()\n const unsubscribe = isRestoring()\n ? () => undefined\n : untracked(() =>\n ngZone.runOutsideAngular(() =>\n observer.subscribe(\n notifyManager.batchCalls((state) => {\n ngZone.run(() => {\n if (\n state.isError &&\n !state.isFetching &&\n shouldThrowError(observer.options.throwOnError, [\n state.error,\n observer.getCurrentQuery(),\n ])\n ) {\n ngZone.onError.emit(state.error)\n throw state.error\n }\n resultFromSubscriberSignal.set(state)\n })\n }),\n ),\n ),\n )\n onCleanup(unsubscribe)\n })\n\n return signalProxy(\n computed(() => {\n const subscriberResult = resultFromSubscriberSignal()\n const optimisticResult = optimisticResultSignal()\n return subscriberResult ?? optimisticResult\n }),\n )\n}\n"],"names":[],"mappings":";;;;AA4BgB,SAAA,gBAOd,WAOA,UACA;AACM,QAAA,SAAS,OAAO,MAAM;AACtB,QAAA,cAAc,OAAO,WAAW;AACtC,QAAM,cAAc,kBAAkB;AAQhC,QAAA,yBAAyB,SAAS,MAAM;AAC5C,UAAM,mBAAmB,YAAY,oBAAoB,UAAA,CAAW;AACnD,qBAAA,qBAAqB,YAAY,IAC9C,gBACA;AACG,WAAA;AAAA,EAAA,CACR;AAED,QAAM,kBAAkB,MAAM;AAC5B,QAAI,WAMO;AAEX,WAAO,SAAS,MAAM;AACpB,aAAQ,wBAAa,IAAI,SAAS,aAAa,wBAAwB;AAAA,IAAA,CACxE;AAAA,EAAA,GACA;AAEH,QAAM,yBAAyB;AAAA,IAAS,MACtC,eAAA,EAAiB,oBAAoB,uBAAwB,CAAA;AAAA,EAC/D;AAEM,QAAA,6BAA6B,OAGzB,IAAI;AAEd;AAAA,IACE,CAAC,cAAc;AACb,YAAM,WAAW,eAAe;AAChC,YAAM,mBAAmB,uBAAuB;AAEhD,gBAAU,MAAM;AACd,iBAAS,WAAW,gBAAgB;AAAA,MAAA,CACrC;AACD,gBAAU,MAAM;AACd,eAAO,IAAI,MAAM,2BAA2B,IAAI,IAAI,CAAC;AAAA,MAAA,CACtD;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA,MAGE,mBAAmB,QAAQ,QAAQ,QAAQ;AAAA,IAAA;AAAA,EAE/C;AAEA,SAAO,CAAC,cAAc;AAEpB,UAAM,WAAW,eAAe;AAChC,UAAM,cAAc,gBAChB,MAAM,SACN;AAAA,MAAU,MACR,OAAO;AAAA,QAAkB,MACvB,SAAS;AAAA,UACP,cAAc,WAAW,CAAC,UAAU;AAClC,mBAAO,IAAI,MAAM;AAEb,kBAAA,MAAM,WACN,CAAC,MAAM,cACP,iBAAiB,SAAS,QAAQ,cAAc;AAAA,gBAC9C,MAAM;AAAA,gBACN,SAAS,gBAAgB;AAAA,cAAA,CAC1B,GACD;AACO,uBAAA,QAAQ,KAAK,MAAM,KAAK;AAC/B,sBAAM,MAAM;AAAA,cAAA;AAEd,yCAA2B,IAAI,KAAK;AAAA,YAAA,CACrC;AAAA,UACF,CAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAEJ;AACJ,cAAU,WAAW;AAAA,EAAA,CACtB;AAEM,SAAA;AAAA,IACL,SAAS,MAAM;AACb,YAAM,mBAAmB,2BAA2B;AACpD,YAAM,mBAAmB,uBAAuB;AAChD,aAAO,oBAAoB;AAAA,IAC5B,CAAA;AAAA,EACH;AACF;"}
1
+ {"version":3,"file":"create-base-query.mjs","sources":["../src/create-base-query.ts"],"sourcesContent":["import {\n NgZone,\n VERSION,\n computed,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core'\nimport {\n QueryClient,\n notifyManager,\n shouldThrowError,\n} from '@tanstack/query-core'\nimport { signalProxy } from './signal-proxy'\nimport { injectIsRestoring } from './inject-is-restoring'\nimport { PENDING_TASKS } from './pending-tasks-compat'\nimport type { PendingTaskRef } from './pending-tasks-compat'\nimport type {\n QueryKey,\n QueryObserver,\n QueryObserverResult,\n} from '@tanstack/query-core'\nimport type { CreateBaseQueryOptions } from './types'\n\n/**\n * Base implementation for `injectQuery` and `injectInfiniteQuery`.\n * @param optionsFn\n * @param Observer\n */\nexport function createBaseQuery<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey extends QueryKey,\n>(\n optionsFn: () => CreateBaseQueryOptions<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n >,\n Observer: typeof QueryObserver,\n) {\n const ngZone = inject(NgZone)\n const pendingTasks = inject(PENDING_TASKS)\n const queryClient = inject(QueryClient)\n const isRestoring = injectIsRestoring()\n\n /**\n * Signal that has the default options from query client applied\n * computed() is used so signals can be inserted into the options\n * making it reactive. Wrapping options in a function ensures embedded expressions\n * are preserved and can keep being applied after signal changes\n */\n const defaultedOptionsSignal = computed(() => {\n const defaultedOptions = queryClient.defaultQueryOptions(optionsFn())\n defaultedOptions._optimisticResults = isRestoring()\n ? 'isRestoring'\n : 'optimistic'\n return defaultedOptions\n })\n\n const observerSignal = (() => {\n let instance: QueryObserver<\n TQueryFnData,\n TError,\n TData,\n TQueryData,\n TQueryKey\n > | null = null\n\n return computed(() => {\n return (instance ||= new Observer(queryClient, defaultedOptionsSignal()))\n })\n })()\n\n const optimisticResultSignal = computed(() =>\n observerSignal().getOptimisticResult(defaultedOptionsSignal()),\n )\n\n const resultFromSubscriberSignal = signal<QueryObserverResult<\n TData,\n TError\n > | null>(null)\n\n effect(\n (onCleanup) => {\n const observer = observerSignal()\n const defaultedOptions = defaultedOptionsSignal()\n\n untracked(() => {\n observer.setOptions(defaultedOptions)\n })\n onCleanup(() => {\n ngZone.run(() => resultFromSubscriberSignal.set(null))\n })\n },\n {\n // Set allowSignalWrites to support Angular < v19\n // Set to undefined to avoid warning on newer versions\n allowSignalWrites: VERSION.major < '19' || undefined,\n },\n )\n\n effect((onCleanup) => {\n // observer.trackResult is not used as this optimization is not needed for Angular\n const observer = observerSignal()\n let pendingTaskRef: PendingTaskRef | null = null\n\n const unsubscribe = isRestoring()\n ? () => undefined\n : untracked(() =>\n ngZone.runOutsideAngular(() => {\n return observer.subscribe(\n notifyManager.batchCalls((state) => {\n ngZone.run(() => {\n if (state.fetchStatus === 'fetching' && !pendingTaskRef) {\n pendingTaskRef = pendingTasks.add()\n }\n\n if (state.fetchStatus === 'idle' && pendingTaskRef) {\n pendingTaskRef()\n pendingTaskRef = null\n }\n\n if (\n state.isError &&\n !state.isFetching &&\n shouldThrowError(observer.options.throwOnError, [\n state.error,\n observer.getCurrentQuery(),\n ])\n ) {\n ngZone.onError.emit(state.error)\n throw state.error\n }\n resultFromSubscriberSignal.set(state)\n })\n }),\n )\n }),\n )\n\n onCleanup(() => {\n if (pendingTaskRef) {\n pendingTaskRef()\n pendingTaskRef = null\n }\n unsubscribe()\n })\n })\n\n return signalProxy(\n computed(() => {\n const subscriberResult = resultFromSubscriberSignal()\n const optimisticResult = optimisticResultSignal()\n const result = subscriberResult ?? optimisticResult\n\n // Wrap methods to ensure observer has latest options before execution\n const observer = observerSignal()\n\n const originalRefetch = result.refetch\n return {\n ...result,\n refetch: ((...args: Parameters<typeof originalRefetch>) => {\n observer.setOptions(defaultedOptionsSignal())\n return originalRefetch(...args)\n }) as typeof originalRefetch,\n }\n }),\n )\n}\n"],"names":[],"mappings":";;;;;AA8BgB,SAAA,gBAOd,WAOA,UACA;AACM,QAAA,SAAS,OAAO,MAAM;AACtB,QAAA,eAAe,OAAO,aAAa;AACnC,QAAA,cAAc,OAAO,WAAW;AACtC,QAAM,cAAc,kBAAkB;AAQhC,QAAA,yBAAyB,SAAS,MAAM;AAC5C,UAAM,mBAAmB,YAAY,oBAAoB,UAAA,CAAW;AACnD,qBAAA,qBAAqB,YAAY,IAC9C,gBACA;AACG,WAAA;AAAA,EAAA,CACR;AAED,QAAM,kBAAkB,MAAM;AAC5B,QAAI,WAMO;AAEX,WAAO,SAAS,MAAM;AACpB,aAAQ,wBAAa,IAAI,SAAS,aAAa,wBAAwB;AAAA,IAAA,CACxE;AAAA,EAAA,GACA;AAEH,QAAM,yBAAyB;AAAA,IAAS,MACtC,eAAA,EAAiB,oBAAoB,uBAAwB,CAAA;AAAA,EAC/D;AAEM,QAAA,6BAA6B,OAGzB,IAAI;AAEd;AAAA,IACE,CAAC,cAAc;AACb,YAAM,WAAW,eAAe;AAChC,YAAM,mBAAmB,uBAAuB;AAEhD,gBAAU,MAAM;AACd,iBAAS,WAAW,gBAAgB;AAAA,MAAA,CACrC;AACD,gBAAU,MAAM;AACd,eAAO,IAAI,MAAM,2BAA2B,IAAI,IAAI,CAAC;AAAA,MAAA,CACtD;AAAA,IACH;AAAA,IACA;AAAA;AAAA;AAAA,MAGE,mBAAmB,QAAQ,QAAQ,QAAQ;AAAA,IAAA;AAAA,EAE/C;AAEA,SAAO,CAAC,cAAc;AAEpB,UAAM,WAAW,eAAe;AAChC,QAAI,iBAAwC;AAE5C,UAAM,cAAc,gBAChB,MAAM,SACN;AAAA,MAAU,MACR,OAAO,kBAAkB,MAAM;AAC7B,eAAO,SAAS;AAAA,UACd,cAAc,WAAW,CAAC,UAAU;AAClC,mBAAO,IAAI,MAAM;AACf,kBAAI,MAAM,gBAAgB,cAAc,CAAC,gBAAgB;AACvD,iCAAiB,aAAa,IAAI;AAAA,cAAA;AAGhC,kBAAA,MAAM,gBAAgB,UAAU,gBAAgB;AACnC,+BAAA;AACE,iCAAA;AAAA,cAAA;AAIjB,kBAAA,MAAM,WACN,CAAC,MAAM,cACP,iBAAiB,SAAS,QAAQ,cAAc;AAAA,gBAC9C,MAAM;AAAA,gBACN,SAAS,gBAAgB;AAAA,cAAA,CAC1B,GACD;AACO,uBAAA,QAAQ,KAAK,MAAM,KAAK;AAC/B,sBAAM,MAAM;AAAA,cAAA;AAEd,yCAA2B,IAAI,KAAK;AAAA,YAAA,CACrC;AAAA,UACF,CAAA;AAAA,QACH;AAAA,MACD,CAAA;AAAA,IACH;AAEJ,cAAU,MAAM;AACd,UAAI,gBAAgB;AACH,uBAAA;AACE,yBAAA;AAAA,MAAA;AAEP,kBAAA;AAAA,IAAA,CACb;AAAA,EAAA,CACF;AAEM,SAAA;AAAA,IACL,SAAS,MAAM;AACb,YAAM,mBAAmB,2BAA2B;AACpD,YAAM,mBAAmB,uBAAuB;AAChD,YAAM,SAAS,oBAAoB;AAGnC,YAAM,WAAW,eAAe;AAEhC,YAAM,kBAAkB,OAAO;AACxB,aAAA;AAAA,QACL,GAAG;AAAA,QACH,SAAU,IAAI,SAA6C;AAChD,mBAAA,WAAW,wBAAwB;AACrC,iBAAA,gBAAgB,GAAG,IAAI;AAAA,QAAA;AAAA,MAElC;AAAA,IACD,CAAA;AAAA,EACH;AACF;"}
@@ -16,6 +16,5 @@ export interface InjectIsFetchingOptions {
16
16
  * @param filters - The filters to apply to the query.
17
17
  * @param options - Additional configuration
18
18
  * @returns signal with number of loading or fetching queries.
19
- * @public
20
19
  */
21
20
  export declare function injectIsFetching(filters?: QueryFilters, options?: InjectIsFetchingOptions): Signal<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"inject-is-fetching.mjs","sources":["../src/inject-is-fetching.ts"],"sourcesContent":["import {\n DestroyRef,\n Injector,\n NgZone,\n assertInInjectionContext,\n inject,\n signal,\n} from '@angular/core'\nimport { QueryClient, notifyManager } from '@tanstack/query-core'\nimport type { QueryFilters } from '@tanstack/query-core'\nimport type { Signal } from '@angular/core'\n\nexport interface InjectIsFetchingOptions {\n /**\n * The `Injector` in which to create the isFetching signal.\n *\n * If this is not provided, the current injection context will be used instead (via `inject`).\n */\n injector?: Injector\n}\n\n/**\n * Injects a signal that tracks the number of queries that your application is loading or\n * fetching in the background.\n *\n * Can be used for app-wide loading indicators\n * @param filters - The filters to apply to the query.\n * @param options - Additional configuration\n * @returns signal with number of loading or fetching queries.\n * @public\n */\nexport function injectIsFetching(\n filters?: QueryFilters,\n options?: InjectIsFetchingOptions,\n): Signal<number> {\n !options?.injector && assertInInjectionContext(injectIsFetching)\n const injector = options?.injector ?? inject(Injector)\n const destroyRef = injector.get(DestroyRef)\n const ngZone = injector.get(NgZone)\n const queryClient = injector.get(QueryClient)\n\n const cache = queryClient.getQueryCache()\n // isFetching is the prev value initialized on mount *\n let isFetching = queryClient.isFetching(filters)\n\n const result = signal(isFetching)\n\n const unsubscribe = ngZone.runOutsideAngular(() =>\n cache.subscribe(\n notifyManager.batchCalls(() => {\n const newIsFetching = queryClient.isFetching(filters)\n if (isFetching !== newIsFetching) {\n // * and update with each change\n isFetching = newIsFetching\n ngZone.run(() => {\n result.set(isFetching)\n })\n }\n }),\n ),\n )\n\n destroyRef.onDestroy(unsubscribe)\n\n return result\n}\n"],"names":[],"mappings":";;AA+BgB,SAAA,iBACd,SACA,SACgB;AACf,IAAA,mCAAS,aAAY,yBAAyB,gBAAgB;AAC/D,QAAM,YAAW,mCAAS,aAAY,OAAO,QAAQ;AAC/C,QAAA,aAAa,SAAS,IAAI,UAAU;AACpC,QAAA,SAAS,SAAS,IAAI,MAAM;AAC5B,QAAA,cAAc,SAAS,IAAI,WAAW;AAEtC,QAAA,QAAQ,YAAY,cAAc;AAEpC,MAAA,aAAa,YAAY,WAAW,OAAO;AAEzC,QAAA,SAAS,OAAO,UAAU;AAEhC,QAAM,cAAc,OAAO;AAAA,IAAkB,MAC3C,MAAM;AAAA,MACJ,cAAc,WAAW,MAAM;AACvB,cAAA,gBAAgB,YAAY,WAAW,OAAO;AACpD,YAAI,eAAe,eAAe;AAEnB,uBAAA;AACb,iBAAO,IAAI,MAAM;AACf,mBAAO,IAAI,UAAU;AAAA,UAAA,CACtB;AAAA,QAAA;AAAA,MAEJ,CAAA;AAAA,IAAA;AAAA,EAEL;AAEA,aAAW,UAAU,WAAW;AAEzB,SAAA;AACT;"}
1
+ {"version":3,"file":"inject-is-fetching.mjs","sources":["../src/inject-is-fetching.ts"],"sourcesContent":["import {\n DestroyRef,\n Injector,\n NgZone,\n assertInInjectionContext,\n inject,\n signal,\n} from '@angular/core'\nimport { QueryClient, notifyManager } from '@tanstack/query-core'\nimport type { QueryFilters } from '@tanstack/query-core'\nimport type { Signal } from '@angular/core'\n\nexport interface InjectIsFetchingOptions {\n /**\n * The `Injector` in which to create the isFetching signal.\n *\n * If this is not provided, the current injection context will be used instead (via `inject`).\n */\n injector?: Injector\n}\n\n/**\n * Injects a signal that tracks the number of queries that your application is loading or\n * fetching in the background.\n *\n * Can be used for app-wide loading indicators\n * @param filters - The filters to apply to the query.\n * @param options - Additional configuration\n * @returns signal with number of loading or fetching queries.\n */\nexport function injectIsFetching(\n filters?: QueryFilters,\n options?: InjectIsFetchingOptions,\n): Signal<number> {\n !options?.injector && assertInInjectionContext(injectIsFetching)\n const injector = options?.injector ?? inject(Injector)\n const destroyRef = injector.get(DestroyRef)\n const ngZone = injector.get(NgZone)\n const queryClient = injector.get(QueryClient)\n\n const cache = queryClient.getQueryCache()\n // isFetching is the prev value initialized on mount *\n let isFetching = queryClient.isFetching(filters)\n\n const result = signal(isFetching)\n\n const unsubscribe = ngZone.runOutsideAngular(() =>\n cache.subscribe(\n notifyManager.batchCalls(() => {\n const newIsFetching = queryClient.isFetching(filters)\n if (isFetching !== newIsFetching) {\n // * and update with each change\n isFetching = newIsFetching\n ngZone.run(() => {\n result.set(isFetching)\n })\n }\n }),\n ),\n )\n\n destroyRef.onDestroy(unsubscribe)\n\n return result\n}\n"],"names":[],"mappings":";;AA8BgB,SAAA,iBACd,SACA,SACgB;AACf,IAAA,mCAAS,aAAY,yBAAyB,gBAAgB;AAC/D,QAAM,YAAW,mCAAS,aAAY,OAAO,QAAQ;AAC/C,QAAA,aAAa,SAAS,IAAI,UAAU;AACpC,QAAA,SAAS,SAAS,IAAI,MAAM;AAC5B,QAAA,cAAc,SAAS,IAAI,WAAW;AAEtC,QAAA,QAAQ,YAAY,cAAc;AAEpC,MAAA,aAAa,YAAY,WAAW,OAAO;AAEzC,QAAA,SAAS,OAAO,UAAU;AAEhC,QAAM,cAAc,OAAO;AAAA,IAAkB,MAC3C,MAAM;AAAA,MACJ,cAAc,WAAW,MAAM;AACvB,cAAA,gBAAgB,YAAY,WAAW,OAAO;AACpD,YAAI,eAAe,eAAe;AAEnB,uBAAA;AACb,iBAAO,IAAI,MAAM;AACf,mBAAO,IAAI,UAAU;AAAA,UAAA,CACtB;AAAA,QAAA;AAAA,MAEJ,CAAA;AAAA,IAAA;AAAA,EAEL;AAEA,aAAW,UAAU,WAAW;AAEzB,SAAA;AACT;"}
@@ -1,11 +1,12 @@
1
- import { assertInInjectionContext, inject, Injector, DestroyRef, NgZone, computed, signal, effect, untracked } from "@angular/core";
1
+ import { assertInInjectionContext, inject, Injector, NgZone, computed, signal, effect, untracked } from "@angular/core";
2
2
  import { QueryClient, MutationObserver, noop, notifyManager, shouldThrowError } from "@tanstack/query-core";
3
3
  import { signalProxy } from "./signal-proxy.mjs";
4
+ import { PENDING_TASKS } from "./pending-tasks-compat.mjs";
4
5
  function injectMutation(injectMutationFn, options) {
5
6
  !(options == null ? void 0 : options.injector) && assertInInjectionContext(injectMutation);
6
7
  const injector = (options == null ? void 0 : options.injector) ?? inject(Injector);
7
- const destroyRef = injector.get(DestroyRef);
8
8
  const ngZone = injector.get(NgZone);
9
+ const pendingTasks = injector.get(PENDING_TASKS);
9
10
  const queryClient = injector.get(QueryClient);
10
11
  const optionsSignal = computed(injectMutationFn);
11
12
  const observerSignal = (() => {
@@ -38,13 +39,21 @@ function injectMutation(injectMutationFn, options) {
38
39
  }
39
40
  );
40
41
  effect(
41
- () => {
42
+ (onCleanup) => {
42
43
  const observer = observerSignal();
44
+ let pendingTaskRef = null;
43
45
  untracked(() => {
44
46
  const unsubscribe = ngZone.runOutsideAngular(
45
47
  () => observer.subscribe(
46
48
  notifyManager.batchCalls((state) => {
47
49
  ngZone.run(() => {
50
+ if (state.isPending && !pendingTaskRef) {
51
+ pendingTaskRef = pendingTasks.add();
52
+ }
53
+ if (!state.isPending && pendingTaskRef) {
54
+ pendingTaskRef();
55
+ pendingTaskRef = null;
56
+ }
48
57
  if (state.isError && shouldThrowError(observer.options.throwOnError, [state.error])) {
49
58
  ngZone.onError.emit(state.error);
50
59
  throw state.error;
@@ -54,7 +63,13 @@ function injectMutation(injectMutationFn, options) {
54
63
  })
55
64
  )
56
65
  );
57
- destroyRef.onDestroy(unsubscribe);
66
+ onCleanup(() => {
67
+ if (pendingTaskRef) {
68
+ pendingTaskRef();
69
+ pendingTaskRef = null;
70
+ }
71
+ unsubscribe();
72
+ });
58
73
  });
59
74
  },
60
75
  {
@@ -1 +1 @@
1
- {"version":3,"file":"inject-mutation.mjs","sources":["../src/inject-mutation.ts"],"sourcesContent":["import {\n DestroyRef,\n Injector,\n NgZone,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core'\nimport {\n MutationObserver,\n QueryClient,\n noop,\n notifyManager,\n shouldThrowError,\n} from '@tanstack/query-core'\nimport { signalProxy } from './signal-proxy'\nimport type { DefaultError, MutationObserverResult } from '@tanstack/query-core'\nimport type {\n CreateMutateFunction,\n CreateMutationOptions,\n CreateMutationResult,\n} from './types'\n\nexport interface InjectMutationOptions {\n /**\n * The `Injector` in which to create the mutation.\n *\n * If this is not provided, the current injection context will be used instead (via `inject`).\n */\n injector?: Injector\n}\n\n/**\n * Injects a mutation: an imperative function that can be invoked which typically performs server side effects.\n *\n * Unlike queries, mutations are not run automatically.\n * @param injectMutationFn - A function that returns mutation options.\n * @param options - Additional configuration\n * @returns The mutation.\n */\nexport function injectMutation<\n TData = unknown,\n TError = DefaultError,\n TVariables = void,\n TOnMutateResult = unknown,\n>(\n injectMutationFn: () => CreateMutationOptions<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n >,\n options?: InjectMutationOptions,\n): CreateMutationResult<TData, TError, TVariables, TOnMutateResult> {\n !options?.injector && assertInInjectionContext(injectMutation)\n const injector = options?.injector ?? inject(Injector)\n const destroyRef = injector.get(DestroyRef)\n const ngZone = injector.get(NgZone)\n const queryClient = injector.get(QueryClient)\n\n /**\n * computed() is used so signals can be inserted into the options\n * making it reactive. Wrapping options in a function ensures embedded expressions\n * are preserved and can keep being applied after signal changes\n */\n const optionsSignal = computed(injectMutationFn)\n\n const observerSignal = (() => {\n let instance: MutationObserver<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n > | null = null\n\n return computed(() => {\n return (instance ||= new MutationObserver(queryClient, optionsSignal()))\n })\n })()\n\n const mutateFnSignal = computed<\n CreateMutateFunction<TData, TError, TVariables, TOnMutateResult>\n >(() => {\n const observer = observerSignal()\n return (variables, mutateOptions) => {\n observer.mutate(variables, mutateOptions).catch(noop)\n }\n })\n\n /**\n * Computed signal that gets result from mutation cache based on passed options\n */\n const resultFromInitialOptionsSignal = computed(() => {\n const observer = observerSignal()\n return observer.getCurrentResult()\n })\n\n /**\n * Signal that contains result set by subscriber\n */\n const resultFromSubscriberSignal = signal<MutationObserverResult<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n > | null>(null)\n\n effect(\n () => {\n const observer = observerSignal()\n const observerOptions = optionsSignal()\n\n untracked(() => {\n observer.setOptions(observerOptions)\n })\n },\n {\n injector,\n },\n )\n\n effect(\n () => {\n // observer.trackResult is not used as this optimization is not needed for Angular\n const observer = observerSignal()\n\n untracked(() => {\n const unsubscribe = ngZone.runOutsideAngular(() =>\n observer.subscribe(\n notifyManager.batchCalls((state) => {\n ngZone.run(() => {\n if (\n state.isError &&\n shouldThrowError(observer.options.throwOnError, [state.error])\n ) {\n ngZone.onError.emit(state.error)\n throw state.error\n }\n\n resultFromSubscriberSignal.set(state)\n })\n }),\n ),\n )\n destroyRef.onDestroy(unsubscribe)\n })\n },\n {\n injector,\n },\n )\n\n const resultSignal = computed(() => {\n const resultFromSubscriber = resultFromSubscriberSignal()\n const resultFromInitialOptions = resultFromInitialOptionsSignal()\n\n const result = resultFromSubscriber ?? resultFromInitialOptions\n\n return {\n ...result,\n mutate: mutateFnSignal(),\n mutateAsync: result.mutate,\n }\n })\n\n return signalProxy(resultSignal) as CreateMutationResult<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n >\n}\n"],"names":[],"mappings":";;;AA2CgB,SAAA,eAMd,kBAMA,SACkE;AACjE,IAAA,mCAAS,aAAY,yBAAyB,cAAc;AAC7D,QAAM,YAAW,mCAAS,aAAY,OAAO,QAAQ;AAC/C,QAAA,aAAa,SAAS,IAAI,UAAU;AACpC,QAAA,SAAS,SAAS,IAAI,MAAM;AAC5B,QAAA,cAAc,SAAS,IAAI,WAAW;AAOtC,QAAA,gBAAgB,SAAS,gBAAgB;AAE/C,QAAM,kBAAkB,MAAM;AAC5B,QAAI,WAKO;AAEX,WAAO,SAAS,MAAM;AACpB,aAAQ,wBAAa,IAAI,iBAAiB,aAAa,eAAe;AAAA,IAAA,CACvE;AAAA,EAAA,GACA;AAEG,QAAA,iBAAiB,SAErB,MAAM;AACN,UAAM,WAAW,eAAe;AACzB,WAAA,CAAC,WAAW,kBAAkB;AACnC,eAAS,OAAO,WAAW,aAAa,EAAE,MAAM,IAAI;AAAA,IACtD;AAAA,EAAA,CACD;AAKK,QAAA,iCAAiC,SAAS,MAAM;AACpD,UAAM,WAAW,eAAe;AAChC,WAAO,SAAS,iBAAiB;AAAA,EAAA,CAClC;AAKK,QAAA,6BAA6B,OAKzB,IAAI;AAEd;AAAA,IACE,MAAM;AACJ,YAAM,WAAW,eAAe;AAChC,YAAM,kBAAkB,cAAc;AAEtC,gBAAU,MAAM;AACd,iBAAS,WAAW,eAAe;AAAA,MAAA,CACpC;AAAA,IACH;AAAA,IACA;AAAA,MACE;AAAA,IAAA;AAAA,EAEJ;AAEA;AAAA,IACE,MAAM;AAEJ,YAAM,WAAW,eAAe;AAEhC,gBAAU,MAAM;AACd,cAAM,cAAc,OAAO;AAAA,UAAkB,MAC3C,SAAS;AAAA,YACP,cAAc,WAAW,CAAC,UAAU;AAClC,qBAAO,IAAI,MAAM;AAEb,oBAAA,MAAM,WACN,iBAAiB,SAAS,QAAQ,cAAc,CAAC,MAAM,KAAK,CAAC,GAC7D;AACO,yBAAA,QAAQ,KAAK,MAAM,KAAK;AAC/B,wBAAM,MAAM;AAAA,gBAAA;AAGd,2CAA2B,IAAI,KAAK;AAAA,cAAA,CACrC;AAAA,YACF,CAAA;AAAA,UAAA;AAAA,QAEL;AACA,mBAAW,UAAU,WAAW;AAAA,MAAA,CACjC;AAAA,IACH;AAAA,IACA;AAAA,MACE;AAAA,IAAA;AAAA,EAEJ;AAEM,QAAA,eAAe,SAAS,MAAM;AAClC,UAAM,uBAAuB,2BAA2B;AACxD,UAAM,2BAA2B,+BAA+B;AAEhE,UAAM,SAAS,wBAAwB;AAEhC,WAAA;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,eAAe;AAAA,MACvB,aAAa,OAAO;AAAA,IACtB;AAAA,EAAA,CACD;AAED,SAAO,YAAY,YAAY;AAMjC;"}
1
+ {"version":3,"file":"inject-mutation.mjs","sources":["../src/inject-mutation.ts"],"sourcesContent":["import {\n Injector,\n NgZone,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n signal,\n untracked,\n} from '@angular/core'\nimport {\n MutationObserver,\n QueryClient,\n noop,\n notifyManager,\n shouldThrowError,\n} from '@tanstack/query-core'\nimport { signalProxy } from './signal-proxy'\nimport { PENDING_TASKS } from './pending-tasks-compat'\nimport type { PendingTaskRef } from './pending-tasks-compat'\nimport type { DefaultError, MutationObserverResult } from '@tanstack/query-core'\nimport type {\n CreateMutateFunction,\n CreateMutationOptions,\n CreateMutationResult,\n} from './types'\n\nexport interface InjectMutationOptions {\n /**\n * The `Injector` in which to create the mutation.\n *\n * If this is not provided, the current injection context will be used instead (via `inject`).\n */\n injector?: Injector\n}\n\n/**\n * Injects a mutation: an imperative function that can be invoked which typically performs server side effects.\n *\n * Unlike queries, mutations are not run automatically.\n * @param injectMutationFn - A function that returns mutation options.\n * @param options - Additional configuration\n * @returns The mutation.\n */\nexport function injectMutation<\n TData = unknown,\n TError = DefaultError,\n TVariables = void,\n TOnMutateResult = unknown,\n>(\n injectMutationFn: () => CreateMutationOptions<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n >,\n options?: InjectMutationOptions,\n): CreateMutationResult<TData, TError, TVariables, TOnMutateResult> {\n !options?.injector && assertInInjectionContext(injectMutation)\n const injector = options?.injector ?? inject(Injector)\n const ngZone = injector.get(NgZone)\n const pendingTasks = injector.get(PENDING_TASKS)\n const queryClient = injector.get(QueryClient)\n\n /**\n * computed() is used so signals can be inserted into the options\n * making it reactive. Wrapping options in a function ensures embedded expressions\n * are preserved and can keep being applied after signal changes\n */\n const optionsSignal = computed(injectMutationFn)\n\n const observerSignal = (() => {\n let instance: MutationObserver<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n > | null = null\n\n return computed(() => {\n return (instance ||= new MutationObserver(queryClient, optionsSignal()))\n })\n })()\n\n const mutateFnSignal = computed<\n CreateMutateFunction<TData, TError, TVariables, TOnMutateResult>\n >(() => {\n const observer = observerSignal()\n return (variables, mutateOptions) => {\n observer.mutate(variables, mutateOptions).catch(noop)\n }\n })\n\n /**\n * Computed signal that gets result from mutation cache based on passed options\n */\n const resultFromInitialOptionsSignal = computed(() => {\n const observer = observerSignal()\n return observer.getCurrentResult()\n })\n\n /**\n * Signal that contains result set by subscriber\n */\n const resultFromSubscriberSignal = signal<MutationObserverResult<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n > | null>(null)\n\n effect(\n () => {\n const observer = observerSignal()\n const observerOptions = optionsSignal()\n\n untracked(() => {\n observer.setOptions(observerOptions)\n })\n },\n {\n injector,\n },\n )\n\n effect(\n (onCleanup) => {\n // observer.trackResult is not used as this optimization is not needed for Angular\n const observer = observerSignal()\n let pendingTaskRef: PendingTaskRef | null = null\n\n untracked(() => {\n const unsubscribe = ngZone.runOutsideAngular(() =>\n observer.subscribe(\n notifyManager.batchCalls((state) => {\n ngZone.run(() => {\n // Track pending task when mutation is pending\n if (state.isPending && !pendingTaskRef) {\n pendingTaskRef = pendingTasks.add()\n }\n\n // Clear pending task when mutation is no longer pending\n if (!state.isPending && pendingTaskRef) {\n pendingTaskRef()\n pendingTaskRef = null\n }\n\n if (\n state.isError &&\n shouldThrowError(observer.options.throwOnError, [state.error])\n ) {\n ngZone.onError.emit(state.error)\n throw state.error\n }\n\n resultFromSubscriberSignal.set(state)\n })\n }),\n ),\n )\n onCleanup(() => {\n // Clean up any pending task on destroy\n if (pendingTaskRef) {\n pendingTaskRef()\n pendingTaskRef = null\n }\n unsubscribe()\n })\n })\n },\n {\n injector,\n },\n )\n\n const resultSignal = computed(() => {\n const resultFromSubscriber = resultFromSubscriberSignal()\n const resultFromInitialOptions = resultFromInitialOptionsSignal()\n\n const result = resultFromSubscriber ?? resultFromInitialOptions\n\n return {\n ...result,\n mutate: mutateFnSignal(),\n mutateAsync: result.mutate,\n }\n })\n\n return signalProxy(resultSignal) as CreateMutationResult<\n TData,\n TError,\n TVariables,\n TOnMutateResult\n >\n}\n"],"names":[],"mappings":";;;;AA4CgB,SAAA,eAMd,kBAMA,SACkE;AACjE,IAAA,mCAAS,aAAY,yBAAyB,cAAc;AAC7D,QAAM,YAAW,mCAAS,aAAY,OAAO,QAAQ;AAC/C,QAAA,SAAS,SAAS,IAAI,MAAM;AAC5B,QAAA,eAAe,SAAS,IAAI,aAAa;AACzC,QAAA,cAAc,SAAS,IAAI,WAAW;AAOtC,QAAA,gBAAgB,SAAS,gBAAgB;AAE/C,QAAM,kBAAkB,MAAM;AAC5B,QAAI,WAKO;AAEX,WAAO,SAAS,MAAM;AACpB,aAAQ,wBAAa,IAAI,iBAAiB,aAAa,eAAe;AAAA,IAAA,CACvE;AAAA,EAAA,GACA;AAEG,QAAA,iBAAiB,SAErB,MAAM;AACN,UAAM,WAAW,eAAe;AACzB,WAAA,CAAC,WAAW,kBAAkB;AACnC,eAAS,OAAO,WAAW,aAAa,EAAE,MAAM,IAAI;AAAA,IACtD;AAAA,EAAA,CACD;AAKK,QAAA,iCAAiC,SAAS,MAAM;AACpD,UAAM,WAAW,eAAe;AAChC,WAAO,SAAS,iBAAiB;AAAA,EAAA,CAClC;AAKK,QAAA,6BAA6B,OAKzB,IAAI;AAEd;AAAA,IACE,MAAM;AACJ,YAAM,WAAW,eAAe;AAChC,YAAM,kBAAkB,cAAc;AAEtC,gBAAU,MAAM;AACd,iBAAS,WAAW,eAAe;AAAA,MAAA,CACpC;AAAA,IACH;AAAA,IACA;AAAA,MACE;AAAA,IAAA;AAAA,EAEJ;AAEA;AAAA,IACE,CAAC,cAAc;AAEb,YAAM,WAAW,eAAe;AAChC,UAAI,iBAAwC;AAE5C,gBAAU,MAAM;AACd,cAAM,cAAc,OAAO;AAAA,UAAkB,MAC3C,SAAS;AAAA,YACP,cAAc,WAAW,CAAC,UAAU;AAClC,qBAAO,IAAI,MAAM;AAEX,oBAAA,MAAM,aAAa,CAAC,gBAAgB;AACtC,mCAAiB,aAAa,IAAI;AAAA,gBAAA;AAIhC,oBAAA,CAAC,MAAM,aAAa,gBAAgB;AACvB,iCAAA;AACE,mCAAA;AAAA,gBAAA;AAIjB,oBAAA,MAAM,WACN,iBAAiB,SAAS,QAAQ,cAAc,CAAC,MAAM,KAAK,CAAC,GAC7D;AACO,yBAAA,QAAQ,KAAK,MAAM,KAAK;AAC/B,wBAAM,MAAM;AAAA,gBAAA;AAGd,2CAA2B,IAAI,KAAK;AAAA,cAAA,CACrC;AAAA,YACF,CAAA;AAAA,UAAA;AAAA,QAEL;AACA,kBAAU,MAAM;AAEd,cAAI,gBAAgB;AACH,2BAAA;AACE,6BAAA;AAAA,UAAA;AAEP,sBAAA;AAAA,QAAA,CACb;AAAA,MAAA,CACF;AAAA,IACH;AAAA,IACA;AAAA,MACE;AAAA,IAAA;AAAA,EAEJ;AAEM,QAAA,eAAe,SAAS,MAAM;AAClC,UAAM,uBAAuB,2BAA2B;AACxD,UAAM,2BAA2B,+BAA+B;AAEhE,UAAM,SAAS,wBAAwB;AAEhC,WAAA;AAAA,MACL,GAAG;AAAA,MACH,QAAQ,eAAe;AAAA,MACvB,aAAa,OAAO;AAAA,IACtB;AAAA,EAAA,CACD;AAED,SAAO,YAAY,YAAY;AAMjC;"}
@@ -1,11 +1,12 @@
1
1
  import { Injector, Signal } from '@angular/core';
2
- import { DefaultError, OmitKeyof, QueriesPlaceholderDataFunction, QueryFunction, QueryKey, QueryObserverOptions, QueryObserverResult, ThrowOnError } from '@tanstack/query-core';
3
- type QueryObserverOptionsForCreateQueries<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey> = OmitKeyof<QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>, 'placeholderData'> & {
2
+ import { DefaultError, OmitKeyof, QueriesPlaceholderDataFunction, QueryFunction, QueryKey, ThrowOnError } from '@tanstack/query-core';
3
+ import { CreateQueryOptions, CreateQueryResult, DefinedCreateQueryResult } from './types.js';
4
+ type QueryObserverOptionsForCreateQueries<TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey> = OmitKeyof<CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'placeholderData'> & {
4
5
  placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>;
5
6
  };
6
7
  type MAXIMUM_DEPTH = 20;
7
- type SkipTokenForUseQueries = symbol;
8
- type GetOptions<T> = T extends {
8
+ type SkipTokenForCreateQueries = symbol;
9
+ type GetCreateQueryOptionsForCreateQueries<T> = T extends {
9
10
  queryFnData: infer TQueryFnData;
10
11
  error?: infer TError;
11
12
  data: infer TData;
@@ -16,33 +17,36 @@ type GetOptions<T> = T extends {
16
17
  data: infer TData;
17
18
  error?: infer TError;
18
19
  } ? QueryObserverOptionsForCreateQueries<unknown, TError, TData> : T extends [infer TQueryFnData, infer TError, infer TData] ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData> : T extends [infer TQueryFnData, infer TError] ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError> : T extends [infer TQueryFnData] ? QueryObserverOptionsForCreateQueries<TQueryFnData> : T extends {
19
- queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey> | SkipTokenForUseQueries;
20
- select: (data: any) => infer TData;
20
+ queryFn?: QueryFunction<infer TQueryFnData, infer TQueryKey> | SkipTokenForCreateQueries;
21
+ select?: (data: any) => infer TData;
21
22
  throwOnError?: ThrowOnError<any, infer TError, any, any>;
22
23
  } ? QueryObserverOptionsForCreateQueries<TQueryFnData, unknown extends TError ? DefaultError : TError, unknown extends TData ? TQueryFnData : TData, TQueryKey> : QueryObserverOptionsForCreateQueries;
23
- type GetResults<T> = T extends {
24
+ type GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {
25
+ initialData?: infer TInitialData;
26
+ } ? unknown extends TInitialData ? CreateQueryResult<TData, TError> : TInitialData extends TData ? DefinedCreateQueryResult<TData, TError> : TInitialData extends () => infer TInitialDataResult ? unknown extends TInitialDataResult ? CreateQueryResult<TData, TError> : TInitialDataResult extends TData ? DefinedCreateQueryResult<TData, TError> : CreateQueryResult<TData, TError> : CreateQueryResult<TData, TError> : CreateQueryResult<TData, TError>;
27
+ type GetCreateQueryResult<T> = T extends {
24
28
  queryFnData: any;
25
29
  error?: infer TError;
26
30
  data: infer TData;
27
- } ? QueryObserverResult<TData, TError> : T extends {
31
+ } ? GetDefinedOrUndefinedQueryResult<T, TData, TError> : T extends {
28
32
  queryFnData: infer TQueryFnData;
29
33
  error?: infer TError;
30
- } ? QueryObserverResult<TQueryFnData, TError> : T extends {
34
+ } ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError> : T extends {
31
35
  data: infer TData;
32
36
  error?: infer TError;
33
- } ? QueryObserverResult<TData, TError> : T extends [any, infer TError, infer TData] ? QueryObserverResult<TData, TError> : T extends [infer TQueryFnData, infer TError] ? QueryObserverResult<TQueryFnData, TError> : T extends [infer TQueryFnData] ? QueryObserverResult<TQueryFnData> : T extends {
34
- queryFn?: QueryFunction<infer TQueryFnData, any> | SkipTokenForUseQueries;
35
- select: (data: any) => infer TData;
37
+ } ? GetDefinedOrUndefinedQueryResult<T, TData, TError> : T extends [any, infer TError, infer TData] ? GetDefinedOrUndefinedQueryResult<T, TData, TError> : T extends [infer TQueryFnData, infer TError] ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError> : T extends [infer TQueryFnData] ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData> : T extends {
38
+ queryFn?: QueryFunction<infer TQueryFnData, any> | SkipTokenForCreateQueries;
39
+ select?: (data: any) => infer TData;
36
40
  throwOnError?: ThrowOnError<any, infer TError, any, any>;
37
- } ? QueryObserverResult<unknown extends TData ? TQueryFnData : TData, unknown extends TError ? DefaultError : TError> : QueryObserverResult;
41
+ } ? GetDefinedOrUndefinedQueryResult<T, unknown extends TData ? TQueryFnData : TData, unknown extends TError ? DefaultError : TError> : CreateQueryResult;
38
42
  /**
39
43
  * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param
40
44
  */
41
- export type QueriesOptions<T extends Array<any>, TResult extends Array<any> = [], TDepth extends ReadonlyArray<number> = []> = TDepth['length'] extends MAXIMUM_DEPTH ? Array<QueryObserverOptionsForCreateQueries> : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetOptions<Head>] : T extends [infer Head, ...infer Tail] ? QueriesOptions<[
42
- ...Tail
45
+ export type QueriesOptions<T extends Array<any>, TResults extends Array<any> = [], TDepth extends ReadonlyArray<number> = []> = TDepth['length'] extends MAXIMUM_DEPTH ? Array<QueryObserverOptionsForCreateQueries> : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueries<Head>] : T extends [infer Head, ...infer Tails] ? QueriesOptions<[
46
+ ...Tails
43
47
  ], [
44
- ...TResult,
45
- GetOptions<Head>
48
+ ...TResults,
49
+ GetCreateQueryOptionsForCreateQueries<Head>
46
50
  ], [
47
51
  ...TDepth,
48
52
  1
@@ -50,23 +54,28 @@ export type QueriesOptions<T extends Array<any>, TResult extends Array<any> = []
50
54
  /**
51
55
  * QueriesResults reducer recursively maps type param to results
52
56
  */
53
- export type QueriesResults<T extends Array<any>, TResult extends Array<any> = [], TDepth extends ReadonlyArray<number> = []> = TDepth['length'] extends MAXIMUM_DEPTH ? Array<QueryObserverResult> : T extends [] ? [] : T extends [infer Head] ? [...TResult, GetResults<Head>] : T extends [infer Head, ...infer Tail] ? QueriesResults<[
54
- ...Tail
57
+ export type QueriesResults<T extends Array<any>, TResults extends Array<any> = [], TDepth extends ReadonlyArray<number> = []> = TDepth['length'] extends MAXIMUM_DEPTH ? Array<CreateQueryResult> : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult<Head>] : T extends [infer Head, ...infer Tails] ? QueriesResults<[
58
+ ...Tails
55
59
  ], [
56
- ...TResult,
57
- GetResults<Head>
60
+ ...TResults,
61
+ GetCreateQueryResult<Head>
58
62
  ], [
59
63
  ...TDepth,
60
64
  1
61
- ]> : T extends Array<QueryObserverOptionsForCreateQueries<infer TQueryFnData, infer TError, infer TData, any>> ? Array<QueryObserverResult<unknown extends TData ? TQueryFnData : TData, unknown extends TError ? DefaultError : TError>> : Array<QueryObserverResult>;
65
+ ]> : {
66
+ [K in keyof T]: GetCreateQueryResult<T[K]>;
67
+ };
68
+ export interface InjectQueriesOptions<T extends Array<any>, TCombinedResult = QueriesResults<T>> {
69
+ queries: readonly [...QueriesOptions<T>] | readonly [
70
+ ...{
71
+ [K in keyof T]: GetCreateQueryOptionsForCreateQueries<T[K]>;
72
+ }
73
+ ];
74
+ combine?: (result: QueriesResults<T>) => TCombinedResult;
75
+ }
62
76
  /**
63
- * @param root0
64
- * @param root0.queries
65
- * @param root0.combine
66
- * @param injector
77
+ * @param optionsFn - A function that returns queries' options.
78
+ * @param injector - The Angular injector to use.
67
79
  */
68
- export declare function injectQueries<T extends Array<any>, TCombinedResult = QueriesResults<T>>({ queries, ...options }: {
69
- queries: Signal<[...QueriesOptions<T>]>;
70
- combine?: (result: QueriesResults<T>) => TCombinedResult;
71
- }, injector?: Injector): Signal<TCombinedResult>;
80
+ export declare function injectQueries<T extends Array<any>, TCombinedResult = QueriesResults<T>>(optionsFn: () => InjectQueriesOptions<T, TCombinedResult>, injector?: Injector): Signal<TCombinedResult>;
72
81
  export {};
@@ -1,46 +1,79 @@
1
1
  import { QueryClient, QueriesObserver, notifyManager } from "@tanstack/query-core";
2
- import { assertInInjectionContext, runInInjectionContext, inject, Injector, DestroyRef, NgZone, computed, effect, signal } from "@angular/core";
2
+ import { assertInInjectionContext, runInInjectionContext, inject, Injector, DestroyRef, NgZone, computed, effect, signal, untracked } from "@angular/core";
3
+ import { signalProxy } from "./signal-proxy.mjs";
3
4
  import { injectIsRestoring } from "./inject-is-restoring.mjs";
4
- function injectQueries({
5
- queries,
6
- ...options
7
- }, injector) {
5
+ function injectQueries(optionsFn, injector) {
8
6
  !injector && assertInInjectionContext(injectQueries);
9
7
  return runInInjectionContext(injector ?? inject(Injector), () => {
10
8
  const destroyRef = inject(DestroyRef);
11
9
  const ngZone = inject(NgZone);
12
10
  const queryClient = inject(QueryClient);
13
11
  const isRestoring = injectIsRestoring();
12
+ const optionsSignal = computed(() => {
13
+ return optionsFn();
14
+ });
14
15
  const defaultedQueries = computed(() => {
15
- return queries().map((opts) => {
16
- const defaultedOptions = queryClient.defaultQueryOptions(opts);
16
+ return optionsSignal().queries.map((opts) => {
17
+ const defaultedOptions = queryClient.defaultQueryOptions(
18
+ opts
19
+ );
17
20
  defaultedOptions._optimisticResults = isRestoring() ? "isRestoring" : "optimistic";
18
21
  return defaultedOptions;
19
22
  });
20
23
  });
21
- const observer = new QueriesObserver(
22
- queryClient,
23
- defaultedQueries(),
24
- options
24
+ const observerSignal = (() => {
25
+ let instance = null;
26
+ return computed(() => {
27
+ return instance || (instance = new QueriesObserver(
28
+ queryClient,
29
+ defaultedQueries(),
30
+ optionsSignal()
31
+ ));
32
+ });
33
+ })();
34
+ const optimisticResultSignal = computed(
35
+ () => observerSignal().getOptimisticResult(
36
+ defaultedQueries(),
37
+ optionsSignal().combine
38
+ )
25
39
  );
26
40
  effect(() => {
27
- observer.setQueries(
41
+ observerSignal().setQueries(
28
42
  defaultedQueries(),
29
- options
43
+ optionsSignal()
30
44
  );
31
45
  });
32
- const [, getCombinedResult] = observer.getOptimisticResult(
33
- defaultedQueries(),
34
- options.combine
35
- );
36
- const result = signal(getCombinedResult());
46
+ const optimisticCombinedResultSignal = computed(() => {
47
+ const [_optimisticResult, getCombinedResult, trackResult] = optimisticResultSignal();
48
+ return getCombinedResult(trackResult());
49
+ });
50
+ const resultFromSubscriberSignal = signal(null);
37
51
  effect(() => {
38
- const unsubscribe = isRestoring() ? () => void 0 : ngZone.runOutsideAngular(
39
- () => observer.subscribe(notifyManager.batchCalls(result.set))
52
+ const observer = observerSignal();
53
+ const [_optimisticResult, getCombinedResult] = optimisticResultSignal();
54
+ untracked(() => {
55
+ const unsubscribe = isRestoring() ? () => void 0 : ngZone.runOutsideAngular(
56
+ () => observer.subscribe(
57
+ notifyManager.batchCalls((state) => {
58
+ resultFromSubscriberSignal.set(getCombinedResult(state));
59
+ })
60
+ )
61
+ );
62
+ destroyRef.onDestroy(unsubscribe);
63
+ });
64
+ });
65
+ const resultSignal = computed(() => {
66
+ const subscriberResult = resultFromSubscriberSignal();
67
+ const optimisticResult = optimisticCombinedResultSignal();
68
+ return subscriberResult ?? optimisticResult;
69
+ });
70
+ return computed(() => {
71
+ const result = resultSignal();
72
+ const { combine } = optionsSignal();
73
+ return combine ? result : result.map(
74
+ (query) => signalProxy(signal(query))
40
75
  );
41
- destroyRef.onDestroy(unsubscribe);
42
76
  });
43
- return result;
44
77
  });
45
78
  }
46
79
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"inject-queries.mjs","sources":["../src/inject-queries.ts"],"sourcesContent":["import {\n QueriesObserver,\n QueryClient,\n notifyManager,\n} from '@tanstack/query-core'\nimport {\n DestroyRef,\n Injector,\n NgZone,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n runInInjectionContext,\n signal,\n} from '@angular/core'\nimport { injectIsRestoring } from './inject-is-restoring'\nimport type { Signal } from '@angular/core'\nimport type {\n DefaultError,\n OmitKeyof,\n QueriesObserverOptions,\n QueriesPlaceholderDataFunction,\n QueryFunction,\n QueryKey,\n QueryObserverOptions,\n QueryObserverResult,\n ThrowOnError,\n} from '@tanstack/query-core'\n\n// This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.\n// `placeholderData` function does not have a parameter\ntype QueryObserverOptionsForCreateQueries<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n> = OmitKeyof<\n QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>,\n 'placeholderData'\n> & {\n placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>\n}\n\n// Avoid TS depth-limit error in case of large array literal\ntype MAXIMUM_DEPTH = 20\n\n// Widen the type of the symbol to enable type inference even if skipToken is not immutable.\ntype SkipTokenForUseQueries = symbol\n\ntype GetOptions<T> =\n // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }\n T extends {\n queryFnData: infer TQueryFnData\n error?: infer TError\n data: infer TData\n }\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>\n : T extends { queryFnData: infer TQueryFnData; error?: infer TError }\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>\n : T extends { data: infer TData; error?: infer TError }\n ? QueryObserverOptionsForCreateQueries<unknown, TError, TData>\n : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]\n T extends [infer TQueryFnData, infer TError, infer TData]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>\n : T extends [infer TQueryFnData, infer TError]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>\n : T extends [infer TQueryFnData]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData>\n : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided\n T extends {\n queryFn?:\n | QueryFunction<infer TQueryFnData, infer TQueryKey>\n | SkipTokenForUseQueries\n select: (data: any) => infer TData\n throwOnError?: ThrowOnError<any, infer TError, any, any>\n }\n ? QueryObserverOptionsForCreateQueries<\n TQueryFnData,\n unknown extends TError ? DefaultError : TError,\n unknown extends TData ? TQueryFnData : TData,\n TQueryKey\n >\n : // Fallback\n QueryObserverOptionsForCreateQueries\n\ntype GetResults<T> =\n // Part 1: responsible for mapping explicit type parameter to function result, if object\n T extends { queryFnData: any; error?: infer TError; data: infer TData }\n ? QueryObserverResult<TData, TError>\n : T extends { queryFnData: infer TQueryFnData; error?: infer TError }\n ? QueryObserverResult<TQueryFnData, TError>\n : T extends { data: infer TData; error?: infer TError }\n ? QueryObserverResult<TData, TError>\n : // Part 2: responsible for mapping explicit type parameter to function result, if tuple\n T extends [any, infer TError, infer TData]\n ? QueryObserverResult<TData, TError>\n : T extends [infer TQueryFnData, infer TError]\n ? QueryObserverResult<TQueryFnData, TError>\n : T extends [infer TQueryFnData]\n ? QueryObserverResult<TQueryFnData>\n : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided\n T extends {\n queryFn?:\n | QueryFunction<infer TQueryFnData, any>\n | SkipTokenForUseQueries\n select: (data: any) => infer TData\n throwOnError?: ThrowOnError<any, infer TError, any, any>\n }\n ? QueryObserverResult<\n unknown extends TData ? TQueryFnData : TData,\n unknown extends TError ? DefaultError : TError\n >\n : // Fallback\n QueryObserverResult\n\n/**\n * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param\n */\nexport type QueriesOptions<\n T extends Array<any>,\n TResult extends Array<any> = [],\n TDepth extends ReadonlyArray<number> = [],\n> = TDepth['length'] extends MAXIMUM_DEPTH\n ? Array<QueryObserverOptionsForCreateQueries>\n : T extends []\n ? []\n : T extends [infer Head]\n ? [...TResult, GetOptions<Head>]\n : T extends [infer Head, ...infer Tail]\n ? QueriesOptions<\n [...Tail],\n [...TResult, GetOptions<Head>],\n [...TDepth, 1]\n >\n : ReadonlyArray<unknown> extends T\n ? T\n : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!\n // use this to infer the param types in the case of Array.map() argument\n T extends Array<\n QueryObserverOptionsForCreateQueries<\n infer TQueryFnData,\n infer TError,\n infer TData,\n infer TQueryKey\n >\n >\n ? Array<\n QueryObserverOptionsForCreateQueries<\n TQueryFnData,\n TError,\n TData,\n TQueryKey\n >\n >\n : // Fallback\n Array<QueryObserverOptionsForCreateQueries>\n\n/**\n * QueriesResults reducer recursively maps type param to results\n */\nexport type QueriesResults<\n T extends Array<any>,\n TResult extends Array<any> = [],\n TDepth extends ReadonlyArray<number> = [],\n> = TDepth['length'] extends MAXIMUM_DEPTH\n ? Array<QueryObserverResult>\n : T extends []\n ? []\n : T extends [infer Head]\n ? [...TResult, GetResults<Head>]\n : T extends [infer Head, ...infer Tail]\n ? QueriesResults<\n [...Tail],\n [...TResult, GetResults<Head>],\n [...TDepth, 1]\n >\n : T extends Array<\n QueryObserverOptionsForCreateQueries<\n infer TQueryFnData,\n infer TError,\n infer TData,\n any\n >\n >\n ? // Dynamic-size (homogenous) CreateQueryOptions array: map directly to array of results\n Array<\n QueryObserverResult<\n unknown extends TData ? TQueryFnData : TData,\n unknown extends TError ? DefaultError : TError\n >\n >\n : // Fallback\n Array<QueryObserverResult>\n\n/**\n * @param root0\n * @param root0.queries\n * @param root0.combine\n * @param injector\n */\nexport function injectQueries<\n T extends Array<any>,\n TCombinedResult = QueriesResults<T>,\n>(\n {\n queries,\n ...options\n }: {\n queries: Signal<[...QueriesOptions<T>]>\n combine?: (result: QueriesResults<T>) => TCombinedResult\n },\n injector?: Injector,\n): Signal<TCombinedResult> {\n !injector && assertInInjectionContext(injectQueries)\n return runInInjectionContext(injector ?? inject(Injector), () => {\n const destroyRef = inject(DestroyRef)\n const ngZone = inject(NgZone)\n const queryClient = inject(QueryClient)\n const isRestoring = injectIsRestoring()\n\n const defaultedQueries = computed(() => {\n return queries().map((opts) => {\n const defaultedOptions = queryClient.defaultQueryOptions(opts)\n // Make sure the results are already in fetching state before subscribing or updating options\n defaultedOptions._optimisticResults = isRestoring()\n ? 'isRestoring'\n : 'optimistic'\n\n return defaultedOptions as QueryObserverOptions\n })\n })\n\n const observer = new QueriesObserver<TCombinedResult>(\n queryClient,\n defaultedQueries(),\n options as QueriesObserverOptions<TCombinedResult>,\n )\n\n // Do not notify on updates because of changes in the options because\n // these changes should already be reflected in the optimistic result.\n effect(() => {\n observer.setQueries(\n defaultedQueries(),\n options as QueriesObserverOptions<TCombinedResult>,\n )\n })\n\n const [, getCombinedResult] = observer.getOptimisticResult(\n defaultedQueries(),\n (options as QueriesObserverOptions<TCombinedResult>).combine,\n )\n\n const result = signal(getCombinedResult() as any)\n\n effect(() => {\n const unsubscribe = isRestoring()\n ? () => undefined\n : ngZone.runOutsideAngular(() =>\n observer.subscribe(notifyManager.batchCalls(result.set)),\n )\n destroyRef.onDestroy(unsubscribe)\n })\n\n return result\n })\n}\n"],"names":[],"mappings":";;;AAyMO,SAAS,cAId;AAAA,EACE;AAAA,EACA,GAAG;AACL,GAIA,UACyB;AACxB,GAAA,YAAY,yBAAyB,aAAa;AACnD,SAAO,sBAAsB,YAAY,OAAO,QAAQ,GAAG,MAAM;AACzD,UAAA,aAAa,OAAO,UAAU;AAC9B,UAAA,SAAS,OAAO,MAAM;AACtB,UAAA,cAAc,OAAO,WAAW;AACtC,UAAM,cAAc,kBAAkB;AAEhC,UAAA,mBAAmB,SAAS,MAAM;AACtC,aAAO,QAAQ,EAAE,IAAI,CAAC,SAAS;AACvB,cAAA,mBAAmB,YAAY,oBAAoB,IAAI;AAE5C,yBAAA,qBAAqB,YAAY,IAC9C,gBACA;AAEG,eAAA;AAAA,MAAA,CACR;AAAA,IAAA,CACF;AAED,UAAM,WAAW,IAAI;AAAA,MACnB;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,IACF;AAIA,WAAO,MAAM;AACF,eAAA;AAAA,QACP,iBAAiB;AAAA,QACjB;AAAA,MACF;AAAA,IAAA,CACD;AAED,UAAM,CAAG,EAAA,iBAAiB,IAAI,SAAS;AAAA,MACrC,iBAAiB;AAAA,MAChB,QAAoD;AAAA,IACvD;AAEM,UAAA,SAAS,OAAO,mBAA0B;AAEhD,WAAO,MAAM;AACX,YAAM,cAAc,YAAA,IAChB,MAAM,SACN,OAAO;AAAA,QAAkB,MACvB,SAAS,UAAU,cAAc,WAAW,OAAO,GAAG,CAAC;AAAA,MACzD;AACJ,iBAAW,UAAU,WAAW;AAAA,IAAA,CACjC;AAEM,WAAA;AAAA,EAAA,CACR;AACH;"}
1
+ {"version":3,"file":"inject-queries.mjs","sources":["../src/inject-queries.ts"],"sourcesContent":["import {\n QueriesObserver,\n QueryClient,\n notifyManager,\n} from '@tanstack/query-core'\nimport {\n DestroyRef,\n Injector,\n NgZone,\n assertInInjectionContext,\n computed,\n effect,\n inject,\n runInInjectionContext,\n signal,\n untracked,\n} from '@angular/core'\nimport { signalProxy } from './signal-proxy'\nimport { injectIsRestoring } from './inject-is-restoring'\nimport type {\n DefaultError,\n OmitKeyof,\n QueriesObserverOptions,\n QueriesPlaceholderDataFunction,\n QueryFunction,\n QueryKey,\n QueryObserverOptions,\n ThrowOnError,\n} from '@tanstack/query-core'\nimport type {\n CreateQueryOptions,\n CreateQueryResult,\n DefinedCreateQueryResult,\n} from './types'\nimport type { Signal } from '@angular/core'\n\n// This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`.\n// `placeholderData` function always gets undefined passed\ntype QueryObserverOptionsForCreateQueries<\n TQueryFnData = unknown,\n TError = DefaultError,\n TData = TQueryFnData,\n TQueryKey extends QueryKey = QueryKey,\n> = OmitKeyof<\n CreateQueryOptions<TQueryFnData, TError, TData, TQueryKey>,\n 'placeholderData'\n> & {\n placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction<TQueryFnData>\n}\n\n// Avoid TS depth-limit error in case of large array literal\ntype MAXIMUM_DEPTH = 20\n\n// Widen the type of the symbol to enable type inference even if skipToken is not immutable.\ntype SkipTokenForCreateQueries = symbol\n\ntype GetCreateQueryOptionsForCreateQueries<T> =\n // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData }\n T extends {\n queryFnData: infer TQueryFnData\n error?: infer TError\n data: infer TData\n }\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>\n : T extends { queryFnData: infer TQueryFnData; error?: infer TError }\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>\n : T extends { data: infer TData; error?: infer TError }\n ? QueryObserverOptionsForCreateQueries<unknown, TError, TData>\n : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData]\n T extends [infer TQueryFnData, infer TError, infer TData]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError, TData>\n : T extends [infer TQueryFnData, infer TError]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData, TError>\n : T extends [infer TQueryFnData]\n ? QueryObserverOptionsForCreateQueries<TQueryFnData>\n : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided\n T extends {\n queryFn?:\n | QueryFunction<infer TQueryFnData, infer TQueryKey>\n | SkipTokenForCreateQueries\n select?: (data: any) => infer TData\n throwOnError?: ThrowOnError<any, infer TError, any, any>\n }\n ? QueryObserverOptionsForCreateQueries<\n TQueryFnData,\n unknown extends TError ? DefaultError : TError,\n unknown extends TData ? TQueryFnData : TData,\n TQueryKey\n >\n : // Fallback\n QueryObserverOptionsForCreateQueries\n\n// A defined initialData setting should return a DefinedCreateQueryResult rather than CreateQueryResult\ntype GetDefinedOrUndefinedQueryResult<T, TData, TError = unknown> = T extends {\n initialData?: infer TInitialData\n}\n ? unknown extends TInitialData\n ? CreateQueryResult<TData, TError>\n : TInitialData extends TData\n ? DefinedCreateQueryResult<TData, TError>\n : TInitialData extends () => infer TInitialDataResult\n ? unknown extends TInitialDataResult\n ? CreateQueryResult<TData, TError>\n : TInitialDataResult extends TData\n ? DefinedCreateQueryResult<TData, TError>\n : CreateQueryResult<TData, TError>\n : CreateQueryResult<TData, TError>\n : CreateQueryResult<TData, TError>\n\ntype GetCreateQueryResult<T> =\n // Part 1: responsible for mapping explicit type parameter to function result, if object\n T extends { queryFnData: any; error?: infer TError; data: infer TData }\n ? GetDefinedOrUndefinedQueryResult<T, TData, TError>\n : T extends { queryFnData: infer TQueryFnData; error?: infer TError }\n ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>\n : T extends { data: infer TData; error?: infer TError }\n ? GetDefinedOrUndefinedQueryResult<T, TData, TError>\n : // Part 2: responsible for mapping explicit type parameter to function result, if tuple\n T extends [any, infer TError, infer TData]\n ? GetDefinedOrUndefinedQueryResult<T, TData, TError>\n : T extends [infer TQueryFnData, infer TError]\n ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData, TError>\n : T extends [infer TQueryFnData]\n ? GetDefinedOrUndefinedQueryResult<T, TQueryFnData>\n : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided\n T extends {\n queryFn?:\n | QueryFunction<infer TQueryFnData, any>\n | SkipTokenForCreateQueries\n select?: (data: any) => infer TData\n throwOnError?: ThrowOnError<any, infer TError, any, any>\n }\n ? GetDefinedOrUndefinedQueryResult<\n T,\n unknown extends TData ? TQueryFnData : TData,\n unknown extends TError ? DefaultError : TError\n >\n : // Fallback\n CreateQueryResult\n\n/**\n * QueriesOptions reducer recursively unwraps function arguments to infer/enforce type param\n */\nexport type QueriesOptions<\n T extends Array<any>,\n TResults extends Array<any> = [],\n TDepth extends ReadonlyArray<number> = [],\n> = TDepth['length'] extends MAXIMUM_DEPTH\n ? Array<QueryObserverOptionsForCreateQueries>\n : T extends []\n ? []\n : T extends [infer Head]\n ? [...TResults, GetCreateQueryOptionsForCreateQueries<Head>]\n : T extends [infer Head, ...infer Tails]\n ? QueriesOptions<\n [...Tails],\n [...TResults, GetCreateQueryOptionsForCreateQueries<Head>],\n [...TDepth, 1]\n >\n : ReadonlyArray<unknown> extends T\n ? T\n : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogenous type!\n // use this to infer the param types in the case of Array.map() argument\n T extends Array<\n QueryObserverOptionsForCreateQueries<\n infer TQueryFnData,\n infer TError,\n infer TData,\n infer TQueryKey\n >\n >\n ? Array<\n QueryObserverOptionsForCreateQueries<\n TQueryFnData,\n TError,\n TData,\n TQueryKey\n >\n >\n : // Fallback\n Array<QueryObserverOptionsForCreateQueries>\n\n/**\n * QueriesResults reducer recursively maps type param to results\n */\nexport type QueriesResults<\n T extends Array<any>,\n TResults extends Array<any> = [],\n TDepth extends ReadonlyArray<number> = [],\n> = TDepth['length'] extends MAXIMUM_DEPTH\n ? Array<CreateQueryResult>\n : T extends []\n ? []\n : T extends [infer Head]\n ? [...TResults, GetCreateQueryResult<Head>]\n : T extends [infer Head, ...infer Tails]\n ? QueriesResults<\n [...Tails],\n [...TResults, GetCreateQueryResult<Head>],\n [...TDepth, 1]\n >\n : { [K in keyof T]: GetCreateQueryResult<T[K]> }\n\nexport interface InjectQueriesOptions<\n T extends Array<any>,\n TCombinedResult = QueriesResults<T>,\n> {\n queries:\n | readonly [...QueriesOptions<T>]\n | readonly [\n ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueries<T[K]> },\n ]\n combine?: (result: QueriesResults<T>) => TCombinedResult\n}\n\n/**\n * @param optionsFn - A function that returns queries' options.\n * @param injector - The Angular injector to use.\n */\nexport function injectQueries<\n T extends Array<any>,\n TCombinedResult = QueriesResults<T>,\n>(\n optionsFn: () => InjectQueriesOptions<T, TCombinedResult>,\n injector?: Injector,\n): Signal<TCombinedResult> {\n !injector && assertInInjectionContext(injectQueries)\n return runInInjectionContext(injector ?? inject(Injector), () => {\n const destroyRef = inject(DestroyRef)\n const ngZone = inject(NgZone)\n const queryClient = inject(QueryClient)\n const isRestoring = injectIsRestoring()\n\n /**\n * Signal that has the default options from query client applied\n * computed() is used so signals can be inserted into the options\n * making it reactive. Wrapping options in a function ensures embedded expressions\n * are preserved and can keep being applied after signal changes\n */\n const optionsSignal = computed(() => {\n return optionsFn()\n })\n\n const defaultedQueries = computed(() => {\n return optionsSignal().queries.map((opts) => {\n const defaultedOptions = queryClient.defaultQueryOptions(\n opts as QueryObserverOptions,\n )\n // Make sure the results are already in fetching state before subscribing or updating options\n defaultedOptions._optimisticResults = isRestoring()\n ? 'isRestoring'\n : 'optimistic'\n\n return defaultedOptions as QueryObserverOptions\n })\n })\n\n const observerSignal = (() => {\n let instance: QueriesObserver<TCombinedResult> | null = null\n\n return computed(() => {\n return (instance ||= new QueriesObserver<TCombinedResult>(\n queryClient,\n defaultedQueries(),\n optionsSignal() as QueriesObserverOptions<TCombinedResult>,\n ))\n })\n })()\n\n const optimisticResultSignal = computed(() =>\n observerSignal().getOptimisticResult(\n defaultedQueries(),\n (optionsSignal() as QueriesObserverOptions<TCombinedResult>).combine,\n ),\n )\n\n // Do not notify on updates because of changes in the options because\n // these changes should already be reflected in the optimistic result.\n effect(() => {\n observerSignal().setQueries(\n defaultedQueries(),\n optionsSignal() as QueriesObserverOptions<TCombinedResult>,\n )\n })\n\n const optimisticCombinedResultSignal = computed(() => {\n const [_optimisticResult, getCombinedResult, trackResult] =\n optimisticResultSignal()\n return getCombinedResult(trackResult())\n })\n\n const resultFromSubscriberSignal = signal<TCombinedResult | null>(null)\n\n effect(() => {\n const observer = observerSignal()\n const [_optimisticResult, getCombinedResult] = optimisticResultSignal()\n\n untracked(() => {\n const unsubscribe = isRestoring()\n ? () => undefined\n : ngZone.runOutsideAngular(() =>\n observer.subscribe(\n notifyManager.batchCalls((state) => {\n resultFromSubscriberSignal.set(getCombinedResult(state))\n }),\n ),\n )\n\n destroyRef.onDestroy(unsubscribe)\n })\n })\n\n const resultSignal = computed(() => {\n const subscriberResult = resultFromSubscriberSignal()\n const optimisticResult = optimisticCombinedResultSignal()\n return subscriberResult ?? optimisticResult\n })\n\n return computed(() => {\n const result = resultSignal()\n const { combine } = optionsSignal()\n\n return combine\n ? result\n : (result as QueriesResults<T>).map((query) =>\n signalProxy(signal(query)),\n )\n })\n }) as unknown as Signal<TCombinedResult>\n}\n"],"names":[],"mappings":";;;;AA2NgB,SAAA,cAId,WACA,UACyB;AACxB,GAAA,YAAY,yBAAyB,aAAa;AACnD,SAAO,sBAAsB,YAAY,OAAO,QAAQ,GAAG,MAAM;AACzD,UAAA,aAAa,OAAO,UAAU;AAC9B,UAAA,SAAS,OAAO,MAAM;AACtB,UAAA,cAAc,OAAO,WAAW;AACtC,UAAM,cAAc,kBAAkB;AAQhC,UAAA,gBAAgB,SAAS,MAAM;AACnC,aAAO,UAAU;AAAA,IAAA,CAClB;AAEK,UAAA,mBAAmB,SAAS,MAAM;AACtC,aAAO,cAAc,EAAE,QAAQ,IAAI,CAAC,SAAS;AAC3C,cAAM,mBAAmB,YAAY;AAAA,UACnC;AAAA,QACF;AAEiB,yBAAA,qBAAqB,YAAY,IAC9C,gBACA;AAEG,eAAA;AAAA,MAAA,CACR;AAAA,IAAA,CACF;AAED,UAAM,kBAAkB,MAAM;AAC5B,UAAI,WAAoD;AAExD,aAAO,SAAS,MAAM;AACpB,eAAQ,wBAAa,IAAI;AAAA,UACvB;AAAA,UACA,iBAAiB;AAAA,UACjB,cAAc;AAAA,QAChB;AAAA,MAAA,CACD;AAAA,IAAA,GACA;AAEH,UAAM,yBAAyB;AAAA,MAAS,MACtC,eAAiB,EAAA;AAAA,QACf,iBAAiB;AAAA,QAChB,gBAA4D;AAAA,MAAA;AAAA,IAEjE;AAIA,WAAO,MAAM;AACX,qBAAiB,EAAA;AAAA,QACf,iBAAiB;AAAA,QACjB,cAAc;AAAA,MAChB;AAAA,IAAA,CACD;AAEK,UAAA,iCAAiC,SAAS,MAAM;AACpD,YAAM,CAAC,mBAAmB,mBAAmB,WAAW,IACtD,uBAAuB;AAClB,aAAA,kBAAkB,aAAa;AAAA,IAAA,CACvC;AAEK,UAAA,6BAA6B,OAA+B,IAAI;AAEtE,WAAO,MAAM;AACX,YAAM,WAAW,eAAe;AAChC,YAAM,CAAC,mBAAmB,iBAAiB,IAAI,uBAAuB;AAEtE,gBAAU,MAAM;AACd,cAAM,cAAc,YAAA,IAChB,MAAM,SACN,OAAO;AAAA,UAAkB,MACvB,SAAS;AAAA,YACP,cAAc,WAAW,CAAC,UAAU;AACP,yCAAA,IAAI,kBAAkB,KAAK,CAAC;AAAA,YACxD,CAAA;AAAA,UAAA;AAAA,QAEL;AAEJ,mBAAW,UAAU,WAAW;AAAA,MAAA,CACjC;AAAA,IAAA,CACF;AAEK,UAAA,eAAe,SAAS,MAAM;AAClC,YAAM,mBAAmB,2BAA2B;AACpD,YAAM,mBAAmB,+BAA+B;AACxD,aAAO,oBAAoB;AAAA,IAAA,CAC5B;AAED,WAAO,SAAS,MAAM;AACpB,YAAM,SAAS,aAAa;AACtB,YAAA,EAAE,QAAQ,IAAI,cAAc;AAE3B,aAAA,UACH,SACC,OAA6B;AAAA,QAAI,CAAC,UACjC,YAAY,OAAO,KAAK,CAAC;AAAA,MAC3B;AAAA,IAAA,CACL;AAAA,EAAA,CACF;AACH;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/angular-query-experimental",
3
- "version": "5.89.0",
3
+ "version": "5.90.0",
4
4
  "description": "Signals for managing, caching and syncing asynchronous and remote data in Angular",
5
5
  "author": "Arnoud de Vries",
6
6
  "license": "MIT",
@@ -0,0 +1,7 @@
1
+ import { InjectionToken } from '@angular/core';
2
+ type PendingTasksCompat = {
3
+ add: () => PendingTaskRef;
4
+ };
5
+ export type PendingTaskRef = () => void;
6
+ export declare const PENDING_TASKS: InjectionToken<PendingTasksCompat>;
7
+ export {};
@@ -0,0 +1,19 @@
1
+ import * as ng from "@angular/core";
2
+ import { InjectionToken, inject } from "@angular/core";
3
+ import { noop } from "@tanstack/query-core";
4
+ const PENDING_TASKS = new InjectionToken(
5
+ "PENDING_TASKS",
6
+ {
7
+ factory: () => {
8
+ const token = Reflect.get(ng, "PendingTasks");
9
+ const svc = token ? inject(token, { optional: true }) : null;
10
+ return {
11
+ add: svc ? () => svc.add() : () => noop
12
+ };
13
+ }
14
+ }
15
+ );
16
+ export {
17
+ PENDING_TASKS
18
+ };
19
+ //# sourceMappingURL=pending-tasks-compat.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pending-tasks-compat.mjs","sources":["../src/pending-tasks-compat.ts"],"sourcesContent":["import { InjectionToken, inject } from '@angular/core'\nimport * as ng from '@angular/core'\nimport { noop } from '@tanstack/query-core'\n\ntype PendingTasksCompat = { add: () => PendingTaskRef }\n\nexport type PendingTaskRef = () => void\n\nexport const PENDING_TASKS = new InjectionToken<PendingTasksCompat>(\n 'PENDING_TASKS',\n {\n factory: (): PendingTasksCompat => {\n // Access via Reflect so bundlers stay quiet when the token is absent (Angular < 19).\n const token = Reflect.get(ng, 'PendingTasks') as unknown as\n | Parameters<typeof inject>[0]\n | undefined\n\n const svc: PendingTasksCompat | null = token\n ? (inject(token, { optional: true }) as PendingTasksCompat | null)\n : null\n\n // Without PendingTasks we fall back to a stable no-op shim.\n return {\n add: svc ? () => svc.add() : () => noop,\n }\n },\n },\n)\n"],"names":[],"mappings":";;;AAQO,MAAM,gBAAgB,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,IACE,SAAS,MAA0B;AAEjC,YAAM,QAAQ,QAAQ,IAAI,IAAI,cAAc;AAItC,YAAA,MAAiC,QAClC,OAAO,OAAO,EAAE,UAAU,KAAM,CAAA,IACjC;AAGG,aAAA;AAAA,QACL,KAAK,MAAM,MAAM,IAAI,QAAQ,MAAM;AAAA,MACrC;AAAA,IAAA;AAAA,EACF;AAEJ;"}