@tanstack/angular-query-experimental
Version:
Signals for managing, caching and syncing asynchronous and remote data in Angular
1 lines • 14.1 kB
Source Map (JSON)
{"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;"}