@tanstack/angular-query-experimental
Version:
Signals for managing, caching and syncing asynchronous and remote data in Angular
572 lines (552 loc) • 17.7 kB
JavaScript
// src/index.ts
export * from "@tanstack/query-core";
// src/query-options.ts
function queryOptions(options) {
return options;
}
// src/mutation-options.ts
function mutationOptions(options) {
return options;
}
// src/infinite-query-options.ts
function infiniteQueryOptions(options) {
return options;
}
// src/inject-infinite-query.ts
import { InfiniteQueryObserver } from "@tanstack/query-core";
// src/create-base-query.ts
import {
DestroyRef,
NgZone,
VERSION,
computed as computed2,
effect,
inject,
signal,
untracked as untracked2
} from "@angular/core";
import { QueryClient, notifyManager } from "@tanstack/query-core";
// src/signal-proxy.ts
import { computed, untracked } from "@angular/core";
function signalProxy(inputSignal) {
const internalState = {};
return new Proxy(internalState, {
get(target, prop) {
const computedField = target[prop];
if (computedField) return computedField;
const targetField = untracked(inputSignal)[prop];
if (typeof targetField === "function") return targetField;
return target[prop] = computed(() => inputSignal()[prop]);
},
has(_, prop) {
return !!untracked(inputSignal)[prop];
},
ownKeys() {
return Reflect.ownKeys(untracked(inputSignal));
},
getOwnPropertyDescriptor() {
return {
enumerable: true,
configurable: true
};
}
});
}
// src/util/index.ts
function shouldThrowError(throwError, params) {
if (typeof throwError === "function") {
return throwError(...params);
}
return !!throwError;
}
function noop() {
}
// src/create-base-query.ts
function createBaseQuery(optionsFn, Observer) {
const ngZone = inject(NgZone);
const destroyRef = inject(DestroyRef);
const queryClient = inject(QueryClient);
const defaultedOptionsSignal = computed2(() => {
const defaultedOptions = queryClient.defaultQueryOptions(optionsFn());
defaultedOptions._optimisticResults = "optimistic";
return defaultedOptions;
});
const observerSignal = (() => {
let instance = null;
return computed2(() => {
return instance ||= new Observer(queryClient, defaultedOptionsSignal());
});
})();
const optimisticResultSignal = computed2(
() => observerSignal().getOptimisticResult(defaultedOptionsSignal())
);
const resultFromSubscriberSignal = signal(null);
effect(
(onCleanup) => {
const observer = observerSignal();
const defaultedOptions = defaultedOptionsSignal();
untracked2(() => {
observer.setOptions(defaultedOptions);
});
onCleanup(() => {
ngZone.run(() => resultFromSubscriberSignal.set(null));
});
},
{
// Set allowSignalWrites to support Angular < v19
// Set to undefined to avoid warning on newer versions
allowSignalWrites: VERSION.major < "19" || void 0
}
);
effect(() => {
const observer = observerSignal();
untracked2(() => {
const unsubscribe = ngZone.runOutsideAngular(
() => observer.subscribe(
notifyManager.batchCalls((state) => {
ngZone.run(() => {
if (state.isError && !state.isFetching && // !isRestoring() && // todo: enable when client persistence is implemented
shouldThrowError(observer.options.throwOnError, [
state.error,
observer.getCurrentQuery()
])) {
ngZone.onError.emit(state.error);
throw state.error;
}
resultFromSubscriberSignal.set(state);
});
})
)
);
destroyRef.onDestroy(unsubscribe);
});
});
return signalProxy(
computed2(() => {
const subscriberResult = resultFromSubscriberSignal();
const optimisticResult = optimisticResultSignal();
return subscriberResult ?? optimisticResult;
})
);
}
// src/util/assert-injector/assert-injector.ts
import {
Injector,
assertInInjectionContext,
inject as inject2,
runInInjectionContext
} from "@angular/core";
function assertInjector(fn, injector, runner) {
!injector && assertInInjectionContext(fn);
const assertedInjector = injector ?? inject2(Injector);
if (!runner) return assertedInjector;
return runInInjectionContext(assertedInjector, runner);
}
// src/inject-infinite-query.ts
function injectInfiniteQuery(optionsFn, injector) {
return assertInjector(
injectInfiniteQuery,
injector,
() => createBaseQuery(optionsFn, InfiniteQueryObserver)
);
}
// src/inject-is-fetching.ts
import { DestroyRef as DestroyRef2, NgZone as NgZone2, inject as inject3, signal as signal2 } from "@angular/core";
import { QueryClient as QueryClient2, notifyManager as notifyManager2 } from "@tanstack/query-core";
function injectIsFetching(filters, injector) {
return assertInjector(injectIsFetching, injector, () => {
const destroyRef = inject3(DestroyRef2);
const ngZone = inject3(NgZone2);
const queryClient = inject3(QueryClient2);
const cache = queryClient.getQueryCache();
let isFetching = queryClient.isFetching(filters);
const result = signal2(isFetching);
const unsubscribe = ngZone.runOutsideAngular(
() => cache.subscribe(
notifyManager2.batchCalls(() => {
const newIsFetching = queryClient.isFetching(filters);
if (isFetching !== newIsFetching) {
isFetching = newIsFetching;
ngZone.run(() => {
result.set(isFetching);
});
}
})
)
);
destroyRef.onDestroy(unsubscribe);
return result;
});
}
// src/inject-is-mutating.ts
import { DestroyRef as DestroyRef3, NgZone as NgZone3, inject as inject4, signal as signal3 } from "@angular/core";
import { QueryClient as QueryClient3, notifyManager as notifyManager3 } from "@tanstack/query-core";
function injectIsMutating(filters, injector) {
return assertInjector(injectIsMutating, injector, () => {
const destroyRef = inject4(DestroyRef3);
const ngZone = inject4(NgZone3);
const queryClient = inject4(QueryClient3);
const cache = queryClient.getMutationCache();
let isMutating = queryClient.isMutating(filters);
const result = signal3(isMutating);
const unsubscribe = ngZone.runOutsideAngular(
() => cache.subscribe(
notifyManager3.batchCalls(() => {
const newIsMutating = queryClient.isMutating(filters);
if (isMutating !== newIsMutating) {
isMutating = newIsMutating;
ngZone.run(() => {
result.set(isMutating);
});
}
})
)
);
destroyRef.onDestroy(unsubscribe);
return result;
});
}
// src/inject-mutation.ts
import {
DestroyRef as DestroyRef4,
NgZone as NgZone4,
computed as computed3,
effect as effect2,
inject as inject5,
signal as signal4,
untracked as untracked3
} from "@angular/core";
import {
MutationObserver,
QueryClient as QueryClient4,
notifyManager as notifyManager4
} from "@tanstack/query-core";
function injectMutation(optionsFn, injector) {
return assertInjector(injectMutation, injector, () => {
const destroyRef = inject5(DestroyRef4);
const ngZone = inject5(NgZone4);
const queryClient = inject5(QueryClient4);
const optionsSignal = computed3(optionsFn);
const observerSignal = (() => {
let instance = null;
return computed3(() => {
return instance ||= new MutationObserver(queryClient, optionsSignal());
});
})();
const mutateFnSignal = computed3(() => {
const observer = observerSignal();
return (variables, mutateOptions) => {
observer.mutate(variables, mutateOptions).catch(noop);
};
});
const resultFromInitialOptionsSignal = computed3(() => {
const observer = observerSignal();
return observer.getCurrentResult();
});
const resultFromSubscriberSignal = signal4(null);
effect2(
() => {
const observer = observerSignal();
const options = optionsSignal();
untracked3(() => {
observer.setOptions(options);
});
},
{
injector
}
);
effect2(
() => {
const observer = observerSignal();
untracked3(() => {
const unsubscribe = ngZone.runOutsideAngular(
() => observer.subscribe(
notifyManager4.batchCalls((state) => {
ngZone.run(() => {
if (state.isError && shouldThrowError(observer.options.throwOnError, [
state.error
])) {
ngZone.onError.emit(state.error);
throw state.error;
}
resultFromSubscriberSignal.set(state);
});
})
)
);
destroyRef.onDestroy(unsubscribe);
});
},
{
injector
}
);
const resultSignal = computed3(() => {
const resultFromSubscriber = resultFromSubscriberSignal();
const resultFromInitialOptions = resultFromInitialOptionsSignal();
const result = resultFromSubscriber ?? resultFromInitialOptions;
return {
...result,
mutate: mutateFnSignal(),
mutateAsync: result.mutate
};
});
return signalProxy(resultSignal);
});
}
// src/inject-mutation-state.ts
import { DestroyRef as DestroyRef5, NgZone as NgZone5, computed as computed4, inject as inject6, signal as signal5 } from "@angular/core";
import {
QueryClient as QueryClient5,
notifyManager as notifyManager5,
replaceEqualDeep
} from "@tanstack/query-core";
function getResult(mutationCache, options) {
return mutationCache.findAll(options.filters).map(
(mutation) => options.select ? options.select(mutation) : mutation.state
);
}
function injectMutationState(mutationStateOptionsFn = () => ({}), options) {
return assertInjector(injectMutationState, options?.injector, () => {
const destroyRef = inject6(DestroyRef5);
const ngZone = inject6(NgZone5);
const queryClient = inject6(QueryClient5);
const mutationCache = queryClient.getMutationCache();
const resultFromOptionsSignal = computed4(() => {
return [
getResult(mutationCache, mutationStateOptionsFn()),
performance.now()
];
});
const resultFromSubscriberSignal = signal5(
null
);
const effectiveResultSignal = computed4(() => {
const optionsResult = resultFromOptionsSignal();
const subscriberResult = resultFromSubscriberSignal();
return subscriberResult && subscriberResult[1] > optionsResult[1] ? subscriberResult[0] : optionsResult[0];
});
const unsubscribe = ngZone.runOutsideAngular(
() => mutationCache.subscribe(
notifyManager5.batchCalls(() => {
const [lastResult] = effectiveResultSignal();
const nextResult = replaceEqualDeep(
lastResult,
getResult(mutationCache, mutationStateOptionsFn())
);
if (lastResult !== nextResult) {
ngZone.run(() => {
resultFromSubscriberSignal.set([nextResult, performance.now()]);
});
}
})
)
);
destroyRef.onDestroy(unsubscribe);
return effectiveResultSignal;
});
}
// src/inject-queries.ts
import {
QueriesObserver,
QueryClient as QueryClient6,
notifyManager as notifyManager6
} from "@tanstack/query-core";
import {
DestroyRef as DestroyRef6,
NgZone as NgZone6,
computed as computed5,
effect as effect3,
inject as inject7,
signal as signal6
} from "@angular/core";
function injectQueries({
queries,
...options
}, injector) {
return assertInjector(injectQueries, injector, () => {
const destroyRef = inject7(DestroyRef6);
const ngZone = inject7(NgZone6);
const queryClient = inject7(QueryClient6);
const defaultedQueries = computed5(() => {
return queries().map((opts) => {
const defaultedOptions = queryClient.defaultQueryOptions(opts);
defaultedOptions._optimisticResults = "optimistic";
return defaultedOptions;
});
});
const observer = new QueriesObserver(
queryClient,
defaultedQueries(),
options
);
effect3(() => {
observer.setQueries(
defaultedQueries(),
options
);
});
const [, getCombinedResult] = observer.getOptimisticResult(
defaultedQueries(),
options.combine
);
const result = signal6(getCombinedResult());
const unsubscribe = ngZone.runOutsideAngular(
() => observer.subscribe(notifyManager6.batchCalls(result.set))
);
destroyRef.onDestroy(unsubscribe);
return result;
});
}
// src/inject-query.ts
import { QueryObserver } from "@tanstack/query-core";
function injectQuery(optionsFn, injector) {
return assertInjector(
injectQuery,
injector,
() => createBaseQuery(optionsFn, QueryObserver)
);
}
// src/inject-query-client.ts
import { Injector as Injector2, inject as inject8 } from "@angular/core";
import { QueryClient as QueryClient7 } from "@tanstack/query-core";
function injectQueryClient(injectOptions = {}) {
return (injectOptions.injector ?? inject8(Injector2)).get(QueryClient7);
}
// src/providers.ts
import {
DestroyRef as DestroyRef7,
ENVIRONMENT_INITIALIZER,
PLATFORM_ID,
computed as computed6,
effect as effect4,
inject as inject9,
makeEnvironmentProviders
} from "@angular/core";
import { QueryClient as QueryClient8, onlineManager } from "@tanstack/query-core";
import { isPlatformBrowser } from "@angular/common";
// src/util/is-dev-mode/is-dev-mode.ts
import { isDevMode } from "@angular/core";
// src/providers.ts
function provideQueryClient(queryClient) {
return { provide: QueryClient8, useValue: queryClient };
}
function provideTanStackQuery(queryClient, ...features) {
return makeEnvironmentProviders([
provideQueryClient(queryClient),
{
// Do not use provideEnvironmentInitializer while Angular < v19 is supported
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useValue: () => {
queryClient.mount();
inject9(DestroyRef7).onDestroy(() => queryClient.unmount());
}
},
features.map((feature) => feature.\u0275providers)
]);
}
function provideAngularQuery(queryClient) {
return provideTanStackQuery(queryClient);
}
function queryFeature(kind, providers) {
return { \u0275kind: kind, \u0275providers: providers };
}
function withDevtools(optionsFn) {
let providers = [];
if (!isDevMode() && !optionsFn) {
providers = [];
} else {
providers = [
{
// Do not use provideEnvironmentInitializer while Angular < v19 is supported
provide: ENVIRONMENT_INITIALIZER,
multi: true,
useFactory: () => {
if (!isPlatformBrowser(inject9(PLATFORM_ID))) return noop;
const injectedClient = inject9(QueryClient8, {
optional: true
});
const destroyRef = inject9(DestroyRef7);
const options = computed6(() => optionsFn?.() ?? {});
let devtools = null;
let el = null;
const shouldLoadToolsSignal = computed6(() => {
const { loadDevtools } = options();
return typeof loadDevtools === "boolean" ? loadDevtools : isDevMode();
});
const getResolvedQueryClient = () => {
const client = options().client ?? injectedClient;
if (!client) {
throw new Error("No QueryClient found");
}
return client;
};
const destroyDevtools = () => {
devtools?.unmount();
el?.remove();
devtools = null;
};
return () => effect4(() => {
const shouldLoadTools = shouldLoadToolsSignal();
const {
client,
position,
errorTypes,
buttonPosition,
initialIsOpen
} = options();
if (devtools && !shouldLoadTools) {
destroyDevtools();
return;
} else if (devtools && shouldLoadTools) {
client && devtools.setClient(client);
position && devtools.setPosition(position);
errorTypes && devtools.setErrorTypes(errorTypes);
buttonPosition && devtools.setButtonPosition(buttonPosition);
initialIsOpen && devtools.setInitialIsOpen(initialIsOpen);
return;
} else if (!shouldLoadTools) {
return;
}
el = document.body.appendChild(document.createElement("div"));
el.classList.add("tsqd-parent-container");
import("@tanstack/query-devtools").then((queryDevtools) => {
devtools = new queryDevtools.TanstackQueryDevtools({
...options(),
client: getResolvedQueryClient(),
queryFlavor: "Angular Query",
version: "5",
onlineManager
});
el && devtools.mount(el);
destroyRef.onDestroy(destroyDevtools);
});
});
}
}
];
}
return queryFeature("DeveloperTools", providers);
}
var queryFeatures = ["DeveloperTools"];
export {
infiniteQueryOptions,
injectInfiniteQuery,
injectIsFetching,
injectIsMutating,
injectMutation,
injectMutationState,
injectQueries,
injectQuery,
injectQueryClient,
mutationOptions,
provideAngularQuery,
provideQueryClient,
provideTanStackQuery,
queryFeatures,
queryOptions,
withDevtools
};
//# sourceMappingURL=index.mjs.map