diff --git a/.changeset/angular-stable-release.md b/.changeset/angular-stable-release.md new file mode 100644 index 00000000000..7577a4d58ad --- /dev/null +++ b/.changeset/angular-stable-release.md @@ -0,0 +1,7 @@ +--- +'@tanstack/angular-query': major +'@tanstack/angular-query-devtools': major +'@tanstack/angular-query-persist-client': major +--- + +Add the stable Angular Query adapter for Angular 20.1+. It includes SSR support, Resource API interoperability, persistence integration, improved internal subscription and pending tasks management, stable `injectQueries` and consistency improvements in the API. diff --git a/.changeset/config.json b/.changeset/config.json index 984e1d89f1c..f5cbfb9fc03 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -6,12 +6,15 @@ ], "commit": false, "access": "public", - "baseBranch": "main", + "baseBranch": "angular-adapter-pre", "updateInternalDependencies": "patch", "fixed": [ [ - "@tanstack/angular-query-experimental", - "@tanstack/angular-query-persist-client", + "@tanstack/angular-query", + "@tanstack/angular-query-devtools", + "@tanstack/angular-query-persist-client" + ], + [ "@tanstack/eslint-plugin-query", "@tanstack/preact-query", "@tanstack/preact-query-devtools", diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 00000000000..a63b0811ca7 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,35 @@ +{ + "mode": "pre", + "tag": "rc", + "initialVersions": { + "@tanstack/angular-query": "4.0.0", + "@tanstack/angular-query-devtools": "4.0.0", + "@tanstack/angular-query-experimental": "5.103.1", + "@tanstack/angular-query-persist-client": "4.0.0", + "@tanstack/eslint-plugin-query": "5.103.1", + "@tanstack/lit-query": "0.2.22", + "@tanstack/preact-query": "5.103.1", + "@tanstack/preact-query-devtools": "5.103.1", + "@tanstack/preact-query-persist-client": "5.103.1", + "@tanstack/query-async-storage-persister": "5.103.1", + "@tanstack/query-broadcast-client-experimental": "5.103.1", + "@tanstack/query-core": "5.103.1", + "@tanstack/query-devtools": "5.103.1", + "@tanstack/query-persist-client-core": "5.103.1", + "@tanstack/query-sync-storage-persister": "5.103.1", + "@tanstack/query-test-utils": "0.0.0", + "@tanstack/react-query": "5.103.1", + "@tanstack/react-query-devtools": "5.103.1", + "@tanstack/react-query-next-experimental": "5.103.1", + "@tanstack/react-query-persist-client": "5.103.1", + "@tanstack/solid-query": "5.103.1", + "@tanstack/solid-query-devtools": "5.103.1", + "@tanstack/solid-query-persist-client": "5.103.1", + "@tanstack/svelte-query": "6.2.1", + "@tanstack/svelte-query-devtools": "6.2.1", + "@tanstack/svelte-query-persist-client": "6.2.1", + "@tanstack/vue-query": "5.103.1", + "@tanstack/vue-query-devtools": "6.2.1" + }, + "changesets": [] +} diff --git a/docs/config.json b/docs/config.json index 9bbeb14325c..918e29fb4bf 100644 --- a/docs/config.json +++ b/docs/config.json @@ -176,20 +176,20 @@ "to": "framework/angular/quick-start" }, { - "label": "Angular HttpClient and other data fetching clients", + "label": "Devtools", + "to": "framework/angular/devtools" + }, + { + "label": "Angular HttpClient", "to": "framework/angular/angular-httpclient-and-other-data-fetching-clients" }, { - "label": "Devtools", - "to": "framework/angular/devtools" + "label": "Resource API", + "to": "framework/angular/resource-api" }, { "label": "TypeScript", "to": "framework/angular/typescript" - }, - { - "label": "Zoneless", - "to": "framework/angular/zoneless" } ] }, @@ -774,6 +774,10 @@ "label": "Default Query Fn", "to": "framework/angular/guides/default-query-function" }, + { + "label": "SSR", + "to": "framework/angular/guides/ssr" + }, { "label": "Testing", "to": "framework/angular/guides/testing" @@ -781,6 +785,18 @@ { "label": "Does this replace state managers?", "to": "framework/angular/guides/does-this-replace-client-state" + }, + { + "label": "Migrating from Experimental", + "to": "framework/angular/guides/migrating-from-experimental" + }, + { + "label": "Migrating from ngneat/query", + "to": "framework/angular/guides/migrating-from-ngneat-query" + }, + { + "label": "Error Handling", + "to": "framework/angular/guides/error-handling" } ] }, @@ -5048,28 +5064,12 @@ "label": "Functions / partialMatchKey", "to": "framework/angular/reference/functions/partialMatchKey" }, - { - "label": "Functions / provideAngularQuery", - "to": "framework/angular/reference/functions/provideAngularQuery" - }, - { - "label": "Functions / provideIsRestoring", - "to": "framework/angular/reference/functions/provideIsRestoring" - }, - { - "label": "Functions / provideQueryClient", - "to": "framework/angular/reference/functions/provideQueryClient" - }, { "label": "Functions / provideTanStackQuery", "to": "framework/angular/reference/functions/provideTanStackQuery" }, { - "label": "Functions / queryFeature", - "to": "framework/angular/reference/functions/queryFeature" - }, - { - "label": "Functions / queryOptions", + "label": "queryOptions", "to": "framework/angular/reference/functions/queryOptions" }, { @@ -5132,10 +5132,6 @@ "label": "Interfaces / CancelOptions", "to": "framework/angular/reference/interfaces/CancelOptions" }, - { - "label": "Interfaces / CreateBaseQueryOptions", - "to": "framework/angular/reference/interfaces/CreateBaseQueryOptions" - }, { "label": "Interfaces / CreateInfiniteQueryOptions", "to": "framework/angular/reference/interfaces/CreateInfiniteQueryOptions" diff --git a/docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md b/docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md index 031e9fa8f8b..783a55990e5 100644 --- a/docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md +++ b/docs/framework/angular/angular-httpclient-and-other-data-fetching-clients.md @@ -1,22 +1,16 @@ --- id: Angular-HttpClient-and-other-data-fetching-clients -title: Angular HttpClient and other data fetching clients +title: Angular HttpClient --- -Because TanStack Query's fetching mechanisms are agnostically built on Promises, you can use literally any asynchronous data fetching client, including the browser native `fetch` API, `graphql-request`, and more. +TanStack Query works with any data fetching client that returns a Promise, including the browser's +`fetch` API and clients such as `graphql-request`. ## Using Angular's `HttpClient` for data fetching -`HttpClient` is a powerful and integrated part of Angular, which gives the following benefits: - -- Mock responses in unit tests using [provideHttpClientTesting](https://angular.dev/guide/http/testing). -- [Interceptors](https://angular.dev/guide/http/interceptors) can be used for a wide range of functionality including adding authentication headers, performing logging, etc. While some data fetching libraries have their own interceptor system, `HttpClient` interceptors are integrated with Angular's dependency injection system. -- `HttpClient` automatically informs [`PendingTasks`](https://angular.dev/api/core/PendingTasks#), which enables Angular to be aware of pending requests. Unit tests and SSR can use the resulting application _stableness_ information to wait for pending requests to finish. This makes unit testing much easier for [Zoneless](https://angular.dev/guide/zoneless) applications. -- When using SSR, `HttpClient` will [cache requests](https://angular.dev/guide/ssr#caching-data-when-using-HttpClient) performed on the server. This will prevent unneeded requests on the client. `HttpClient` SSR caching works out of the box. TanStack Query has its own hydration functionality which may be more powerful but requires some setup. Which one fits your needs best depends on your use case. - -### Using observables in `queryFn` - -As TanStack Query is a promise based library, observables from `HttpClient` need to be converted to promises. This can be done with the `lastValueFrom` or `firstValueFrom` functions from `rxjs`. +Angular's `HttpClient` returns Observables, which query and mutation functions do not consume +directly. Convert the Observable with RxJS's `firstValueFrom` or `lastValueFrom`. Query functions +can also return a synchronous value. ```ts @Component({ @@ -28,21 +22,28 @@ class ExampleComponent { readonly repoDataQuery = injectQuery(() => ({ queryKey: ['repoData'], queryFn: () => - lastValueFrom( + firstValueFrom( this.http.get('https://api.github.com/repos/tanstack/query'), ), })) } ``` -> Since Angular is moving towards RxJS as an optional dependency, it's expected that `HttpClient` will also support promises in the future. -> -> Support for observables in TanStack Query for Angular is planned. +For regular `HttpClient` requests, `firstValueFrom` resolves the single response and unsubscribes. +Use `lastValueFrom` when the Observable emits multiple values and the query should resolve with the +last one. + +Angular [HTTP interceptors](https://angular.dev/guide/http/interceptors) continue to apply to +requests made inside query and mutation functions. + +## SSR + +When using `HttpClient` with SSR, prefer Angular Query's built-in query hydration and disable +Angular's HTTP transfer cache. Otherwise, both systems serialize the same request data. -## Comparison table +Add +[`withNoHttpTransferCache()`](https://angular.dev/api/platform-browser/withNoHttpTransferCache) to +[`provideClientHydration()`](https://angular.dev/api/platform-browser/provideClientHydration). See +the [SSR guide](./guides/ssr.md) for the complete setup. -| Data fetching client | Pros | Cons | -| --------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------- | -| **Angular HttpClient** | Featureful and very well integrated with Angular. | Observables need to be converted to Promises. | -| **Fetch** | Browser native API, so adds nothing to bundle size. | Barebones API which lacks many features. | -| **Specialized libraries such as `graphql-request`** | Specialized features for specific use cases. | If it's not an Angular library it won't integrate well with the framework. | +For cancellation, see [Query Cancellation](./guides/query-cancellation.md). diff --git a/docs/framework/angular/devtools.md b/docs/framework/angular/devtools.md index 0103213a350..2af57274fef 100644 --- a/docs/framework/angular/devtools.md +++ b/docs/framework/angular/devtools.md @@ -11,79 +11,129 @@ title: Devtools ## Enable devtools +Add the devtools package (in addition to `@tanstack/angular-query`): + +```bash +npm install @tanstack/angular-query-devtools +``` + The devtools help you debug and inspect your queries and mutations. You can enable the devtools by adding `withDevtools` to `provideTanStackQuery`. -By default, Angular Query Devtools are only included in development mode bundles, so you don't need to worry about excluding them during a production build. +By default, Angular Query Devtools only load in development. ```ts -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { withDevtools } from '@tanstack/angular-query-devtools' export const appConfig: ApplicationConfig = { - providers: [provideTanStackQuery(new QueryClient(), withDevtools())], + providers: [provideTanStackQuery(() => new QueryClient(), withDevtools())], } ``` ## Devtools in production -Devtools are automatically excluded from production builds. However, it might be desirable to lazy load the devtools in production. - -To use `withDevtools` in production builds, import using the `production` sub-path. The function exported from the production subpath is identical to the main one, but won't be excluded from production builds. +If you need the real implementation in production, import from the `production` entrypoint. ```ts -import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production' +import { withDevtools } from '@tanstack/angular-query-devtools/production' ``` -To control when devtools are loaded, you can use the `loadDevtools` option. +To control when devtools are rendered, use the `loadDevtools` option. -When not setting the option or setting it to 'auto', the devtools will be loaded automatically only when Angular runs in development mode. +When omitted or set to `'auto'`, devtools are only rendered in development mode. ```ts -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { withDevtools } from '@tanstack/angular-query-devtools' -provideTanStackQuery(new QueryClient(), withDevtools()) +providers: [provideTanStackQuery(() => new QueryClient(), withDevtools())] // which is equivalent to -provideTanStackQuery( - new QueryClient(), - withDevtools(() => ({ loadDevtools: 'auto' })), -) +providers: [ + provideTanStackQuery( + () => new QueryClient(), + withDevtools(() => ({ loadDevtools: 'auto' })), + ), +] ``` -When setting the option to true, the devtools will be loaded in both development and production mode. +When setting the option to true, the devtools will be rendered in both development and production mode. This is useful if you want to load devtools based on [Angular environment configurations](https://angular.dev/tools/cli/environments). E.g. you could set this to true when the application is running on your production build staging environment. ```ts import { environment } from './environments/environment' // Make sure to use the production sub-path to load devtools in production builds -import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production' +import { withDevtools } from '@tanstack/angular-query-devtools/production' + +providers: [ + provideTanStackQuery( + () => new QueryClient(), + withDevtools(() => ({ loadDevtools: environment.loadDevtools })), + ), +] +``` + +When setting the option to false, the devtools will not be rendered. -provideTanStackQuery( - new QueryClient(), - withDevtools(() => ({ loadDevtools: environment.loadDevtools })), -) +```ts +providers: [ + provideTanStackQuery( + () => new QueryClient(), + withDevtools(() => ({ loadDevtools: false })), + ), +] ``` -When setting the option to false, the devtools will not be loaded. +### Webpack file replacements + +Some webpack-based Angular builders do not apply package export conditions when +they bundle application code. Use the explicit `production` and `stub` +entrypoints with Angular CLI file replacements to keep the devtools dependency +out of production bundles. + +Create an application-level import that can be replaced: ```ts -provideTanStackQuery( - new QueryClient(), - withDevtools(() => ({ loadDevtools: false })), -) +// src/app/query-devtools.ts +export { withDevtools } from '@tanstack/angular-query-devtools/production' ``` -## Derive options through reactivity +```ts +// src/app/query-devtools.stub.ts +export { withDevtools } from '@tanstack/angular-query-devtools/stub' +``` -Options are passed to `withDevtools` from a callback function to support reactivity through signals. In the following example -a signal is created from a RxJS observable that emits on a keyboard shortcut. When the derived signal is set to true, the devtools are lazily loaded. +Import the application-level module from your config: -The example below always loads devtools in development mode and loads on-demand in production mode when a keyboard shortcut is pressed. +```ts +import { withDevtools } from './query-devtools' +``` + +Then configure the production build: + +```json +{ + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/app/query-devtools.ts", + "with": "src/app/query-devtools.stub.ts" + } + ] + } + } +} +``` + +The same pattern is available for the programmatic panel: +`@tanstack/angular-query-devtools/devtools-panel/production` and +`@tanstack/angular-query-devtools/devtools-panel/stub`. + +## Reactive options + +Mutable options can be Angular signals. For example, a signal derived from a keyboard shortcut can show devtools on demand: ```ts import { Injectable, isDevMode } from '@angular/core' @@ -107,29 +157,23 @@ export class DevtoolsOptionsManager { } ``` -If you want to use an injectable such as a service in the callback you can use `deps`. The injected value will be passed as parameter to the callback function. - -This is similar to `deps` in Angular's [`useFactory`](https://angular.dev/guide/di/dependency-injection-providers#factory-providers-usefactory) provider. +The callback runs once in an Angular injection context, so it can call +`inject()`. Return signals as option values to update them reactively: ```ts // ... -// 👇 Note we import from the production sub-path to enable devtools lazy loading in production builds -import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production' +// 👇 Import from the production sub-path to make devtools available in production builds +import { inject } from '@angular/core' +import { withDevtools } from '@tanstack/angular-query-devtools/production' export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(), provideTanStackQuery( - new QueryClient(), - withDevtools( - (devToolsOptionsManager: DevtoolsOptionsManager) => ({ - loadDevtools: devToolsOptionsManager.loadDevtools(), - }), - { - // `deps` is used to inject and pass `DevtoolsOptionsManager` to the `withDevtools` callback. - deps: [DevtoolsOptionsManager], - }, - ), + () => new QueryClient(), + withDevtools(() => ({ + loadDevtools: inject(DevtoolsOptionsManager).loadDevtools, + })), ), ], } @@ -137,11 +181,16 @@ export const appConfig: ApplicationConfig = { ### Options returned from the callback -Of these options `loadDevtools`, `client`, `position`, `errorTypes`, `buttonPosition`, `initialIsOpen`, and `theme` support reactivity through signals. +`loadDevtools`, `client`, `position`, `errorTypes`, `buttonPosition`, +`initialIsOpen`, and `theme` accept either their documented static value or a +signal containing that value. + +`styleNonce`, `shadowDOMTarget`, and `hideDisabledQueries` are construction-time +options and do not accept signals. - `loadDevtools?: 'auto' | boolean` - - Defaults to `auto`: lazily loads devtools when in development mode. Skips loading in production mode. - - Use this to control if the devtools are loaded. + - Omit or `'auto'`: load devtools only in development mode. + - Use this to control whether devtools load when using the `/production` import. - `initialIsOpen?: Boolean` - Set this to `true` if you want the tools to default to being open - `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right" | "relative"` @@ -164,4 +213,4 @@ Of these options `loadDevtools`, `client`, `position`, `errorTypes`, `buttonPosi - Set this to true to hide disabled queries from the devtools panel. - `theme?: "light" | "dark" | "system"` - Defaults to `system`. - - Set this to change the theme of the devtools panel. + - Sets the theme of the devtools panel. diff --git a/docs/framework/angular/guides/background-fetching-indicators.md b/docs/framework/angular/guides/background-fetching-indicators.md index 9e5c5804383..032ffae833b 100644 --- a/docs/framework/angular/guides/background-fetching-indicators.md +++ b/docs/framework/angular/guides/background-fetching-indicators.md @@ -6,7 +6,7 @@ replace: { 'useIsFetching': 'injectIsFetching', 'hook': 'function', - '@tanstack/react-query': '@tanstack/angular-query-experimental', + '@tanstack/react-query': '@tanstack/angular-query', } --- @@ -42,7 +42,7 @@ class TodosComponent { [//]: # 'Example2' ```angular-ts -import { injectIsFetching } from '@tanstack/angular-query-experimental' +import { injectIsFetching } from '@tanstack/angular-query' @Component({ selector: 'global-loading-indicator', @@ -58,3 +58,14 @@ export class GlobalLoadingIndicatorComponent { ``` [//]: # 'Example2' + +## Reactive filters + +Pass a factory when filtering activity. It can read signals or required component inputs: + +```ts +readonly isFetching = injectIsFetching(() => ({ queryKey: ['todos', this.userId()] })) +readonly isMutating = injectIsMutating(() => ({ mutationKey: ['save', this.userId()] })) +``` + +The count updates when these dependencies change. diff --git a/docs/framework/angular/guides/default-query-function.md b/docs/framework/angular/guides/default-query-function.md index a7ba445b318..9227eed2817 100644 --- a/docs/framework/angular/guides/default-query-function.md +++ b/docs/framework/angular/guides/default-query-function.md @@ -16,16 +16,17 @@ const defaultQueryFn: QueryFunction = async ({ queryKey }) => { } // provide the default query function to your app with defaultOptions -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - queryFn: defaultQueryFn, +const createQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + queryFn: defaultQueryFn, + }, }, - }, -}) + }) bootstrapApplication(MyAppComponent, { - providers: [provideTanStackQuery(queryClient)], + providers: [provideTanStackQuery(createQueryClient)], }) export class PostsComponent { diff --git a/docs/framework/angular/guides/dependent-queries.md b/docs/framework/angular/guides/dependent-queries.md index 38afbd491f7..a1a58286120 100644 --- a/docs/framework/angular/guides/dependent-queries.md +++ b/docs/framework/angular/guides/dependent-queries.md @@ -27,7 +27,13 @@ projectsQuery = injectQuery(() => ({ [//]: # 'Example2' ```ts -// injectQueries is under development for Angular Query +projectsQueries = injectQueries(() => ({ + queries: + this.userQuery.data()?.projectIds.map((projectId) => ({ + queryKey: ['project', projectId], + queryFn: () => getProjectById(projectId), + })) ?? [], +})) ``` [//]: # 'Example2' diff --git a/docs/framework/angular/guides/disabling-queries.md b/docs/framework/angular/guides/disabling-queries.md index b52266505aa..07bacf2d914 100644 --- a/docs/framework/angular/guides/disabling-queries.md +++ b/docs/framework/angular/guides/disabling-queries.md @@ -70,7 +70,7 @@ export class TodosComponent { [//]: # 'Example3' ```angular-ts -import { skipToken, injectQuery } from '@tanstack/angular-query-experimental' +import { skipToken, injectQuery } from '@tanstack/angular-query' @Component({ selector: 'todos', diff --git a/docs/framework/angular/guides/does-this-replace-client-state.md b/docs/framework/angular/guides/does-this-replace-client-state.md index 3872115c940..c1118ff4b28 100644 --- a/docs/framework/angular/guides/does-this-replace-client-state.md +++ b/docs/framework/angular/guides/does-this-replace-client-state.md @@ -4,8 +4,10 @@ title: Does TanStack Query replace global state managers? ref: docs/framework/react/guides/does-this-replace-client-state.md replace: { - 'useQuery': 'injectQuery', - 'useMutation': 'injectMutation', + 'Does TanStack Query replace Redux, MobX or other global state managers\?': 'Does TanStack Query replace global state managers?', + 'Redux, MobX, Zustand, etc.': 'NgRx Store, Signal Store, etc.', + '`useQuery`': '`injectQuery`', + '`useMutation`': '`injectMutation`', 'hook': 'function', } --- diff --git a/docs/framework/angular/guides/error-handling.md b/docs/framework/angular/guides/error-handling.md new file mode 100644 index 00000000000..4efdfcf87ef --- /dev/null +++ b/docs/framework/angular/guides/error-handling.md @@ -0,0 +1,57 @@ +--- +id: error-handling +title: Error Handling +--- + +Query and mutation errors are exposed through their result signals. The adapter does not report +failed requests to a global error handler automatically. + +To report cache failures to Angular's `ErrorHandler`, configure the cache callbacks when creating +the client. Capture the handler in the provider factory, where `inject()` is available: + +```ts +import { ErrorHandler, inject } from '@angular/core' +import { + MutationCache, + QueryCache, + QueryClient, + provideTanStackQuery, +} from '@tanstack/angular-query' + +export const queryProviders = provideTanStackQuery(() => { + const errors = inject(ErrorHandler) + + return new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (query.meta?.reportToErrorHandler !== false) { + errors.handleError(error) + } + }, + }), + mutationCache: new MutationCache({ + onError: (error, _variables, _context, mutation) => { + if (mutation.meta?.reportToErrorHandler !== false) { + errors.handleError(error) + } + }, + }), + }) +}) +``` + +This example reports failures by default. Set `meta: { reportToErrorHandler: false }` on a query or +mutation which your application handles locally. Compose these callbacks with any cache callbacks +you already use. + +A query cache callback reports a failed cache operation once, even when several components observe +the query. Observer-only `select` errors and exceptions in options or `combine` computations are +not cache failures and are not reported by this recipe. + +`mutateAsync()` rejects and can be caught with `try`/`catch`. `mutate()` returns void; use mutation +callbacks or result signals for its error state. `refetch({ throwOnError: true })` opts into promise +rejection for that call. + +The [Resource view](../resource-api.md) throws from `value()` when its mapped status is `error`. +Check `hasValue()` before reading it. An uncaught error while Angular renders a template can still +reach Angular's error handler. This integration does not provide a component error boundary. diff --git a/docs/framework/angular/guides/important-defaults.md b/docs/framework/angular/guides/important-defaults.md index 886792038b1..95a6b1df98a 100644 --- a/docs/framework/angular/guides/important-defaults.md +++ b/docs/framework/angular/guides/important-defaults.md @@ -6,6 +6,7 @@ replace: { 'React': 'Angular', 'react-query': 'angular-query', + '@tanstack/angular-query': '@tanstack/angular-query', 'useQuery': 'injectQuery', 'useInfiniteQuery': 'injectInfiniteQuery', 'useMemo and useCallback': 'setting signal values', diff --git a/docs/framework/angular/guides/infinite-queries.md b/docs/framework/angular/guides/infinite-queries.md index f606cf8c6ba..202c4361377 100644 --- a/docs/framework/angular/guides/infinite-queries.md +++ b/docs/framework/angular/guides/infinite-queries.md @@ -10,7 +10,7 @@ replace: ```angular-ts import { Component, computed, inject } from '@angular/core' -import { injectInfiniteQuery } from '@tanstack/angular-query-experimental' +import { injectInfiniteQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { ProjectsService } from './projects-service' diff --git a/docs/framework/angular/guides/invalidations-from-mutations.md b/docs/framework/angular/guides/invalidations-from-mutations.md index e69c4e5be7c..a8e866224eb 100644 --- a/docs/framework/angular/guides/invalidations-from-mutations.md +++ b/docs/framework/angular/guides/invalidations-from-mutations.md @@ -20,7 +20,7 @@ postTodoMutation = injectMutation(() => ({ import { injectMutation, QueryClient, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' export class TodosComponent { readonly queryClient = inject(QueryClient) diff --git a/docs/framework/angular/guides/migrating-from-experimental.md b/docs/framework/angular/guides/migrating-from-experimental.md new file mode 100644 index 00000000000..b541fe0309f --- /dev/null +++ b/docs/framework/angular/guides/migrating-from-experimental.md @@ -0,0 +1,267 @@ +--- +id: migrating-from-experimental +title: Migrating from Angular Query Experimental +--- + +The stable package keeps the callback-based query, mutation, and signal result APIs from the +experimental adapter. Most application queries therefore only need an import change. The following +sections cover the breaking configuration and entrypoint changes. + +## Replace the experimental package + +```bash +npm uninstall @tanstack/angular-query-experimental +npm install @tanstack/angular-query +``` + +Angular 20.1 or newer is required by the stable package. + +```ts +import { injectQuery } from '@tanstack/angular-query-experimental' // [!code --] +import { injectQuery } from '@tanstack/angular-query' // [!code ++] +``` + +## Provide a client factory + +`provideTanStackQuery` no longer accepts a pre-created `QueryClient`. Pass a factory instead. + +```ts +provideTanStackQuery(new QueryClient()) // [!code --] +provideTanStackQuery(() => new QueryClient()) // [!code ++] +``` + +`provideTanStackQuery` now returns one `EnvironmentProviders` value rather than an array. Use it in +an environment injector, such as `ApplicationConfig.providers`, the `providers` passed to +`bootstrapApplication`, route-level `Route.providers`, or `createEnvironmentInjector`. It is not +supported in `@Component.providers` or `@Directive.providers`. Add the result directly; do not +spread it. + +```ts +providers: [...provideTanStackQuery(() => new QueryClient())] // [!code --] +providers: [provideTanStackQuery(() => new QueryClient())] // [!code ++] +``` + +## Remove deprecated provider and injection helpers + +`provideAngularQuery` and `injectQueryClient` have been removed. + +```ts +provideAngularQuery(new QueryClient()) // [!code --] +provideTanStackQuery(() => new QueryClient()) // [!code ++] +``` + +```ts +const queryClient = injectQueryClient() // [!code --] +const queryClient = inject(QueryClient) // [!code ++] +``` + +## Install the standalone devtools package + +Devtools no longer ship as entrypoints of the core Angular package. + +```bash +npm install @tanstack/angular-query-devtools +``` + +```ts +import { withDevtools } from '@tanstack/angular-query-experimental/devtools' // [!code --] +import { withDevtools } from '@tanstack/angular-query-devtools' // [!code ++] +``` + +The production and panel entrypoints move in the same way: + +```ts +import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production' // [!code --] +import { withDevtools } from '@tanstack/angular-query-devtools/production' // [!code ++] +``` + +The devtools options callback now runs in an injection context. Remove the `deps` option and call +`inject()` inside the callback instead. + +```ts +const optionsFromManager = (manager: DevtoolsOptionsManager) => ({ + loadDevtools: manager.loadDevtools, +}) + +withDevtools(optionsFromManager, { deps: [DevtoolsOptionsManager] }) // [!code --] +withDevtools(() => optionsFromManager(inject(DevtoolsOptionsManager))) // [!code ++] +``` + +See the [Devtools guide](../devtools.md) for setup, production entrypoints, and configuration +options. + +## Use the stable `injectQueries` export + +`injectQueries` is now exported from the main package. The helper must be called from an Angular +injection context. + +```ts +import { + injectQueries, // [!code --] +} from '@tanstack/angular-query-experimental/inject-queries-experimental' // [!code --] +import { injectQueries } from '@tanstack/angular-query' // [!code ++] + +const getQueries = () => ({ queries }) +const results = injectQueries(getQueries, injector) // [!code --] +const results = runInInjectionContext(injector, () => injectQueries(getQueries)) // [!code ++] +``` + +The `queries` callback is reactive, tuple inference is preserved, and `combine` can derive a single +result: + +```ts +readonly summary = injectQueries(() => ({ + queries: [todosOptions(), usersOptions()], + combine: ([todos, users]) => ({ + pending: todos.isPending || users.isPending, + todoCount: todos.data?.length ?? 0, + userCount: users.data?.length ?? 0, + }), +})) +``` + +## Review SSR hydration + +`provideTanStackQuery` now dehydrates the server cache into Angular `TransferState` and hydrates it +in the browser by default. In most SSR applications, no additional setup is necessary. + +If your application already performs manual dehydration and hydration, either remove the manual +implementation or disable the built-in behavior to avoid hydrating the same client twice: + +```ts +provideTanStackQuery(() => new QueryClient(), withNoQueryHydration()) +``` + +Give each client a unique key when multiple clients use automatic hydration: + +```ts +provideTanStackQuery( + () => new QueryClient(), + withHydrationKey('admin-query-cache'), +) +``` + +See the [SSR guide](./ssr.md) for request-scoped client setup and the interaction with Angular +`HttpClient` transfer caching. + +## Update tests that wait for stability + +Observed queries, parallel queries, and mutations started through the adapter register with Angular's `PendingTasks`. As a result, `ApplicationRef.whenStable()` and `fixture.whenStable()` wait until +that work settles. + +This can change existing tests that expected `whenStable()` to resolve while a request was still in +progress. Make sure mocked requests and mutations resolve or reject before awaiting stability. When +using fake timers, advance the timers and queued microtasks before awaiting `whenStable()`. Disabled +queries do not start fetching automatically; a manual refetch still registers pending work. + +See [Testing](./testing.md) for query, mutation, `HttpClientTestingController`, and fake-timer +examples. + +## New behavior that needs no migration + +The stable package also includes the following compatible improvements: + +- Query results can be converted into an Angular Resource with `toResource`. See the + [Resource API](../resource-api.md). +- `injectQueries` is supported from the main package with reactive options and field-level signals. +- Result field signals are evaluated lazily, so options can safely read required input signals without + being evaluated during class construction. +- Persistence options may be supplied as a browser-only factory, making references such as + `localStorage` safe in SSR applications. + +## Error handling + +Observer options no longer include `throwOnError`. Errors are exposed through the result's +`error`, `status`, and related signals, without an automatic global report. Cached query data +remains readable when a background refetch fails. + +```ts +injectQuery(() => ({ + queryKey: ['todos'], + queryFn: fetchTodos, + throwOnError: true, +})) // [!code --] +injectQuery(() => ({ queryKey: ['todos'], queryFn: fetchTodos })) // [!code ++] +``` + +Remove observer `throwOnError` from query, infinite-query, and mutation options, including your +Angular-specific client defaults. The shared core client still accepts this option for other +framework adapters, but Angular does not use it for reporting. + +`mutateAsync()` still rejects on failure, and `mutate()` still returns void. Imperative options +such as `query.refetch({ throwOnError: true })` retain their promise-rejection behavior. +To report cache failures to Angular, use the [ErrorHandler guide](./error-handling.md). +`toResource(query).value()` throws when the Resource is in its error state; guard the read with +`hasValue()` or inspect `error()` first. + +## Use one client provider + +```ts +provideQueryClient(() => new QueryClient()) // [!code --] +provideTanStackQuery(() => new QueryClient()) // [!code ++] +``` + +`provideQueryClient` has been removed. Use `provideTanStackQuery`, which handles mounting, +unmounting, and default hydration. Replacing it with a bare Angular `useFactory` provider would +omit that setup. + +## Make activity filters reactive + +```ts +injectIsFetching({ queryKey: ['todos'] }) // [!code --] +injectIsFetching(() => ({ queryKey: ['todos', userId()] })) // [!code ++] + +injectIsMutating({ mutationKey: ['save'] }) // [!code --] +injectIsMutating(() => ({ mutationKey: ['save'] })) // [!code ++] +``` + +Calls without filters remain unchanged. Filter factories can read required inputs and signals; +changing a filter updates the count without waiting for a cache event. + +## Imperative methods + +Methods such as `refetch()` and `fetchNextPage()` use the current options even when called +immediately after changing an options signal. + +Read the current `injectQueries()` result when calling a method after adding, removing, or +reordering queries. + +`injectMutationState` selections and `injectQueries` combined results are no longer deeply +compared by the adapter. A newly allocated selected object or combined result can trigger consumers +even when its contents match the previous value. Prefer returning existing values when possible. + +## Stability and notifications + +Signal updates no longer enter `NgZone` or wait for adapter microtasks. Angular's change-detection +scheduler handles rendering. Keep reading reactive values as signals; callbacks do not gain an +injection context or guaranteed zone membership. + +Each mutation invocation blocks stability until its promise settles, including awaited lifecycle +callbacks. A later invocation completing, or calling `reset()`, does not release an earlier mutation's +work. Destroying the owning component or injector releases its pending tasks. Queries that are +paused offline continue to block stability until resumed, cancelled, or unobserved. + +## Persistence configuration + +Pass a factory to `withPersistQueryClient`: + +```ts +withPersistQueryClient(() => ({ + persistOptions: { persister }, +})) +``` + +The factory runs once per injector, in an injection context, only in the browser. +Create persisters that access `localStorage` inside this factory. + +## QueryClient factories + +`provideTanStackQuery` accepts a factory. If another provider already owns the client, +resolve it inside that factory: + +```ts +provideTanStackQuery(() => inject(MY_QUERY_CLIENT)) +``` + +Replace direct token arguments with this form. Persistence manages its restoration +state automatically through `withPersistQueryClient`. diff --git a/docs/framework/angular/guides/migrating-from-ngneat-query.md b/docs/framework/angular/guides/migrating-from-ngneat-query.md new file mode 100644 index 00000000000..491d0c2a1ec --- /dev/null +++ b/docs/framework/angular/guides/migrating-from-ngneat-query.md @@ -0,0 +1,246 @@ +--- +id: migrating-from-ngneat-query +title: Migrating from ngneat/query +--- + +Angular Query requires Angular 20.1 or newer. + +This guide uses `@ngneat/query` in its examples. + +## Replace the packages + +Remove whichever legacy package name your application uses, along with its devtools package. The +stable adapter includes `@tanstack/query-core` as a dependency, so remove a direct installation +unless your application imports it independently. + +```bash +npm uninstall @ngneat/query @ngneat/query-devtools @tanstack/query-core +npm install @tanstack/angular-query @tanstack/angular-query-devtools +``` + +## Configure the `QueryClient` + +Replace `provideQueryClientOptions` with a `QueryClient` factory passed to +`provideTanStackQuery`. The factory runs in an Angular injection context, so it can call `inject()` +when building caches or default options. + +```ts +import { QueryCache } from '@ngneat/query' // [!code --] +import { provideQueryClientOptions } from '@ngneat/query' // [!code --] +import { QueryCache, QueryClient } from '@tanstack/angular-query' // [!code ++] +import { provideTanStackQuery } from '@tanstack/angular-query' // [!code ++] + +const queryClientConfig = { + queryCache: new QueryCache({ onError: handleError }), +} + +providers: [ + provideQueryClientOptions(queryClientConfig), // [!code --] + provideTanStackQuery(() => new QueryClient(queryClientConfig)), // [!code ++] +] +``` + +## Migrate queries + +The legacy `injectQuery()` call returned a function that accepted query options. The stable API +accepts a reactive options callback and returns the query result directly. + +```ts +private readonly useQuery = injectQuery() // [!code --] + +readonly todos = this.useQuery({ // [!code --] + queryKey: ['todos'], // [!code --] + queryFn: fetchTodos, // [!code --] +}) // [!code --] +readonly todos = injectQuery(() => ({ // [!code ++] + queryKey: ['todos'], // [!code ++] + queryFn: fetchTodos, // [!code ++] +})) // [!code ++] +``` + +Read signals inside the callback to update options reactively. This replaces calling +`updateOptions` yourself. + +```ts +readonly filter = signal('') +private readonly useQuery = injectQuery() // [!code --] +readonly todos = this.useQuery({ // [!code --] + queryKey: ['todos', this.filter()], // [!code --] + queryFn: () => fetchTodos(this.filter()), // [!code --] +}) // [!code --] +readonly todos = injectQuery(() => ({ // [!code ++] + queryKey: ['todos', this.filter()], // [!code ++] + queryFn: () => fetchTodos(this.filter()), // [!code ++] +})) // [!code ++] +``` + +When a helper must be called from a callback that is not already in an injection context, use +Angular's `runInInjectionContext` at the call site: + +```ts +runInInjectionContext(injector, () => injectQuery(() => queryOptions)) +``` + +## Migrate infinite queries and mutations + +`injectInfiniteQuery` and `injectMutation` use the same callback pattern: + +```ts +private readonly useInfiniteQuery = injectInfiniteQuery() // [!code --] +readonly posts = this.useInfiniteQuery({ // [!code --] + queryKey: ['posts'], // [!code --] + queryFn: ({ pageParam }) => fetchPosts(pageParam), // [!code --] + initialPageParam: 0, // [!code --] + getNextPageParam: (lastPage) => lastPage.nextId, // [!code --] +}) // [!code --] +readonly posts = injectInfiniteQuery(() => ({ // [!code ++] + queryKey: ['posts'], // [!code ++] + queryFn: ({ pageParam }) => fetchPosts(pageParam), // [!code ++] + initialPageParam: 0, // [!code ++] + getNextPageParam: (lastPage) => lastPage.nextId, // [!code ++] +})) // [!code ++] +``` + +```ts +private readonly useMutation = injectMutation() // [!code --] +readonly addTodo = this.useMutation({ // [!code --] + mutationFn: addTodo, // [!code --] +}) // [!code --] +readonly addTodo = injectMutation(() => ({ // [!code ++] + mutationFn: addTodo, // [!code ++] +})) // [!code ++] +``` + +Imperative methods remain direct methods, for example `addTodo.mutate(value)`, +`addTodo.reset()`, `posts.fetchNextPage()`, and `todos.refetch()`. + +## Migrate query and mutation results + +The legacy adapter exposed one signal and one Observable containing the entire observer result. +The stable adapter exposes each result field as a signal and keeps imperative methods as functions. + +```ts +todos.result().isPending // [!code --] +todos.result().data // [!code --] +todos.result().error // [!code --] +todos.isPending() // [!code ++] +todos.data() // [!code ++] +todos.error() // [!code ++] +``` + +Templates use the field signals in the same way: + +```angular-html +@if (todos.result().isPending) { +@if (todos.isPending()) { + Loading... +} @else if (todos.isError()) { + {{ todos.error()?.message }} +} @else { + @for (todo of todos.data(); track todo.id) { + {{ todo.title }} + } +} +``` + +There is no direct replacement for `result$`. Convert the field signal you need with Angular's +`toObservable`, or compose several fields with `computed` first. + +```ts +readonly todosResult$ = this.todos.result$ // [!code --] +readonly todosState = computed(() => ({ // [!code ++] + data: this.todos.data(), // [!code ++] + error: this.todos.error(), // [!code ++] + status: this.todos.status(), // [!code ++] +})) // [!code ++] +readonly todosResult$ = toObservable(this.todosState) // [!code ++] +``` + +Mutation state follows the same rule: + +```ts +addTodo.result().isPending // [!code --] +addTodo.result().data // [!code --] +addTodo.isPending() // [!code ++] +addTodo.data() // [!code ++] +``` + +## Convert Observable query functions + +The legacy adapter accepted an RxJS `Observable` from `queryFn` and `mutationFn`. The stable +adapter follows TanStack Query's Promise-based contract, so convert an Observable with +`firstValueFrom` or `lastValueFrom`. + +```ts +queryFn: () => this.http.get('/api/todos') // [!code --] +queryFn: () => lastValueFrom(this.http.get('/api/todos')) // [!code ++] +``` + +```ts +const createTodo = (todo: Todo) => this.http.post('/api/todos', todo) + +mutationFn: createTodo // [!code --] +mutationFn: (todo) => lastValueFrom(createTodo(todo)) // [!code ++] +``` + +See [Angular HttpClient](../angular-httpclient-and-other-data-fetching-clients.md) +for a complete example. If an Observable does not complete, prefer `firstValueFrom` or make it +complete before converting it. + +## Migrate background indicators + +`injectIsFetching` and `injectIsMutating` now accept a reactive filters callback and return a +`Signal`. + +```ts +private readonly useIsFetching = injectIsFetching() // [!code --] +readonly fetchingTodos = // [!code --] + this.useIsFetching({ queryKey: ['todos'] }).toSignal() // [!code --] +readonly fetchingTodos = injectIsFetching(() => ({ queryKey: ['todos'] })) // [!code ++] +``` + +```ts +private readonly useIsMutating = injectIsMutating() // [!code --] +readonly mutatingTodos = // [!code --] + this.useIsMutating({ mutationKey: ['todos'] }).toSignal() // [!code --] +readonly mutatingTodos = injectIsMutating(() => ({ mutationKey: ['todos'] })) // [!code ++] +``` + +Use `toObservable(this.fetchingTodos)` if a consumer still needs an Observable. + +## Inject the `QueryClient` + +Use Angular dependency injection directly instead of `injectQueryClient`: + +```ts +import { injectQueryClient } from '@ngneat/query' // [!code --] +import { inject } from '@angular/core' // [!code ++] +import { QueryClient } from '@tanstack/angular-query' // [!code ++] + +private readonly queryClient = injectQueryClient() // [!code --] +private readonly queryClient = inject(QueryClient) // [!code ++] +``` + +## Migrate devtools + +Replace the legacy devtools provider with the `withDevtools` feature from the standalone Angular +Query devtools package. + +```ts +import { provideQueryClientOptions } from '@ngneat/query' // [!code --] +import { provideQueryDevTools } from '@ngneat/query-devtools' // [!code --] +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' // [!code ++] +import { withDevtools } from '@tanstack/angular-query-devtools' // [!code ++] + +const devtools = withDevtools(() => ({ initialIsOpen: true })) + +providers: [ + provideQueryClientOptions({}), // [!code --] + provideQueryDevTools({ initialIsOpen: true }), // [!code --] + provideTanStackQuery(() => new QueryClient(), devtools), // [!code ++] +] +``` + +See the [Devtools guide](../devtools.md) for production entrypoints and reactive options. + +See [Error handling](./error-handling.md) for optional Angular `ErrorHandler` integration. diff --git a/docs/framework/angular/guides/migrating-to-angular-query.md b/docs/framework/angular/guides/migrating-to-angular-query.md new file mode 100644 index 00000000000..393d15ea7cc --- /dev/null +++ b/docs/framework/angular/guides/migrating-to-angular-query.md @@ -0,0 +1,11 @@ +--- +id: migrating-to-angular-query +title: Migrating to Angular Query +--- + +Choose the guide for your current adapter: + +- [Migrating from Angular Query Experimental](./migrating-from-experimental.md) +- [Migrating from ngneat/query or openng/query](./migrating-from-ngneat-query.md) + +Both target Angular 20.1 or newer. diff --git a/docs/framework/angular/guides/mutations.md b/docs/framework/angular/guides/mutations.md index b40933c55ba..18f3e53b4bc 100644 --- a/docs/framework/angular/guides/mutations.md +++ b/docs/framework/angular/guides/mutations.md @@ -272,3 +272,5 @@ queryClient.resumePausedMutations() [//]: # 'Example11' [//]: # 'Materials' [//]: # 'Materials' + +See [Error handling](./error-handling.md) to connect cache failures to Angular’s `ErrorHandler`. diff --git a/docs/framework/angular/guides/parallel-queries.md b/docs/framework/angular/guides/parallel-queries.md index 8e2e2348fc5..d6071e9d3b3 100644 --- a/docs/framework/angular/guides/parallel-queries.md +++ b/docs/framework/angular/guides/parallel-queries.md @@ -17,6 +17,9 @@ replace: [//]: # 'Example' ```ts +@Component({ + // ... +}) export class AppComponent { // The following queries will execute in parallel readonly usersQuery = injectQuery(() => ({ @@ -45,12 +48,14 @@ TanStack Query provides `injectQueries`, which you can use to dynamically execut [//]: # 'Example2' ```ts +@Component({ + // ... +}) export class AppComponent { users = signal>([]) - // Please note injectQueries is under development and this code does not work yet - readonly userQueries = injectQueries(() => ({ - queries: users().map((user) => { + userQueries = injectQueries(() => ({ + queries: this.users().map((user) => { return { queryKey: ['user', user.id], queryFn: () => fetchUserById(user.id), diff --git a/docs/framework/angular/guides/queries.md b/docs/framework/angular/guides/queries.md index 051d332bf8a..1bf5816d206 100644 --- a/docs/framework/angular/guides/queries.md +++ b/docs/framework/angular/guides/queries.md @@ -6,6 +6,7 @@ replace: { 'React': 'Angular', 'react-query': 'angular-query', + '@tanstack/angular-query': '@tanstack/angular-query', 'promise': 'promise or observable', 'custom hooks': 'services', 'the `useQuery` hook': '`injectQuery`', @@ -17,7 +18,7 @@ replace: [//]: # 'Example' ```ts -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' export class TodosComponent { readonly todosQuery = injectQuery(() => ({ @@ -101,4 +102,11 @@ class TodosComponent {} [//]: # 'Example4' [//]: # 'Materials' + +Angular Query can convert each query result into a Resource-compatible view with `toResource`. See +the [Resource API](../resource-api.md) when an Angular API expects a +[Resource](https://angular.dev/api/core/Resource). + [//]: # 'Materials' + +See [Error handling](./error-handling.md) to connect cache failures to Angular’s `ErrorHandler`. diff --git a/docs/framework/angular/guides/query-cancellation.md b/docs/framework/angular/guides/query-cancellation.md index 6d107f2fffc..86de25536c3 100644 --- a/docs/framework/angular/guides/query-cancellation.md +++ b/docs/framework/angular/guides/query-cancellation.md @@ -15,7 +15,7 @@ However, if you consume the `AbortSignal`, the Promise will be cancelled (e.g. a ```ts import { HttpClient } from '@angular/common/http' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' postQuery = injectQuery(() => ({ enabled: this.postId() > 0, diff --git a/docs/framework/angular/guides/query-invalidation.md b/docs/framework/angular/guides/query-invalidation.md index 36956557dcf..eb287dd0070 100644 --- a/docs/framework/angular/guides/query-invalidation.md +++ b/docs/framework/angular/guides/query-invalidation.md @@ -8,7 +8,7 @@ replace: { 'useQuery': 'injectQuery', 'hooks': 'functions' } [//]: # 'Example2' ```ts -import { injectQuery, QueryClient } from '@tanstack/angular-query-experimental' +import { injectQuery, QueryClient } from '@tanstack/angular-query' class QueryInvalidationExample { readonly queryClient = inject(QueryClient) diff --git a/docs/framework/angular/guides/query-options.md b/docs/framework/angular/guides/query-options.md index 4ce9f836d59..19ed50384e8 100644 --- a/docs/framework/angular/guides/query-options.md +++ b/docs/framework/angular/guides/query-options.md @@ -7,7 +7,7 @@ ref: docs/framework/react/guides/query-options.md [//]: # 'Example1' ```ts -import { queryOptions, noop } from '@tanstack/angular-query-experimental' +import { noop, queryOptions } from '@tanstack/angular-query' @Injectable({ providedIn: 'root', @@ -38,7 +38,7 @@ queries = inject(QueriesService) postQuery = injectQuery(() => this.queries.post(this.postId())) -queryClient.query(this.queries.post(23)).catch(noop) +void queryClient.query(this.queries.post(23)).catch(noop) queryClient.setQueryData(this.queries.post(42).queryKey, newPost) ``` diff --git a/docs/framework/angular/guides/query-retries.md b/docs/framework/angular/guides/query-retries.md index a7b0b1841ca..e19c8f85a37 100644 --- a/docs/framework/angular/guides/query-retries.md +++ b/docs/framework/angular/guides/query-retries.md @@ -15,7 +15,7 @@ replace: [//]: # 'Example' ```ts -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' // Make a specific query retry a certain number of times const todoQuery = injectQuery(() => ({ @@ -33,19 +33,20 @@ const todoQuery = injectQuery(() => ({ import { QueryCache, QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' + QueryClientProvider, +} from '@tanstack/angular-query' -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), +const createQueryClient = () => + new QueryClient({ + defaultOptions: { + queries: { + retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), + }, }, - }, -}) + }) bootstrapApplication(AppComponent, { - providers: [provideTanStackQuery(queryClient)], + providers: [provideTanStackQuery(createQueryClient)], }) ``` diff --git a/docs/framework/angular/guides/ssr.md b/docs/framework/angular/guides/ssr.md new file mode 100644 index 00000000000..2b6b727796f --- /dev/null +++ b/docs/framework/angular/guides/ssr.md @@ -0,0 +1,101 @@ +--- +id: ssr +title: SSR +--- + +For [Angular SSR](https://angular.dev/guide/ssr), [`provideTanStackQuery`](../reference/functions/provideTanStackQuery.md) serializes the `QueryClient` cache into Angular's `TransferState` and restores it when the browser application starts. + +See the [Angular SSR example](https://github.com/TanStack/query/tree/main/examples/angular/ssr). The +[SSR persistence example](https://github.com/TanStack/query/tree/main/examples/angular/ssr-persist) +builds on the same setup with browser persistence. + +## Query client factory + +```ts +import { isPlatformBrowser } from '@angular/common' +import { inject, PLATFORM_ID } from '@angular/core' +import { QueryClient } from '@tanstack/angular-query' + +export function createQueryClient() { + const isBrowser = isPlatformBrowser(inject(PLATFORM_ID)) + + return new QueryClient({ + defaultOptions: { + queries: { + // Retry failed queries in the browser, but not on the server. + retry: isBrowser ? 3 : 0, + }, + }, + }) +} +``` + +## Browser config + +Use the factory with `provideTanStackQuery` in your application config. Angular's HTTP transfer +cache is enabled by default with +[`provideClientHydration`](https://angular.dev/api/platform-browser/provideClientHydration). Add +[`withNoHttpTransferCache`](https://angular.dev/api/platform-browser/withNoHttpTransferCache) so +query results are not serialized once by each cache. + +```ts +import type { ApplicationConfig } from '@angular/core' +import { provideHttpClient } from '@angular/common/http' +import { + provideClientHydration, + withEventReplay, + withNoHttpTransferCache, +} from '@angular/platform-browser' +import { provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' +import { createQueryClient } from './query-client' + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(), + provideClientHydration(withEventReplay(), withNoHttpTransferCache()), + provideTanStackQuery(createQueryClient, withDevtools()), + ], +} +``` + +## Server config + +Merge the application config with `provideServerRendering` in the server config. + +```ts +import { mergeApplicationConfig } from '@angular/core' +import { provideServerRendering, withRoutes } from '@angular/ssr' +import { appConfig } from './app.config' +import { serverRoutes } from './app.routes.server' + +export const serverConfig = mergeApplicationConfig(appConfig, { + providers: [provideServerRendering(withRoutes(serverRoutes))], +}) +``` + +## Multiple query clients + +Built-in hydration uses a default transfer key. For a second `QueryClient` in a child injector, pass a distinct key with `withHydrationKey` so each client's serialized cache stays separate. + +```ts +providers: [ + provideTanStackQuery( + createSecondaryQueryClient, + withHydrationKey('my-secondary-query-cache'), + ), +] +``` + +## Disabling built-in hydration + +If you need to opt out of TanStack Query's built-in `TransferState` integration for a specific injector, add `withNoQueryHydration()`. + +```ts +providers: [provideTanStackQuery(createQueryClient, withNoQueryHydration())] +``` + +## See also + +- [Angular HttpClient and data fetching](../angular-httpclient-and-other-data-fetching-clients.md) +- [Devtools](../devtools.md) diff --git a/docs/framework/angular/guides/testing.md b/docs/framework/angular/guides/testing.md index 7648d7f6b32..2bf32bfd2b1 100644 --- a/docs/framework/angular/guides/testing.md +++ b/docs/framework/angular/guides/testing.md @@ -9,11 +9,11 @@ TanStack Query's `inject*` functions integrate with [`PendingTasks`](https://ang This means tests and SSR can wait until mutations and queries resolve. In unit tests you can use `ApplicationRef.whenStable()` or `fixture.whenStable()` to await query completion. This works for both Zone.js and Zoneless setups. -> This integration requires Angular 19 or later. Earlier versions of Angular do not support `PendingTasks`. +> This adapter requires Angular 20.1 or later. It uses `PendingTasks` to keep application stability open while observed work is pending. ## TestBed setup -Create a fresh `QueryClient` for every spec and provide it with `provideTanStackQuery` or `provideQueryClient`. This keeps caches isolated and lets you change default options per test: +Create a fresh `QueryClient` for every spec and return it from the provider factory. Keeping a reference makes it easy to inspect or clear the cache: ```ts const queryClient = new QueryClient({ @@ -25,11 +25,11 @@ const queryClient = new QueryClient({ }) TestBed.configureTestingModule({ - providers: [provideTanStackQuery(queryClient)], + providers: [provideTanStackQuery(() => queryClient)], }) ``` -> If your applications actual TanStack Query config is used in unit tests, make sure `withDevtools` is not accidentally included in test providers. This can cause slow tests. It is best to keep test and production configs separate. +> If your application's TanStack Query config is used in unit tests, make sure `withDevtools` is not accidentally included in test providers. This can cause slow tests. It is best to keep test and production configs separate. If you share helpers, remember to call `queryClient.clear()` (or build a new instance) in `afterEach` so data from one test never bleeds into another. @@ -46,7 +46,7 @@ const query = TestBed.runInInjectionContext(() => })), ) -TestBed.tick() // Trigger effect +TestBed.tick() // Synchronize the test application // Application is stable when queries are idle await appRef.whenStable() @@ -69,6 +69,7 @@ For components, bootstrap them through `TestBed.createComponent`, then await `fi ```ts const fixture = TestBed.createComponent(ExampleComponent) +fixture.autoDetectChanges() await fixture.whenStable() expect(fixture.componentInstance.query.data()).toEqual({ value: 42 }) @@ -80,12 +81,17 @@ Retries slow failing tests because the default backoff runs three times. Set `re ## HttpClient & network stubs -Angular's `HttpClientTestingModule` plays nicely with PendingTasks. Register it alongside the Query provider and flush responses through `HttpTestingController`: +Angular's `provideHttpClientTesting` plays nicely with PendingTasks. Register it after +`provideHttpClient`, alongside the Query provider, and flush responses through +`HttpTestingController`: ```ts TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [provideTanStackQuery(queryClient)], + providers: [ + provideHttpClient(), + provideHttpClientTesting(), + provideTanStackQuery(() => queryClient), + ], }) const httpCtrl = TestBed.inject(HttpTestingController) @@ -96,6 +102,7 @@ const query = TestBed.runInInjectionContext(() => })), ) +TestBed.tick() // Synchronize the test application so the request starts const fixturePromise = TestBed.inject(ApplicationRef).whenStable() httpCtrl.expectOne('/api/todos').flush([{ id: 1 }]) await fixturePromise @@ -104,6 +111,17 @@ expect(query.data()).toEqual([{ id: 1 }]) httpCtrl.verify() ``` +Test observable results after normal Angular initialization and updates. For components, render +with `fixture.detectChanges()` and await `fixture.whenStable()` when the operation should finish. +For injection-context-only tests, synchronize the test application as shown above. When using +HTTP mocks or fake timers, flush the request or advance time before awaiting stability. + +Avoid asserting an exact subscription order or a stale intermediate value during initialization. +Those are implementation details. To test pending mutation or fetching state, hold the operation +open with a controlled promise and assert after rendering; do not await stability until you +resolve that promise. To test completed state, resolve the operation and await stability before +asserting the result. + ## Infinite queries & pagination Use the same pattern for infinite queries: call `fetchNextPage()`, advance timers if you are faking time, then await stability and assert on `data().pages`. @@ -112,19 +130,20 @@ Use the same pattern for infinite queries: call `fetchNextPage()`, advance timer const infinite = TestBed.runInInjectionContext(() => injectInfiniteQuery(() => ({ queryKey: ['pages'], - queryFn: ({ pageParam = 1 }) => fetchPage(pageParam), + initialPageParam: 1, + queryFn: ({ pageParam }) => fetchPage(pageParam), getNextPageParam: (last, all) => all.length + 1, })), ) +TestBed.tick() await appRef.whenStable() -expect(infinite.data().pages).toHaveLength(1) +expect(infinite.data()?.pages).toHaveLength(1) await infinite.fetchNextPage() -await vi.advanceTimersByTimeAsync(0) await appRef.whenStable() -expect(infinite.data().pages).toHaveLength(2) +expect(infinite.data()?.pages).toHaveLength(2) ``` ## Mutations and optimistic updates @@ -138,7 +157,7 @@ const mutation = TestBed.runInInjectionContext(() => mutation.mutate('test') -// Trigger effect +// Synchronize the test application TestBed.tick() await appRef.whenStable() @@ -147,11 +166,15 @@ expect(mutation.isSuccess()).toBe(true) expect(mutation.data()).toBe('TEST') ``` +`whenStable()` waits for every mutation invocation, including awaited lifecycle callbacks. +Resolve or reject all outstanding mocked mutations before awaiting stability; calling `reset()` +does not finish a request. Queries paused offline also keep stability open until resumed or cancelled. + ## Quick checklist - Fresh `QueryClient` per test (and clear it afterwards) - Disable or control retries to avoid timeouts - Advance timers + microtasks before `whenStable()` when using fake timers -- Use `HttpClientTestingModule` or your preferred mock to assert network calls +- Use `provideHttpClientTesting` or your preferred mock to assert network calls - Await `whenStable()` after every `refetch`, `fetchNextPage`, or mutation - Prefer `TestBed.runInInjectionContext` for service tests and `fixture.whenStable()` for component tests diff --git a/docs/framework/angular/guides/window-focus-refetching.md b/docs/framework/angular/guides/window-focus-refetching.md index 14490ba07ca..36e446238df 100644 --- a/docs/framework/angular/guides/window-focus-refetching.md +++ b/docs/framework/angular/guides/window-focus-refetching.md @@ -2,7 +2,7 @@ id: window-focus-refetching title: Window Focus Refetching ref: docs/framework/react/guides/window-focus-refetching.md -replace: { '@tanstack/react-query': '@tanstack/angular-query-experimental' } +replace: { '@tanstack/react-query': '@tanstack/angular-query' } --- [//]: # 'Example' @@ -11,13 +11,14 @@ replace: { '@tanstack/react-query': '@tanstack/angular-query-experimental' } export const appConfig: ApplicationConfig = { providers: [ provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, // default: true + () => + new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, // default: true + }, }, - }, - }), + }), ), ], } diff --git a/docs/framework/angular/installation.md b/docs/framework/angular/installation.md index dffc092e7cd..5cfae0bd3bd 100644 --- a/docs/framework/angular/installation.md +++ b/docs/framework/angular/installation.md @@ -3,32 +3,45 @@ id: installation title: Installation --- -> IMPORTANT: This library is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Upgrade carefully. If you use this in production while in experimental stage, please lock your version to a patch-level version to avoid unexpected breaking changes. +_Angular Query is compatible with Angular v20.1 and higher._ -### NPM +### Angular CLI -_Angular Query is compatible with Angular v16 and higher_ +```bash +ng add @tanstack/angular-query +``` + +This installs the package and devtools, then configures an SSR-safe `QueryClient` factory with +`withDevtools()`. + +### Package managers ```bash -npm i @tanstack/angular-query-experimental +npm i @tanstack/angular-query ``` or ```bash -pnpm add @tanstack/angular-query-experimental +pnpm add @tanstack/angular-query ``` or ```bash -yarn add @tanstack/angular-query-experimental +yarn add @tanstack/angular-query ``` or ```bash -bun add @tanstack/angular-query-experimental +bun add @tanstack/angular-query +``` + +If you want Angular Query devtools, install the standalone devtools package as well: + +```bash +npm i @tanstack/angular-query-devtools ``` -> Wanna give it a spin before you download? Try out the [simple](./examples/simple) or [basic](./examples/basic) examples! +> Want to try it first? See the [simple](./examples/simple) or [basic](./examples/basic) example. diff --git a/docs/framework/angular/overview.md b/docs/framework/angular/overview.md index 31dd1131ccb..83dd7497cf5 100644 --- a/docs/framework/angular/overview.md +++ b/docs/framework/angular/overview.md @@ -3,17 +3,13 @@ id: overview title: Overview --- -> IMPORTANT: This library is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Upgrade carefully. If you use this in production while in experimental stage, please lock your version to a patch-level version to avoid unexpected breaking changes. +The `@tanstack/angular-query` package offers a 1st-class API for using TanStack Query via Angular. -The `@tanstack/angular-query-experimental` package offers a 1st-class API for using TanStack Query via Angular. - -## Feedback welcome! - -We are in the process of getting to a stable API for TanStack Query on Angular. If you have any feedback, please contact us at the [TanStack Discord](https://tlinz.com/discord) server or [visit this discussion](https://github.com/TanStack/query/discussions/6293) on Github. +If you have any feedback, please contact us at the [TanStack Discord](https://tlinz.com/discord) server or [visit this discussion](https://github.com/TanStack/query/discussions/6293) on Github. ## Supported Angular Versions -TanStack Query is compatible with Angular v16 and higher. +TanStack Query is compatible with Angular v20.1 and higher. TanStack Query (FKA React Query) is often described as the missing data-fetching library for web applications, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your web applications a breeze. @@ -63,8 +59,7 @@ In the example below, you can see TanStack Query in its most basic and simple fo ```angular-ts import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { HttpClient } from '@angular/common/http' -import { CommonModule } from '@angular/common' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' @Component({ @@ -73,11 +68,10 @@ import { lastValueFrom } from 'rxjs' template: ` @if (repoDataQuery.isPending()) { Loading... - } - @if (repoDataQuery.error()) { - An error has occurred: {{ repoDataQuery.error().message }} - } - @if (repoDataQuery.data(); as data) { + } @else if (query.isError()) { + An error has occurred: {{ query.error().message }} + } @else if (query.isSuccess()) { + @let data = query.data();

{{ data.name }}

{{ data.description }}

👀 {{ data.subscribers_count }} diff --git a/docs/framework/angular/quick-start.md b/docs/framework/angular/quick-start.md index cfe29d87b58..c7358a767af 100644 --- a/docs/framework/angular/quick-start.md +++ b/docs/framework/angular/quick-start.md @@ -3,42 +3,25 @@ id: quick-start title: Quick Start --- -> IMPORTANT: This library is currently in an experimental stage. This means that breaking changes will happen in minor AND patch releases. Upgrade carefully. If you use this in production while in experimental stage, please lock your version to a patch-level version to avoid unexpected breaking changes. - [//]: # 'Example' If you're looking for a fully functioning example, please have a look at our [basic codesandbox example](./examples/basic) -### Provide the client to your App +### Configure the QueryClient -```ts -import { provideHttpClient } from '@angular/common/http' -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' - -bootstrapApplication(AppComponent, { - providers: [provideHttpClient(), provideTanStackQuery(new QueryClient())], -}) -``` - -or in a NgModule-based app +Pass a factory to `provideTanStackQuery`. Angular runs it once per injector and in an injection context, so it can use `inject()`. ```ts import { provideHttpClient } from '@angular/common/http' -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' +import { bootstrapApplication } from '@angular/platform-browser' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query' -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule], - providers: [provideTanStackQuery(new QueryClient())], - bootstrap: [AppComponent], +bootstrapApplication(AppComponent, { + providers: [ + provideHttpClient(), + provideTanStackQuery(() => new QueryClient()), + ], }) -export class AppModule {} ``` ### Component with query and mutation @@ -52,7 +35,7 @@ import { injectMutation, injectQuery, QueryClient, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' @Component({ template: ` diff --git a/docs/framework/angular/reference/functions/infiniteQueryOptions.md b/docs/framework/angular/reference/functions/infiniteQueryOptions.md index 1011653b452..263900df70a 100644 --- a/docs/framework/angular/reference/functions/infiniteQueryOptions.md +++ b/docs/framework/angular/reference/functions/infiniteQueryOptions.md @@ -9,7 +9,7 @@ title: infiniteQueryOptions function infiniteQueryOptions(options): CreateInfiniteQueryOptions & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:181](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L181) +Defined in: [packages/angular-query/src/infinite-query-options.ts:87](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L87) You can generally pass everything to `infiniteQueryOptions` that you can also pass to `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as @@ -51,7 +51,7 @@ The [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfini ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. +[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> ### See @@ -100,7 +100,7 @@ export class Projects { function infiniteQueryOptions(options): OmitKeyof, "queryFn"> & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:255](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L255) +Defined in: [packages/angular-query/src/infinite-query-options.ts:117](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L117) You can generally pass everything to `infiniteQueryOptions` that you can also pass to `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as @@ -140,7 +140,7 @@ The [UnusedSkipTokenInfiniteOptions](../type-aliases/UnusedSkipTokenInfiniteOpti ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. +`OmitKeyof`\<[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"queryFn"`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> ### Remarks @@ -195,7 +195,7 @@ export class Comments { function infiniteQueryOptions(options): CreateInfiniteQueryOptions & object & QueryKeyWithDataTag, TError>; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:329](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L329) +Defined in: [packages/angular-query/src/infinite-query-options.ts:147](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L147) You can generally pass everything to `infiniteQueryOptions` that you can also pass to `injectInfiniteQuery`. These options can be shared across functions and imperative APIs such as @@ -235,7 +235,7 @@ The [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataIn ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. +[`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `InfiniteData`\<`TQueryFnData`, `unknown`\>, `TError`\> ### Remarks diff --git a/docs/framework/angular/reference/functions/injectInfiniteQuery.md b/docs/framework/angular/reference/functions/injectInfiniteQuery.md index 2b121d5e4ac..def9d593971 100644 --- a/docs/framework/angular/reference/functions/injectInfiniteQuery.md +++ b/docs/framework/angular/reference/functions/injectInfiniteQuery.md @@ -3,20 +3,26 @@ id: injectInfiniteQuery title: injectInfiniteQuery --- +# Function: injectInfiniteQuery() + +Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Infinite queries can additively "load more" data onto an existing set of data or support infinite scroll. + +## Param + +A function that returns infinite query options. + +## See + +https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + ## Call Signature ```ts -function injectInfiniteQuery(injectInfiniteQueryFn, options?): DefinedCreateInfiniteQueryResult; +function injectInfiniteQuery(optionsFn): DefinedCreateInfiniteQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-infinite-query.ts:83](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L83) - -The options for `injectInfiniteQuery` are identical to `injectQuery`, with the addition of -`initialPageParam`, `getNextPageParam`, `getPreviousPageParam`, and `maxPages`. Infinite queries can -additively "load more" data onto an existing set of data, or "infinite scroll". - -This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, -so the resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). +Defined in: [packages/angular-query/src/inject-infinite-query.ts:27](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-infinite-query.ts#L27) ### Type Parameters @@ -42,79 +48,21 @@ so the resulting `data` signal is never `undefined` (unless a `select` changes ` ### Parameters -#### injectInfiniteQueryFn +#### optionsFn () => [`DefinedInitialDataInfiniteOptions`](../type-aliases/DefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function returning the [DefinedInitialDataInfiniteOptions](../type-aliases/DefinedInitialDataInfiniteOptions.md) to use — -everything you can pass to `injectInfiniteQuery`, with `initialData` set. Similar to `computed` from -Angular, this function runs in the reactive context, so signals read inside it drive the query. - -#### options? - -[`InjectInfiniteQueryOptions`](../interfaces/InjectInfiniteQueryOptions.md) - -Additional configuration. - ### Returns [`DefinedCreateInfiniteQueryResult`](../type-aliases/DefinedCreateInfiniteQueryResult.md)\<`TData`, `TError`\> -The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, -`hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and -`data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default -`InfiniteData` shape. - -### Remarks - -Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default -refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user -actions, or add conditions like `hasNextPage() && !isFetching()`. - -### See - -[infiniteQueryOptions](infiniteQueryOptions.md) to share these options between `injectInfiniteQuery` and imperative APIs -like `queryClient.fetchInfiniteQuery`. - -### Example - -```angular-ts -@Component({ - selector: 'projects', - template: ` - -
    - @for (page of projectsQuery.data().pages; track $index) { - @for (project of page.projects; track project.id) { -
  • {{ project.name }}
  • - } - } -
- `, -}) -export class Projects { - readonly projectsQuery = injectInfiniteQuery(() => ({ - queryKey: ['projects'], - queryFn: ({ pageParam }) => fetchProjects(pageParam), - initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage.nextId, - initialData: { pages: [], pageParams: [] }, - })) -} -``` - ## Call Signature ```ts -function injectInfiniteQuery(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult; +function injectInfiniteQuery(optionsFn): CreateInfiniteQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-infinite-query.ts:239](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L239) - -Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a -unique key. Infinite queries can additively "load more" data onto an existing set of data, or -"infinite scroll". +Defined in: [packages/angular-query/src/inject-infinite-query.ts:43](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-infinite-query.ts#L43) ### Type Parameters @@ -140,171 +88,21 @@ unique key. Infinite queries can additively "load more" data onto an existing se ### Parameters -#### injectInfiniteQueryFn +#### optionsFn () => [`UndefinedInitialDataInfiniteOptions`](../type-aliases/UndefinedInitialDataInfiniteOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function returning the [UndefinedInitialDataInfiniteOptions](../type-aliases/UndefinedInitialDataInfiniteOptions.md) to use -— everything you can pass to `injectInfiniteQuery`. Similar to `computed` from Angular, this function runs -in the reactive context, so signals read inside it drive the query. - -#### options? - -[`InjectInfiniteQueryOptions`](../interfaces/InjectInfiniteQueryOptions.md) - -Additional configuration. - ### Returns [`CreateInfiniteQueryResult`](../type-aliases/CreateInfiniteQueryResult.md)\<`TData`, `TError`\> -The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, -`hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and -`data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default -`InfiniteData` shape. - -### Remarks - -Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default -refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user -actions, or add conditions like `hasNextPage() && !isFetching()`. This is the only overload that accepts -`queryFn: skipToken`, shown below. - -### See - -[infiniteQueryOptions](infiniteQueryOptions.md) to share these options between `injectInfiniteQuery` and imperative APIs -like `queryClient.fetchInfiniteQuery`. - -### Examples - -Fetching the next page from a button click: -```angular-ts -@Component({ - selector: 'projects-list', - template: ` -
    - @for (page of projectsQuery.data()?.pages; track $index) { - @for (project of page.projects; track project.id) { -
  • {{ project.name }}
  • - } - } -
- - `, -}) -export class ProjectsList { - readonly projectsQuery = injectInfiniteQuery(() => ({ - queryKey: ['projects'], - queryFn: ({ pageParam }) => fetchProjects(pageParam), - initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage.nextId, - })) -} -``` - -Fetching the next page automatically as the user scrolls, using an `IntersectionObserver` on a sentinel -element after the list: -```angular-ts -@Component({ - selector: 'projects-list', - template: ` -
    - @for (page of projectsQuery.data()?.pages; track $index) { - @for (project of page.projects; track project.id) { -
  • {{ project.name }}
  • - } - } -
-
{{ projectsQuery.isFetchingNextPage() ? 'Loading more...' : '' }}
- `, -}) -export class ProjectsList { - readonly sentinel = viewChild>('sentinel') - - readonly projectsQuery = injectInfiniteQuery(() => ({ - queryKey: ['projects'], - queryFn: ({ pageParam }) => fetchProjects(pageParam), - initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage.nextId, - })) - - constructor() { - effect((onCleanup) => { - const sentinel = this.sentinel()?.nativeElement - if ( - sentinel == null || - !this.projectsQuery.hasNextPage() || - this.projectsQuery.isFetching() - ) { - return - } - - const observer = new IntersectionObserver(([entry]) => { - if (entry?.isIntersecting) this.projectsQuery.fetchNextPage() - }) - observer.observe(sentinel) - - onCleanup(() => observer.disconnect()) - }) - } -} -``` - -A query that's disabled, type safe, until `postId` is set — pass `skipToken` as `queryFn` instead of -setting `enabled: false`: -```angular-ts -@Component({ - selector: 'comments', - template: ` - @if (postId() == null) { - Select a post - } @else if (commentsQuery.isPending()) { - Loading... - } @else if (commentsQuery.isError()) { - Error: {{ commentsQuery.error()?.message }} - } @else { -
    - @for (page of commentsQuery.data().pages; track $index) { - @for (comment of page.comments; track comment.id) { -
  • {{ comment.text }}
  • - } - } -
- } - `, -}) -export class Comments { - readonly postId = signal(undefined) - - readonly commentsQuery = injectInfiniteQuery(() => ({ - queryKey: ['post', this.postId(), 'comments'], - queryFn: - this.postId() != null - ? ({ pageParam }) => fetchComments(this.postId()!, pageParam) - : skipToken, - initialPageParam: 0, - getNextPageParam: (lastPage) => lastPage.nextId, - })) -} -``` - ## Call Signature ```ts -function injectInfiniteQuery(injectInfiniteQueryFn, options?): CreateInfiniteQueryResult; +function injectInfiniteQuery(optionsFn): CreateInfiniteQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-infinite-query.ts:267](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L267) - -This overload accepts the general [CreateInfiniteQueryOptions](../interfaces/CreateInfiniteQueryOptions.md) shape rather than the -`initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call site — -useful when wrapping `injectInfiniteQuery` in your own helper function that forwards caller-provided -options. +Defined in: [packages/angular-query/src/inject-infinite-query.ts:59](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-infinite-query.ts#L59) ### Type Parameters @@ -330,21 +128,10 @@ options. ### Parameters -#### injectInfiniteQueryFn +#### optionsFn () => [`CreateInfiniteQueryOptions`](../interfaces/CreateInfiniteQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\> -A function that returns infinite query options. Similar to `computed` from -Angular, this function runs in the reactive context, so signals read inside it drive the query. - -#### options? - -[`InjectInfiniteQueryOptions`](../interfaces/InjectInfiniteQueryOptions.md) - -Additional configuration. - ### Returns [`CreateInfiniteQueryResult`](../type-aliases/CreateInfiniteQueryResult.md)\<`TData`, `TError`\> - -The infinite query result. diff --git a/docs/framework/angular/reference/functions/injectIsFetching.md b/docs/framework/angular/reference/functions/injectIsFetching.md index 1d121851e47..48a8aafe436 100644 --- a/docs/framework/angular/reference/functions/injectIsFetching.md +++ b/docs/framework/angular/reference/functions/injectIsFetching.md @@ -4,27 +4,21 @@ title: injectIsFetching --- ```ts -function injectIsFetching(filters?, options?): Signal; +function injectIsFetching(filters): Signal; ``` -Defined in: [packages/angular-query-experimental/src/inject-is-fetching.ts:63](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-fetching.ts#L63) +Defined in: [packages/angular-query/src/inject-is-fetching.ts:15](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-is-fetching.ts#L15) Injects a signal that tracks the number of queries that your application is loading or fetching in the background (useful for app-wide loading indicators). ## Parameters -### filters? +### filters -[`QueryFilters`](../interfaces/QueryFilters.md)\ +() => `QueryFilters` -The [QueryFilters](../interfaces/QueryFilters.md) to narrow down the matched queries. - -### options? - -[`InjectIsFetchingOptions`](../interfaces/InjectIsFetchingOptions.md) - -Additional configuration +A reactive factory for the filters. ## Returns diff --git a/docs/framework/angular/reference/functions/injectIsMutating.md b/docs/framework/angular/reference/functions/injectIsMutating.md index b3a8b063e60..c674046a97b 100644 --- a/docs/framework/angular/reference/functions/injectIsMutating.md +++ b/docs/framework/angular/reference/functions/injectIsMutating.md @@ -4,27 +4,21 @@ title: injectIsMutating --- ```ts -function injectIsMutating(filters?, options?): Signal; +function injectIsMutating(filters): Signal; ``` -Defined in: [packages/angular-query-experimental/src/inject-is-mutating.ts:46](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-mutating.ts#L46) +Defined in: [packages/angular-query/src/inject-is-mutating.ts:14](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-is-mutating.ts#L14) Injects a signal that tracks the number of mutations that your application currently has `pending` (useful for app-wide loading indicators). ## Parameters -### filters? +### filters -[`MutationFilters`](../interfaces/MutationFilters.md)\<`unknown`, `Error`, `unknown`, `unknown`\> +() => `MutationFilters` -The [MutationFilters](../interfaces/MutationFilters.md) to narrow down the matched mutations. - -### options? - -[`InjectIsMutatingOptions`](../interfaces/InjectIsMutatingOptions.md) - -Additional configuration +A reactive factory for the filters. ## Returns diff --git a/docs/framework/angular/reference/functions/injectIsRestoring.md b/docs/framework/angular/reference/functions/injectIsRestoring.md index c66a4901ce0..627aad15b52 100644 --- a/docs/framework/angular/reference/functions/injectIsRestoring.md +++ b/docs/framework/angular/reference/functions/injectIsRestoring.md @@ -4,22 +4,12 @@ title: injectIsRestoring --- ```ts -function injectIsRestoring(options?): Signal; +function injectIsRestoring(): Signal; ``` -Defined in: [packages/angular-query-experimental/src/inject-is-restoring.ts:35](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L35) +Defined in: [packages/angular-query/src/inject-is-restoring.ts:22](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-is-restoring.ts#L22) -Injects a signal that tracks whether a restore (e.g. from a persisted client, wired up via -`provideIsRestoring`) is currently in progress. `injectQuery` and friends also check this internally to -avoid race conditions between the restore and initializing queries. - -## Parameters - -### options? - -`InjectIsRestoringOptions` - -Additional configuration +Returns a readonly signal that is true while the persistence integration restores cached query data. It is false when no restoration is in progress. ## Returns diff --git a/docs/framework/angular/reference/functions/injectMutation.md b/docs/framework/angular/reference/functions/injectMutation.md index b4cca9d4cb3..a4050838d76 100644 --- a/docs/framework/angular/reference/functions/injectMutation.md +++ b/docs/framework/angular/reference/functions/injectMutation.md @@ -4,10 +4,10 @@ title: injectMutation --- ```ts -function injectMutation(injectMutationFn, options?): CreateMutationResult; +function injectMutation(optionsFn): CreateMutationResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-mutation.ts:174](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L174) +Defined in: [packages/angular-query/src/inject-mutation.ts:28](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-mutation.ts#L28) Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. `injectMutation` is the function for that. Unlike queries, mutations are not run automatically. @@ -32,19 +32,13 @@ Unlike queries, mutations are typically used to create/update/delete data or per ## Parameters -### injectMutationFn +### optionsFn () => [`CreateMutationOptions`](../interfaces/CreateMutationOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> A function that returns mutation options. Similar to `computed` from Angular, this function runs in the reactive context, so signals read inside it drive the mutation's options. -### options? - -[`InjectMutationOptions`](../interfaces/InjectMutationOptions.md) - -Additional configuration - ## Returns [`CreateMutationResult`](../type-aliases/CreateMutationResult.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\> diff --git a/docs/framework/angular/reference/functions/injectMutationState.md b/docs/framework/angular/reference/functions/injectMutationState.md index a96ac308998..2b14f5046e5 100644 --- a/docs/framework/angular/reference/functions/injectMutationState.md +++ b/docs/framework/angular/reference/functions/injectMutationState.md @@ -4,10 +4,10 @@ title: injectMutationState --- ```ts -function injectMutationState(injectMutationStateFn, options?): Signal; +function injectMutationState(options): Signal; ``` -Defined in: [packages/angular-query-experimental/src/inject-mutation-state.ts:106](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L106) +Defined in: [packages/angular-query/src/inject-mutation-state.ts:55](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-mutation-state.ts#L55) Injects a signal that gives you access to all mutations in the `MutationCache`. You can pass `filters` ([MutationFilters](../interfaces/MutationFilters.md)) to narrow down your mutations, and `select` to transform the mutation state. @@ -18,22 +18,20 @@ Injects a signal that gives you access to all mutations in the `MutationCache`. `TResult` = [`MutationState`](../interfaces/MutationState.md)\<`unknown`, `Error`, `unknown`, `unknown`\> +### TMutation + +`TMutation` *extends* `Mutation`\<`any`, `any`, `any`, `any`\> = `MutationTypeFromResult`\<`TResult`\> + ## Parameters -### injectMutationStateFn +### options -() => `MutationStateOptions`\<`TResult`\> +() => [`MutationStateOptions`](../type-aliases/MutationStateOptions.md)\<`TResult`, `TMutation`\> A function returning the `filters` to narrow down matched mutations, and an optional `select` to transform the mutation state. Similar to `computed` from Angular, this function runs in the reactive context, so signals read inside it re-narrow the matched mutations. -### options? - -[`InjectMutationStateOptions`](../interfaces/InjectMutationStateOptions.md) - -Additional configuration - ## Returns `Signal`\<`TResult`[]\> diff --git a/docs/framework/angular/reference/functions/injectQueries.md b/docs/framework/angular/reference/functions/injectQueries.md new file mode 100644 index 00000000000..f07d91a2b71 --- /dev/null +++ b/docs/framework/angular/reference/functions/injectQueries.md @@ -0,0 +1,49 @@ +--- +id: injectQueries +title: injectQueries +--- + +```ts +function injectQueries(optionsFn): Signal; +``` + +Defined in: [packages/angular-query/src/inject-queries.ts:43](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.ts#L43) + +Injects multiple queries that run in parallel and react to Angular signals. + +```ts +class UsersComponent { + readonly users = input.required>() + + readonly userQueries = injectQueries(() => ({ + queries: this.users().map((user) => ({ + queryKey: ['user', user.id], + queryFn: () => fetchUserById(user.id), + })), + })) +} +``` + +## Type Parameters + +### T + +`T` *extends* `any`[] + +### TCombinedResult + +`TCombinedResult` = `T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<`Head`\>\[`"data"`\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<`Head`\>\[`"data"`\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>\>, `GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<`Head`\>\[`"data"`\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GenericGetDefinedOrUndefinedQueryResult`\<`Head`, ...\[...\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<..., ...\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<..., ...\>\>, `GenericGetDefinedOrUndefinedQueryResult`\<`Head`, ...\[...\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<..., ...\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<..., ...\>\>, `GenericGetDefinedOrUndefinedQueryResult`\<`Head`, ...\[...\], [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<..., ...\>, [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<..., ...\>\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...(...)[]`\] *extends* \[\] ? \[\] : ... *extends* ... ? ... : ... : \[`...{ [K in (...)]: (...) }[]`\] : \[...\{ \[K in string \| number \| symbol\]: GenericGetDefinedOrUndefinedQueryResult\\], InferDataAndError\<(...)\>\["data"\], CreateQueryResult\<(...)\[(...)\], (...)\[(...)\]\>, DefinedCreateQueryResult\<(...)\[(...)\], (...)\[(...)\]\>\> \}\[\]\] : \{ \[K in string \| number \| symbol\]: GenericGetDefinedOrUndefinedQueryResult\\], InferDataAndError\\]\>\["data"\], CreateQueryResult\\]\>\["data"\], InferDataAndError\\]\>\["error"\]\>, DefinedCreateQueryResult\\]\>\["data"\], InferDataAndError\\]\>\["error"\]\>\> \} + +## Parameters + +### optionsFn + +() => [`InjectQueriesOptions`](../interfaces/InjectQueriesOptions.md)\<`T`, `TCombinedResult`\> + +A function that returns queries' options. + +## Returns + +`Signal`\<`TCombinedResult`\> + +A signal containing the query results in the same order as the input queries. diff --git a/docs/framework/angular/reference/functions/injectQuery.md b/docs/framework/angular/reference/functions/injectQuery.md index 2dca2d7fb19..860e5ddc567 100644 --- a/docs/framework/angular/reference/functions/injectQuery.md +++ b/docs/framework/angular/reference/functions/injectQuery.md @@ -3,16 +3,58 @@ id: injectQuery title: injectQuery --- -## Call Signature +# Function: injectQuery() + +Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + +**Basic example** +```ts +import { lastValueFrom } from 'rxjs' + +class ServiceOrComponent { + query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.#http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} +``` + +Similar to `computed` from Angular, the function passed to `injectQuery` will be run in the reactive context. +In the example below, the query will be automatically enabled and executed when the filter signal changes +to a truthy value. When the filter signal changes back to a falsy value, the query will be disabled. +**Reactive example** ```ts -function injectQuery(injectQueryFn, options?): DefinedCreateQueryResult; +class ServiceOrComponent { + filter = signal('') + + todosQuery = injectQuery(() => ({ + queryKey: ['todos', this.filter()], + queryFn: () => fetchTodos(this.filter()), + // Signals can be combined with expressions + enabled: !!this.filter(), + })) +} ``` -Defined in: [packages/angular-query-experimental/src/inject-query.ts:69](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L69) +## Param -This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the -resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). +A function that returns query options. + +## See + +https://tanstack.com/query/latest/docs/framework/angular/guides/queries + +## Call Signature + +```ts +function injectQuery(optionsFn): DefinedCreateQueryResult; +``` + +Defined in: [packages/angular-query/src/inject-query.ts:17](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-query.ts#L17) ### Type Parameters @@ -34,69 +76,21 @@ resulting `data` signal is never `undefined` (unless a `select` changes `TData` ### Parameters -#### injectQueryFn +#### optionsFn () => [`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -A function returning the [DefinedInitialDataOptions](../type-aliases/DefinedInitialDataOptions.md) to use — everything you -can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs -in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. - -#### options? - -[`InjectQueryOptions`](../interfaces/InjectQueryOptions.md) - -Additional configuration - ### Returns [`DefinedCreateQueryResult`](../type-aliases/DefinedCreateQueryResult.md)\<`TData`, `TError`\> -The query result, typed so that `data` is never `undefined` (unless a `select` changes `TData` to -include `undefined`). - -### See - - - https://tanstack.com/query/latest/docs/framework/angular/guides/queries - - [queryOptions](queryOptions.md) to share these options between `injectQuery` and imperative APIs like -`queryClient.fetchQuery`. - -### Example - -```angular-ts -@Component({ - selector: 'posts', - template: ` - - @if (postsQuery.isError()) { - Error: {{ postsQuery.error()?.message }} - } -
    - @for (post of postsQuery.data(); track post.id) { -
  • {{ post.title }}
  • - } -
- `, -}) -export class Posts { - readonly postsQuery = injectQuery(() => ({ - queryKey: ['posts'], - queryFn: fetchPosts, - initialData: [], - })) -} -``` - ## Call Signature ```ts -function injectQuery(injectQueryFn, options?): CreateQueryResult; +function injectQuery(optionsFn): CreateQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-query.ts:158](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L158) - -Injects a query: a declarative dependency on an asynchronous source of data that is tied to a unique key. +Defined in: [packages/angular-query/src/inject-query.ts:31](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-query.ts#L31) ### Type Parameters @@ -118,105 +112,21 @@ Injects a query: a declarative dependency on an asynchronous source of data that ### Parameters -#### injectQueryFn +#### optionsFn () => [`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -A function returning the [UndefinedInitialDataOptions](../type-aliases/UndefinedInitialDataOptions.md) to use — everything -you can pass to `injectQuery`. Similar to `computed` from Angular, this function runs in the reactive -context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. - -#### options? - -[`InjectQueryOptions`](../interfaces/InjectQueryOptions.md) - -Additional configuration - ### Returns [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`TData`, `TError`\> -The query result. `status()` is `'pending'` if there is no cached data to display, `'error'` if -the last fetch attempt failed, or `'success'` if the query has data to display. `isPending`/`isSuccess`/ -`isError` are type-guard methods for convenience. - -### See - - - https://tanstack.com/query/latest/docs/framework/angular/guides/queries - - [queryOptions](queryOptions.md) to share these options between `injectQuery` and imperative APIs like -`queryClient.fetchQuery`. - -### Examples - -```angular-ts -@Component({ - selector: 'posts', - template: ` - @if (postsQuery.isPending()) { - Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error()?.message }} - } @else { -
    - @for (post of postsQuery.data(); track post.id) { -
  • {{ post.title }}
  • - } -
- } - `, -}) -export class Posts { - readonly postsQuery = injectQuery(() => ({ - queryKey: ['posts'], - queryFn: fetchPosts, - })) -} -``` - -Similar to `computed` from Angular, the function passed to `injectQuery` runs in the reactive context. In -the example below, the query is automatically enabled and executed when the filter signal changes to a -truthy value. When the filter signal changes back to a falsy value, the query is disabled. -```angular-ts -@Component({ - selector: 'posts', - template: ` - - @if (postsQuery.isPending()) { - Loading... - } @else if (postsQuery.isError()) { - Error: {{ postsQuery.error()?.message }} - } @else { -
    - @for (post of postsQuery.data(); track post.id) { -
  • {{ post.title }}
  • - } -
- } - `, -}) -export class Posts { - readonly filter = signal('') - - readonly postsQuery = injectQuery(() => ({ - queryKey: ['posts', this.filter()], - queryFn: () => fetchPosts(this.filter()), - // Signals can be combined with expressions - enabled: !!this.filter(), - })) -} -``` - ## Call Signature ```ts -function injectQuery(injectQueryFn, options?): CreateQueryResult; +function injectQuery(optionsFn): CreateQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/inject-query.ts:185](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L185) - -This overload accepts the general [CreateQueryOptions](../interfaces/CreateQueryOptions.md) shape rather than the `initialData`-aware -overloads above, so whether `data` is defined can't be inferred from the call site — useful when wrapping -`injectQuery` in your own helper function that forwards caller-provided options. +Defined in: [packages/angular-query/src/inject-query.ts:45](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-query.ts#L45) ### Type Parameters @@ -238,26 +148,10 @@ overloads above, so whether `data` is defined can't be inferred from the call si ### Parameters -#### injectQueryFn - -() => [`CreateQueryOptions`](../interfaces/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> +#### optionsFn -A function that returns query options. Similar to `computed` from Angular, this -function runs in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive -the query. - -#### options? - -[`InjectQueryOptions`](../interfaces/InjectQueryOptions.md) - -Additional configuration +() => [`CreateQueryOptions`](../type-aliases/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> ### Returns [`CreateQueryResult`](../type-aliases/CreateQueryResult.md)\<`TData`, `TError`\> - -The query result. - -### See - -https://tanstack.com/query/latest/docs/framework/angular/guides/queries diff --git a/docs/framework/angular/reference/functions/injectQueryClient.md b/docs/framework/angular/reference/functions/injectQueryClient.md deleted file mode 100644 index a6f475f5e62..00000000000 --- a/docs/framework/angular/reference/functions/injectQueryClient.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: injectQueryClient -title: injectQueryClient ---- - -```ts -function injectQueryClient(injectOptions): QueryClient; -``` - -Defined in: [packages/angular-query-experimental/src/inject-query-client.ts:18](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query-client.ts#L18) - -Injects a `QueryClient` instance and allows passing a custom injector. - -## Parameters - -### injectOptions - -`InjectOptions` & `object` = `{}` - -Type of the options argument to inject and optionally a custom injector. - -## Returns - -[`QueryClient`](../classes/QueryClient.md) - -The `QueryClient` instance. - -## Deprecated - -Use `inject(QueryClient)` instead. -If you need to get a `QueryClient` from a custom injector, use `injector.get(QueryClient)`. - -**Example** -```ts -const queryClient = injectQueryClient(); -``` diff --git a/docs/framework/angular/reference/functions/mutationOptions.md b/docs/framework/angular/reference/functions/mutationOptions.md index 4b4a987169d..ff1a72c0950 100644 --- a/docs/framework/angular/reference/functions/mutationOptions.md +++ b/docs/framework/angular/reference/functions/mutationOptions.md @@ -9,7 +9,7 @@ title: mutationOptions function mutationOptions(options): WithRequired, "mutationKey">; ``` -Defined in: [packages/angular-query-experimental/src/mutation-options.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L40) +Defined in: [packages/angular-query/src/mutation-options.ts:39](https://github.com/TanStack/query/blob/main/packages/angular-query/src/mutation-options.ts#L39) You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. A `mutationKey` is required on this overload so the mutation can be looked up later, e.g. with @@ -85,7 +85,7 @@ export class SavingIndicator { function mutationOptions(options): Omit, "mutationKey">; ``` -Defined in: [packages/angular-query-experimental/src/mutation-options.ts:98](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/mutation-options.ts#L98) +Defined in: [packages/angular-query/src/mutation-options.ts:53](https://github.com/TanStack/query/blob/main/packages/angular-query/src/mutation-options.ts#L53) You can generally pass everything to `mutationOptions` that you can also pass to `injectMutation`. No `mutationKey` is required on this overload — use this when you don't need to target the mutation via a diff --git a/docs/framework/angular/reference/functions/provideAngularQuery.md b/docs/framework/angular/reference/functions/provideAngularQuery.md deleted file mode 100644 index d77bb0c7b7e..00000000000 --- a/docs/framework/angular/reference/functions/provideAngularQuery.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: provideAngularQuery -title: provideAngularQuery ---- - -```ts -function provideAngularQuery(queryClient): Provider[]; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:121](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L121) - -Sets up providers necessary to enable TanStack Query functionality for Angular applications. - -Allows configuring a `QueryClient`. - -## Parameters - -### queryClient - -[`QueryClient`](../classes/QueryClient.md) - -A `QueryClient` instance. - -## Returns - -`Provider`[] - -A set of providers to set up TanStack Query. - -## See - -https://tanstack.com/query/v5/docs/framework/angular/quick-start - -## Deprecated - -Use `provideTanStackQuery` instead. diff --git a/docs/framework/angular/reference/functions/provideIsRestoring.md b/docs/framework/angular/reference/functions/provideIsRestoring.md deleted file mode 100644 index cd2847e5ddc..00000000000 --- a/docs/framework/angular/reference/functions/provideIsRestoring.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -id: provideIsRestoring -title: provideIsRestoring ---- - -```ts -function provideIsRestoring(isRestoring): Provider; -``` - -Defined in: [packages/angular-query-experimental/src/inject-is-restoring.ts:48](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-restoring.ts#L48) - -Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a -restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can defer subscribing -to their observer (avoiding a race with the restore) until the restore signal flips back to `false`. - -## Parameters - -### isRestoring - -`Signal`\<`boolean`\> - -A readonly `Signal` that tracks the restore state. - -## Returns - -`Provider` - -A provider for the `isRestoring` signal. diff --git a/docs/framework/angular/reference/functions/provideQueryClient.md b/docs/framework/angular/reference/functions/provideQueryClient.md deleted file mode 100644 index a599930ff20..00000000000 --- a/docs/framework/angular/reference/functions/provideQueryClient.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: provideQueryClient -title: provideQueryClient ---- - -```ts -function provideQueryClient(queryClient): Provider; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:22](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L22) - -Usually [provideTanStackQuery](provideTanStackQuery.md) is used once to set up TanStack Query and the -[`QueryClient`](https://tanstack.com/query/latest/docs/reference/QueryClient) for the entire application — -it calls `provideQueryClient` internally. Use `provideQueryClient` directly to provide a different -`QueryClient` instance for part of the application, or for unit testing. - -## Parameters - -### queryClient - -A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - -[`QueryClient`](../classes/QueryClient.md) | `InjectionToken`\<[`QueryClient`](../classes/QueryClient.md)\> - -## Returns - -`Provider` - -A provider object that can be used to provide the `QueryClient` instance. - -## Example - -Providing a test-only `QueryClient` in a component test, without wiring up `provideTanStackQuery`'s other -defaults: -```ts -TestBed.configureTestingModule({ - providers: [provideQueryClient(new QueryClient())], -}) -``` diff --git a/docs/framework/angular/reference/functions/provideTanStackQuery.md b/docs/framework/angular/reference/functions/provideTanStackQuery.md index 0113a274dff..e50e8e9f560 100644 --- a/docs/framework/angular/reference/functions/provideTanStackQuery.md +++ b/docs/framework/angular/reference/functions/provideTanStackQuery.md @@ -4,85 +4,74 @@ title: provideTanStackQuery --- ```ts -function provideTanStackQuery(queryClient, ...features): Provider[]; +function provideTanStackQuery(queryClientFactory, ...features): EnvironmentProviders; ``` -Defined in: [packages/angular-query-experimental/src/providers.ts:102](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L102) +Defined in: [packages/angular-query/src/providers.ts:99](https://github.com/TanStack/query/blob/main/packages/angular-query/src/providers.ts#L99) -Sets up providers necessary to enable TanStack Query functionality for Angular applications. Allows -configuring a `QueryClient` and optional features such as developer tools. +Provides a `QueryClient` and optional TanStack Query features. +The factory runs once per injector in Angular's injection context, so it can +call `inject()` and each SSR request can receive an independent cache. -## Parameters - -### queryClient - -A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - -[`QueryClient`](../classes/QueryClient.md) | `InjectionToken`\<[`QueryClient`](../classes/QueryClient.md)\> - -### features - -...[`QueryFeatures`](../type-aliases/QueryFeatures.md)[] - -Optional features to configure additional Query functionality. - -## Returns - -`Provider`[] - -A set of providers to set up TanStack Query. - -## See - - - https://tanstack.com/query/v5/docs/framework/angular/quick-start - - withDevtools - -## Examples +**Example - standalone** ```ts -import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' +import { + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query' bootstrapApplication(AppComponent, { - providers: [provideTanStackQuery(new QueryClient())], + providers: [provideTanStackQuery(() => new QueryClient())], }) ``` -The same, in an `NgModule`-based application: -```ts -import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' +You can also enable optional developer tools by adding `withDevtools`. By +default the tools will then be loaded when your app is in development mode. -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule], - providers: [provideTanStackQuery(new QueryClient())], - bootstrap: [AppComponent], -}) -export class AppModule {} -``` - -Enabling optional developer tools by adding `withDevtools` — by default, the tools are then loaded when -your app is in development mode: ```ts -import { - provideTanStackQuery, - withDevtools, - QueryClient, -} from '@tanstack/angular-query-experimental' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' bootstrapApplication(AppComponent, { - providers: [provideTanStackQuery(new QueryClient(), withDevtools())], + providers: [ + provideTanStackQuery(() => new QueryClient(), withDevtools()), + ], }) ``` -Using an `InjectionToken` for the `QueryClient` — an advanced optimization that lets TanStack Query be -absent from the main application bundle, useful for including it on lazy-loaded routes only while still -sharing a `QueryClient`. This is a small optimization; for most applications it's preferable to provide -the `QueryClient` in the main application config, as in the examples above: +Resolve an existing token inside the factory when another provider owns client creation: + ```ts export const MY_QUERY_CLIENT = new InjectionToken('', { factory: () => new QueryClient(), }) -// In a lazy loaded route or lazy loaded component's providers array: -providers: [provideTanStackQuery(MY_QUERY_CLIENT)] +providers: [provideTanStackQuery(() => inject(MY_QUERY_CLIENT))] ``` + +## Parameters + +### queryClientFactory + +Creates or resolves a `QueryClient` in the injection context. + +() => `QueryClient` + +### features + +...readonly [`QueryFeature`](../interfaces/QueryFeature.md)[] + +Optional features to configure additional Query functionality. + +## Returns + +`EnvironmentProviders` + +A single EnvironmentProviders value (do not spread into `providers`). + +## See + + - https://tanstack.com/query/v5/docs/framework/angular/quick-start + - https://tanstack.com/query/v5/docs/framework/angular/devtools + - https://tanstack.com/query/latest/docs/framework/angular/guides/ssr diff --git a/docs/framework/angular/reference/functions/queryFeature.md b/docs/framework/angular/reference/functions/queryFeature.md deleted file mode 100644 index bd2bd0932c0..00000000000 --- a/docs/framework/angular/reference/functions/queryFeature.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: queryFeature -title: queryFeature ---- - -```ts -function queryFeature(kind, providers): QueryFeature; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:143](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L143) - -Helper function to create an object that represents a Query feature. - -## Type Parameters - -### TFeatureKind - -`TFeatureKind` *extends* `"Devtools"` \| `"PersistQueryClient"` - -## Parameters - -### kind - -`TFeatureKind` - -The kind of feature, e.g. `'Devtools'`. - -### providers - -`Provider`[] - -The Angular providers this feature contributes to `provideTanStackQuery`. - -## Returns - -[`QueryFeature`](../interfaces/QueryFeature.md)\<`TFeatureKind`\> - -A Query feature. diff --git a/docs/framework/angular/reference/functions/queryOptions.md b/docs/framework/angular/reference/functions/queryOptions.md index a1c901882e6..bfa2aab500b 100644 --- a/docs/framework/angular/reference/functions/queryOptions.md +++ b/docs/framework/angular/reference/functions/queryOptions.md @@ -3,20 +3,35 @@ id: queryOptions title: queryOptions --- -## Call Signature +Allows sharing and re-using query options in a type-safe way. + +The `queryKey` will be tagged with the type from `queryFn`. + +**Example** ```ts -function queryOptions(options): Omit, "queryFn"> & object & QueryKeyWithDataTag; + const { queryKey } = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(5), + // ^? Promise + }) + + const queryClient = new QueryClient() + const data = queryClient.getQueryData(queryKey) + // ^? number | undefined ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:151](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L151) +## Param -You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options -can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is -required and is the query key to generate options for. +The query options to tag with the type from `queryFn`. + +## Call Signature + +```ts +function queryOptions(options): CreateQueryOptions & object & QueryKeyWithDataTag; +``` -This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless -a `select` changes `TData` to include `undefined`). +Defined in: [packages/angular-query/src/query-options.ts:50](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L50) ### Type Parameters @@ -42,48 +57,9 @@ a `select` changes `TData` to include `undefined`). [`DefinedInitialDataOptions`](../type-aliases/DefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The [DefinedInitialDataOptions](../type-aliases/DefinedInitialDataOptions.md) to use — everything you can pass to `injectQuery`, -with `initialData` set. - ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. - -### See - - - [injectQuery](injectQuery.md) to run a query with these options. - - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. - -### Example - -```angular-ts -import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' - -export const postsOptions = queryOptions({ - queryKey: ['posts'], - queryFn: fetchPosts, - initialData: [], -}) - -@Component({ - selector: 'posts', - template: ` - - @if (postsQuery.isError()) { - Error: {{ postsQuery.error()?.message }} - } -
    - @for (post of postsQuery.data(); track post.id) { -
  • {{ post.title }}
  • - } -
- `, -}) -export class Posts { - readonly postsQuery = injectQuery(() => postsOptions) -} -``` +[`CreateQueryOptions`](../type-aliases/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> ## Call Signature @@ -91,11 +67,7 @@ export class Posts { function queryOptions(options): OmitKeyof, "queryFn"> & object & QueryKeyWithDataTag; ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:200](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L200) - -You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options -can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is -required and is the query key to generate options for. +Defined in: [packages/angular-query/src/query-options.ts:60](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L60) ### Type Parameters @@ -121,46 +93,9 @@ required and is the query key to generate options for. [`UnusedSkipTokenOptions`](../type-aliases/UnusedSkipTokenOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The [UnusedSkipTokenOptions](../type-aliases/UnusedSkipTokenOptions.md) to use — everything you can pass to `injectQuery`. - ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. - -### See - - - [injectQuery](injectQuery.md) to run a query with these options. - - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. - -### Example - -A parameterized factory, so the same options object can be reused per `id`: -```angular-ts -import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' - -export const postOptions = (id: string) => - queryOptions({ - queryKey: ['post', id], - queryFn: () => fetchPost(id), - }) - -@Component({ - selector: 'post', - template: ` - @if (postQuery.isPending()) { - Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error()?.message }} - } @else { -

{{ postQuery.data().title }}

- } - `, -}) -export class Post { - readonly id = signal('1') - readonly postQuery = injectQuery(() => postOptions(this.id())) -} -``` +`OmitKeyof`\<[`CreateQueryOptions`](../type-aliases/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\>, `"queryFn"`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> ## Call Signature @@ -168,11 +103,7 @@ export class Post { function queryOptions(options): CreateQueryOptions & object & QueryKeyWithDataTag; ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:281](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L281) - -You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options -can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is -required and is the query key to generate options for. +Defined in: [packages/angular-query/src/query-options.ts:70](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L70) ### Type Parameters @@ -198,77 +129,6 @@ required and is the query key to generate options for. [`UndefinedInitialDataOptions`](../type-aliases/UndefinedInitialDataOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> -The [UndefinedInitialDataOptions](../type-aliases/UndefinedInitialDataOptions.md) to use — everything you can pass to `injectQuery`. - ### Returns -The same options object, typed so that `queryKey` carries the inferred data type. - -### See - - - [injectQuery](injectQuery.md) to run a query with these options. - - [The Query Options API](https://tkdodo.eu/blog/the-query-options-api) for more on this pattern. - -### Remarks - -This is the only overload that accepts `queryFn: skipToken`, shown below. - -### Examples - -A parameterized factory, so the same options object can be reused per `id`: -```angular-ts -import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' - -export const postOptions = (id: string) => - queryOptions({ - queryKey: ['post', id], - queryFn: () => fetchPost(id), - }) - -@Component({ - selector: 'post', - template: ` - @if (postQuery.isPending()) { - Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error()?.message }} - } @else { -

{{ postQuery.data().title }}

- } - `, -}) -export class Post { - readonly id = signal('1') - readonly postQuery = injectQuery(() => postOptions(this.id())) -} -``` - -A factory that disables the query, type safe, until `postId` is set: -```angular-ts -import { queryOptions, skipToken, injectQuery } from '@tanstack/angular-query-experimental' - -export const postOptions = (postId: number | undefined) => - queryOptions({ - queryKey: ['post', postId], - queryFn: postId != null ? () => fetchPost(postId) : skipToken, - }) - -@Component({ - selector: 'post', - template: ` - @if (postId() == null) { - Select a post - } @else if (postQuery.isPending()) { - Loading... - } @else if (postQuery.isError()) { - Error: {{ postQuery.error()?.message }} - } @else { -

{{ postQuery.data().title }}

- } - `, -}) -export class Post { - readonly postId = signal(undefined) - readonly postQuery = injectQuery(() => postOptions(this.postId())) -} -``` +[`CreateQueryOptions`](../type-aliases/CreateQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`\> & `object` & `QueryKeyWithDataTag`\<`TQueryKey`, `TQueryFnData`, `TError`\> diff --git a/docs/framework/angular/reference/functions/toResource.md b/docs/framework/angular/reference/functions/toResource.md new file mode 100644 index 00000000000..3baecfef1dd --- /dev/null +++ b/docs/framework/angular/reference/functions/toResource.md @@ -0,0 +1,34 @@ +--- +id: toResource +title: toResource +--- + +```ts +function toResource(query): QueryResource; +``` + +Defined in: [packages/angular-query/src/query-resource.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-resource.ts#L40) + +Converts an Angular Query result into Angular's Resource interface. + +Call this function once and reuse the returned resource. + +## Type Parameters + +### TValue + +`TValue` + +## Parameters + +### query + +`QueryResourceSource`\<`TValue`\> + +An Angular Query or infinite-query result. + +## Returns + +[`QueryResource`](../interfaces/QueryResource.md)\<`TValue`\> + +A Resource-compatible view of the query result. diff --git a/docs/framework/angular/reference/functions/withHydrationKey.md b/docs/framework/angular/reference/functions/withHydrationKey.md new file mode 100644 index 00000000000..800b9158ac7 --- /dev/null +++ b/docs/framework/angular/reference/functions/withHydrationKey.md @@ -0,0 +1,35 @@ +--- +id: withHydrationKey +title: withHydrationKey +--- + +```ts +function withHydrationKey(key): QueryFeature; +``` + +Defined in: [packages/angular-query/src/providers.ts:167](https://github.com/TanStack/query/blob/main/packages/angular-query/src/providers.ts#L167) + +Sets a non-default serialization key for this injector's `QueryClient` cache (server dehydrate / +browser hydrate via `TransferState`). Use this when you have multiple `QueryClient` instances +so each has its own key. The default key applies when you do not add this feature. + +```ts +providers: [ + provideTanStackQuery( + () => new QueryClient(), + withHydrationKey('my-secondary-query-cache'), + ), +] +``` + +## Parameters + +### key + +`string` + +A unique string for this client's `TransferState` entry. + +## Returns + +[`QueryFeature`](../interfaces/QueryFeature.md) diff --git a/docs/framework/angular/reference/functions/withNoQueryHydration.md b/docs/framework/angular/reference/functions/withNoQueryHydration.md new file mode 100644 index 00000000000..b0f003789b4 --- /dev/null +++ b/docs/framework/angular/reference/functions/withNoQueryHydration.md @@ -0,0 +1,16 @@ +--- +id: withNoQueryHydration +title: withNoQueryHydration +--- + +```ts +function withNoQueryHydration(): QueryFeature; +``` + +Defined in: [packages/angular-query/src/providers.ts:181](https://github.com/TanStack/query/blob/main/packages/angular-query/src/providers.ts#L181) + +Disables `TransferState` hydration and dehydration for the current environment injector. + +## Returns + +[`QueryFeature`](../interfaces/QueryFeature.md) diff --git a/docs/framework/angular/reference/index.md b/docs/framework/angular/reference/index.md index 4b10910da24..9b511013b2c 100644 --- a/docs/framework/angular/reference/index.md +++ b/docs/framework/angular/reference/index.md @@ -1,104 +1,30 @@ --- -id: "@tanstack/angular-query-experimental" -title: "@tanstack/angular-query-experimental" +id: "@tanstack/angular-query" +title: "@tanstack/angular-query" --- -## Classes - -- [CancelledError](classes/CancelledError.md) -- [InfiniteQueryObserver](classes/InfiniteQueryObserver.md) -- [Mutation](classes/Mutation.md) -- [MutationCache](classes/MutationCache.md) -- [MutationObserver](classes/MutationObserver.md) -- [QueriesObserver](classes/QueriesObserver.md) -- [Query](classes/Query.md) -- [QueryCache](classes/QueryCache.md) -- [QueryClient](classes/QueryClient.md) -- [QueryObserver](classes/QueryObserver.md) - ## Interfaces +- [BaseInfiniteQueryNarrowing](interfaces/BaseInfiniteQueryNarrowing.md) - [BaseMutationNarrowing](interfaces/BaseMutationNarrowing.md) - [BaseQueryNarrowing](interfaces/BaseQueryNarrowing.md) -- [CancelOptions](interfaces/CancelOptions.md) -- [CreateBaseQueryOptions](interfaces/CreateBaseQueryOptions.md) - [CreateInfiniteQueryOptions](interfaces/CreateInfiniteQueryOptions.md) - [CreateMutationOptions](interfaces/CreateMutationOptions.md) -- [CreateQueryOptions](interfaces/CreateQueryOptions.md) -- [DefaultOptions](interfaces/DefaultOptions.md) -- [DehydratedState](interfaces/DehydratedState.md) -- [DehydrateOptions](interfaces/DehydrateOptions.md) -- [~~EnsureQueryDataOptions~~](interfaces/EnsureQueryDataOptions.md) -- [FetchNextPageOptions](interfaces/FetchNextPageOptions.md) -- [FetchPreviousPageOptions](interfaces/FetchPreviousPageOptions.md) -- [~~FetchQueryOptions~~](interfaces/FetchQueryOptions.md) -- [FocusManager](interfaces/FocusManager.md) -- [HydrateOptions](interfaces/HydrateOptions.md) -- [InfiniteData](interfaces/InfiniteData.md) -- [InfiniteQueryObserverBaseResult](interfaces/InfiniteQueryObserverBaseResult.md) -- [InfiniteQueryObserverLoadingErrorResult](interfaces/InfiniteQueryObserverLoadingErrorResult.md) -- [InfiniteQueryObserverLoadingResult](interfaces/InfiniteQueryObserverLoadingResult.md) -- [InfiniteQueryObserverOptions](interfaces/InfiniteQueryObserverOptions.md) -- [InfiniteQueryObserverPendingResult](interfaces/InfiniteQueryObserverPendingResult.md) -- [InfiniteQueryObserverPlaceholderResult](interfaces/InfiniteQueryObserverPlaceholderResult.md) -- [InfiniteQueryObserverRefetchErrorResult](interfaces/InfiniteQueryObserverRefetchErrorResult.md) -- [InfiniteQueryObserverSuccessResult](interfaces/InfiniteQueryObserverSuccessResult.md) -- [InfiniteQueryPageParamsOptions](interfaces/InfiniteQueryPageParamsOptions.md) -- [InitialPageParam](interfaces/InitialPageParam.md) -- [InjectInfiniteQueryOptions](interfaces/InjectInfiniteQueryOptions.md) -- [InjectIsFetchingOptions](interfaces/InjectIsFetchingOptions.md) -- [InjectIsMutatingOptions](interfaces/InjectIsMutatingOptions.md) -- [InjectMutationOptions](interfaces/InjectMutationOptions.md) -- [InjectMutationStateOptions](interfaces/InjectMutationStateOptions.md) -- [InjectQueryOptions](interfaces/InjectQueryOptions.md) -- [InvalidateOptions](interfaces/InvalidateOptions.md) -- [InvalidateQueryFilters](interfaces/InvalidateQueryFilters.md) -- [MutateOptions](interfaces/MutateOptions.md) -- [MutationCacheConfig](interfaces/MutationCacheConfig.md) -- [MutationFilters](interfaces/MutationFilters.md) -- [MutationObserverBaseResult](interfaces/MutationObserverBaseResult.md) -- [MutationObserverErrorResult](interfaces/MutationObserverErrorResult.md) -- [MutationObserverIdleResult](interfaces/MutationObserverIdleResult.md) -- [MutationObserverLoadingResult](interfaces/MutationObserverLoadingResult.md) -- [MutationObserverOptions](interfaces/MutationObserverOptions.md) -- [MutationObserverSuccessResult](interfaces/MutationObserverSuccessResult.md) -- [MutationOptions](interfaces/MutationOptions.md) -- [MutationState](interfaces/MutationState.md) -- [NotifyEvent](interfaces/NotifyEvent.md) -- [OnlineManager](interfaces/OnlineManager.md) -- [QueriesObserverOptions](interfaces/QueriesObserverOptions.md) -- [QueryCacheConfig](interfaces/QueryCacheConfig.md) -- [QueryClientConfig](interfaces/QueryClientConfig.md) -- [QueryExecuteOptions](interfaces/QueryExecuteOptions.md) +- [DefinedInfiniteQueryNarrowing](interfaces/DefinedInfiniteQueryNarrowing.md) +- [DefinedQueryNarrowing](interfaces/DefinedQueryNarrowing.md) +- [InjectQueriesOptions](interfaces/InjectQueriesOptions.md) - [QueryFeature](interfaces/QueryFeature.md) -- [QueryFilters](interfaces/QueryFilters.md) -- [QueryObserverBaseResult](interfaces/QueryObserverBaseResult.md) -- [QueryObserverLoadingErrorResult](interfaces/QueryObserverLoadingErrorResult.md) -- [QueryObserverLoadingResult](interfaces/QueryObserverLoadingResult.md) -- [QueryObserverOptions](interfaces/QueryObserverOptions.md) -- [QueryObserverPendingResult](interfaces/QueryObserverPendingResult.md) -- [QueryObserverPlaceholderResult](interfaces/QueryObserverPlaceholderResult.md) -- [QueryObserverRefetchErrorResult](interfaces/QueryObserverRefetchErrorResult.md) -- [QueryObserverSuccessResult](interfaces/QueryObserverSuccessResult.md) -- [QueryOptions](interfaces/QueryOptions.md) -- [QueryState](interfaces/QueryState.md) -- [RefetchOptions](interfaces/RefetchOptions.md) -- [RefetchQueryFilters](interfaces/RefetchQueryFilters.md) -- [Register](interfaces/Register.md) -- [ResetOptions](interfaces/ResetOptions.md) -- [ResultOptions](interfaces/ResultOptions.md) -- [SetDataOptions](interfaces/SetDataOptions.md) -- [TimeoutManager](interfaces/TimeoutManager.md) +- [QueryResource](interfaces/QueryResource.md) ## Type Aliases -- [AnyDataTag](type-aliases/AnyDataTag.md) - [CreateBaseMutationResult](type-aliases/CreateBaseMutationResult.md) - [CreateBaseQueryResult](type-aliases/CreateBaseQueryResult.md) - [CreateInfiniteQueryResult](type-aliases/CreateInfiniteQueryResult.md) - [CreateMutateAsyncFunction](type-aliases/CreateMutateAsyncFunction.md) - [CreateMutateFunction](type-aliases/CreateMutateFunction.md) - [CreateMutationResult](type-aliases/CreateMutationResult.md) +- [CreateQueryOptions](type-aliases/CreateQueryOptions.md) - [CreateQueryResult](type-aliases/CreateQueryResult.md) - [DataTag](type-aliases/DataTag.md) - [dataTagErrorSymbol](type-aliases/dataTagErrorSymbol.md) @@ -111,59 +37,10 @@ title: "@tanstack/angular-query-experimental" - [DefinedInfiniteQueryObserverResult](type-aliases/DefinedInfiniteQueryObserverResult.md) - [DefinedInitialDataInfiniteOptions](type-aliases/DefinedInitialDataInfiniteOptions.md) - [DefinedInitialDataOptions](type-aliases/DefinedInitialDataOptions.md) -- [DefinedQueryObserverResult](type-aliases/DefinedQueryObserverResult.md) -- [DevtoolsFeature](type-aliases/DevtoolsFeature.md) -- [DistributiveOmit](type-aliases/DistributiveOmit.md) -- [~~EnsureInfiniteQueryDataOptions~~](type-aliases/EnsureInfiniteQueryDataOptions.md) -- [~~FetchInfiniteQueryOptions~~](type-aliases/FetchInfiniteQueryOptions.md) -- [FetchStatus](type-aliases/FetchStatus.md) -- [GetNextPageParamFunction](type-aliases/GetNextPageParamFunction.md) -- [GetPreviousPageParamFunction](type-aliases/GetPreviousPageParamFunction.md) -- [InferDataFromTag](type-aliases/InferDataFromTag.md) -- [InferErrorFromTag](type-aliases/InferErrorFromTag.md) -- [InfiniteQueryExecuteOptions](type-aliases/InfiniteQueryExecuteOptions.md) -- [InfiniteQueryObserverResult](type-aliases/InfiniteQueryObserverResult.md) -- [InitialDataFunction](type-aliases/InitialDataFunction.md) -- [ManagedTimerId](type-aliases/ManagedTimerId.md) -- [MutateFunction](type-aliases/MutateFunction.md) -- [MutateFunctionRest](type-aliases/MutateFunctionRest.md) -- [MutationCacheNotifyEvent](type-aliases/MutationCacheNotifyEvent.md) -- [MutationFunction](type-aliases/MutationFunction.md) -- [MutationFunctionContext](type-aliases/MutationFunctionContext.md) -- [MutationKey](type-aliases/MutationKey.md) -- [MutationMeta](type-aliases/MutationMeta.md) -- [MutationObserverResult](type-aliases/MutationObserverResult.md) -- [MutationScope](type-aliases/MutationScope.md) -- [MutationStatus](type-aliases/MutationStatus.md) -- [NetworkMode](type-aliases/NetworkMode.md) -- [NonUndefinedGuard](type-aliases/NonUndefinedGuard.md) -- [NotifyEventType](type-aliases/NotifyEventType.md) -- [NotifyOnChangeProps](type-aliases/NotifyOnChangeProps.md) -- [OmitKeyof](type-aliases/OmitKeyof.md) -- [Override](type-aliases/Override.md) -- [PersistQueryClientFeature](type-aliases/PersistQueryClientFeature.md) -- [PlaceholderDataFunction](type-aliases/PlaceholderDataFunction.md) +- [MutationStateOptions](type-aliases/MutationStateOptions.md) - [QueriesOptions](type-aliases/QueriesOptions.md) - [QueriesPlaceholderDataFunction](type-aliases/QueriesPlaceholderDataFunction.md) - [QueriesResults](type-aliases/QueriesResults.md) -- [QueryBooleanOption](type-aliases/QueryBooleanOption.md) -- [QueryCacheNotifyEvent](type-aliases/QueryCacheNotifyEvent.md) -- [QueryFeatures](type-aliases/QueryFeatures.md) -- [QueryFunction](type-aliases/QueryFunction.md) -- [QueryFunctionContext](type-aliases/QueryFunctionContext.md) -- [QueryKey](type-aliases/QueryKey.md) -- [QueryKeyHashFunction](type-aliases/QueryKeyHashFunction.md) -- [QueryKeyWithDataTag](type-aliases/QueryKeyWithDataTag.md) -- [QueryMeta](type-aliases/QueryMeta.md) -- [QueryObserverResult](type-aliases/QueryObserverResult.md) -- [QueryPersister](type-aliases/QueryPersister.md) -- [QueryStatus](type-aliases/QueryStatus.md) -- [SkipToken](type-aliases/SkipToken.md) -- [StaleTime](type-aliases/StaleTime.md) -- [StaleTimeFunction](type-aliases/StaleTimeFunction.md) -- [ThrowOnError](type-aliases/ThrowOnError.md) -- [TimeoutCallback](type-aliases/TimeoutCallback.md) -- [TimeoutProvider](type-aliases/TimeoutProvider.md) - [UndefinedInitialDataInfiniteOptions](type-aliases/UndefinedInitialDataInfiniteOptions.md) - [UndefinedInitialDataOptions](type-aliases/UndefinedInitialDataOptions.md) - [UnsetMarker](type-aliases/UnsetMarker.md) @@ -202,20 +79,11 @@ title: "@tanstack/angular-query-experimental" - [injectIsRestoring](functions/injectIsRestoring.md) - [injectMutation](functions/injectMutation.md) - [injectMutationState](functions/injectMutationState.md) +- [injectQueries](functions/injectQueries.md) - [injectQuery](functions/injectQuery.md) -- [~~injectQueryClient~~](functions/injectQueryClient.md) -- [~~isCancelledError~~](functions/isCancelledError.md) -- [keepPreviousData](functions/keepPreviousData.md) -- [matchMutation](functions/matchMutation.md) -- [matchQuery](functions/matchQuery.md) - [mutationOptions](functions/mutationOptions.md) -- [noop](functions/noop.md) -- [partialMatchKey](functions/partialMatchKey.md) -- [~~provideAngularQuery~~](functions/provideAngularQuery.md) -- [provideIsRestoring](functions/provideIsRestoring.md) -- [provideQueryClient](functions/provideQueryClient.md) - [provideTanStackQuery](functions/provideTanStackQuery.md) -- [queryFeature](functions/queryFeature.md) - [queryOptions](functions/queryOptions.md) -- [replaceEqualDeep](functions/replaceEqualDeep.md) -- [shouldThrowError](functions/shouldThrowError.md) +- [toResource](functions/toResource.md) +- [withHydrationKey](functions/withHydrationKey.md) +- [withNoQueryHydration](functions/withNoQueryHydration.md) diff --git a/docs/framework/angular/reference/interfaces/BaseInfiniteQueryNarrowing.md b/docs/framework/angular/reference/interfaces/BaseInfiniteQueryNarrowing.md new file mode 100644 index 00000000000..252eb9e27b8 --- /dev/null +++ b/docs/framework/angular/reference/interfaces/BaseInfiniteQueryNarrowing.md @@ -0,0 +1,46 @@ +--- +id: BaseInfiniteQueryNarrowing +title: BaseInfiniteQueryNarrowing +--- + +Defined in: [packages/angular-query/src/types.ts:114](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L114) + +## Type Parameters + +### TData + +`TData` = `unknown` + +### TError + +`TError` = `DefaultError` + +## Properties + +### isError + +```ts +isError: SignalFunction<(this) => this is CreateInfiniteQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:127](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L127) + +*** + +### isPending + +```ts +isPending: SignalFunction<(this) => this is CreateInfiniteQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:136](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L136) + +*** + +### isSuccess + +```ts +isSuccess: SignalFunction<(this) => this is CreateInfiniteQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:118](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L118) diff --git a/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md b/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md index 6a148af0310..af8dfb3884d 100644 --- a/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md +++ b/docs/framework/angular/reference/interfaces/BaseMutationNarrowing.md @@ -3,11 +3,7 @@ id: BaseMutationNarrowing title: BaseMutationNarrowing --- -Defined in: [packages/angular-query-experimental/src/types.ts:328](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L328) - -The `isSuccess`/`isError`/`isPending`/`isIdle` methods on a mutation result. Each is both a `Signal` -(its current boolean value is read reactively without calling it) and a type-guard function you can -call — `if (mutation.isSuccess())` — so that `mutation.data` narrows away `undefined` inside the branch. +Defined in: [packages/angular-query/src/types.ts:297](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L297) ## Type Parameters @@ -44,7 +40,7 @@ their `onMutateResult` parameter — useful for optimistic-update rollback data. isError: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:351](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L351) +Defined in: [packages/angular-query/src/types.ts:320](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L320) *** @@ -54,7 +50,7 @@ Defined in: [packages/angular-query-experimental/src/types.ts:351](https://githu isIdle: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:385](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L385) +Defined in: [packages/angular-query/src/types.ts:354](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L354) *** @@ -64,7 +60,7 @@ Defined in: [packages/angular-query-experimental/src/types.ts:385](https://githu isPending: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:368](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L368) +Defined in: [packages/angular-query/src/types.ts:337](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L337) *** @@ -74,4 +70,4 @@ Defined in: [packages/angular-query-experimental/src/types.ts:368](https://githu isSuccess: SignalFunction<(this) => this is CreateMutationResult, { mutate: CreateMutateFunction }> & { mutateAsync: CreateMutateAsyncFunction }>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:334](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L334) +Defined in: [packages/angular-query/src/types.ts:303](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L303) diff --git a/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md b/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md index 48ebe0aa8c7..38ffb777ec0 100644 --- a/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md +++ b/docs/framework/angular/reference/interfaces/BaseQueryNarrowing.md @@ -3,12 +3,7 @@ id: BaseQueryNarrowing title: BaseQueryNarrowing --- -Defined in: [packages/angular-query-experimental/src/types.ts:83](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L83) - -The `isSuccess`/`isError`/`isPending` methods on a query result. Unlike `react-query`'s derived booleans, -these are type-guard methods you call — `if (query.isSuccess())` — so that `query.data` narrows away -`undefined` inside the branch, the same way `status` narrowing works on the plain object `react-query` -returns. +Defined in: [packages/angular-query/src/types.ts:60](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L60) ## Type Parameters @@ -26,60 +21,30 @@ The type of errors your `queryFn` may throw. ## Properties -### isError() +### isError ```ts -isError: (this) => this is CreateBaseQueryResult>; +isError: SignalFunction<(this) => this is CreateBaseQueryResult>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:91](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L91) - -#### Parameters - -##### this - -[`CreateBaseQueryResult`](../type-aliases/CreateBaseQueryResult.md)\<`TData`, `TError`\> - -#### Returns - -`this is CreateBaseQueryResult>` +Defined in: [packages/angular-query/src/types.ts:70](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L70) *** -### isPending() +### isPending ```ts -isPending: (this) => this is CreateBaseQueryResult>; +isPending: SignalFunction<(this) => this is CreateBaseQueryResult>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:98](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L98) - -#### Parameters - -##### this - -[`CreateBaseQueryResult`](../type-aliases/CreateBaseQueryResult.md)\<`TData`, `TError`\> - -#### Returns - -`this is CreateBaseQueryResult>` +Defined in: [packages/angular-query/src/types.ts:79](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L79) *** -### isSuccess() +### isSuccess ```ts -isSuccess: (this) => this is CreateBaseQueryResult>; +isSuccess: SignalFunction<(this) => this is CreateBaseQueryResult>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:84](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L84) - -#### Parameters - -##### this - -[`CreateBaseQueryResult`](../type-aliases/CreateBaseQueryResult.md)\<`TData`, `TError`\> - -#### Returns - -`this is CreateBaseQueryResult>` +Defined in: [packages/angular-query/src/types.ts:61](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L61) diff --git a/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md deleted file mode 100644 index c2f4508ffbc..00000000000 --- a/docs/framework/angular/reference/interfaces/CreateBaseQueryOptions.md +++ /dev/null @@ -1,636 +0,0 @@ ---- -id: CreateBaseQueryOptions -title: CreateBaseQueryOptions ---- - -Defined in: [packages/angular-query-experimental/src/types.ts:34](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L34) - -The options shared across `angular-query-experimental`'s query functions. Extends -[QueryObserverOptions](QueryObserverOptions.md) from `@tanstack/query-core` as-is — unlike `react-query`, -`angular-query-experimental` has no extra framework-specific option here. - -## Extends - -- [`QueryObserverOptions`](QueryObserverOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryData`, `TQueryKey`\> - -## Type Parameters - -### TQueryFnData - -`TQueryFnData` = `unknown` - -The type your `queryFn` resolves to. - -### TError - -`TError` = [`DefaultError`](../type-aliases/DefaultError.md) - -The type of errors your `queryFn` may throw. - -### TData - -`TData` = `TQueryFnData` - -The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no -`select` is used. - -### TQueryData - -`TQueryData` = `TQueryFnData` - -The type of the data actually held in the query cache — the input to `select` and -`placeholderData`. Defaults to, and is usually the same as, `TQueryFnData`. - -### TQueryKey - -`TQueryKey` *extends* [`QueryKey`](../type-aliases/QueryKey.md) = [`QueryKey`](../type-aliases/QueryKey.md) - -The type of your `queryKey`. - -## Properties - -### enabled? - -```ts -optional enabled: QueryBooleanOption; -``` - -Defined in: [packages/query-core/src/types.ts:398](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L398) - -Set this to `false` or a function that returns `false` to disable automatic refetching when the query mounts or changes query keys. -To refetch the query, use the `refetch` method returned from the `useQuery` instance. -Accepts a boolean or function that returns a boolean. - -Defaults to `true`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`enabled`](QueryObserverOptions.md#enabled) - -*** - -### gcTime? - -```ts -optional gcTime: number; -``` - -Defined in: [packages/query-core/src/types.ts:277](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L277) - -The time in milliseconds that unused/inactive cache data remains in memory. -When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. -When different garbage collection times are specified, the longest one will be used. -Setting it to `Infinity` will disable garbage collection. - -Defaults to `5 * 60 * 1000` (5 minutes), or `Infinity` during SSR. - -Note: the maximum allowed time is about 24 days, imposed by `setTimeout`'s 32-bit signed integer delay — see -`timeoutManager.setTimeoutProvider` for a workaround. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`gcTime`](QueryObserverOptions.md#gctime) - -*** - -### initialData? - -```ts -optional initialData: - | TQueryData -| InitialDataFunction; -``` - -Defined in: [packages/query-core/src/types.ts:317](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L317) - -If set, this value will be used as the initial data for the query cache (as long as the query hasn't been -created or cached yet). -If set to a function, the function will be called **once** during the shared/root query initialization, and be -expected to synchronously return the initial data. -Initial data is considered stale by default unless a `staleTime` has been set. -`initialData` **is persisted** to the cache. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`initialData`](QueryObserverOptions.md#initialdata) - -*** - -### initialDataUpdatedAt? - -```ts -optional initialDataUpdatedAt: number | () => number | undefined; -``` - -Defined in: [packages/query-core/src/types.ts:321](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L321) - -If set, this value will be used as the time (in milliseconds) of when the `initialData` itself was last updated. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`initialDataUpdatedAt`](QueryObserverOptions.md#initialdataupdatedat) - -*** - -### maxPages? - -```ts -optional maxPages: number; -``` - -Defined in: [packages/query-core/src/types.ts:345](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L345) - -Maximum number of pages to store in the data of an infinite query. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`maxPages`](QueryObserverOptions.md#maxpages) - -*** - -### meta? - -```ts -optional meta: Record; -``` - -Defined in: [packages/query-core/src/types.ts:341](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L341) - -Additional payload to be stored on each query. -Use this property to pass information that can be used in other places. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`meta`](QueryObserverOptions.md#meta) - -*** - -### networkMode? - -```ts -optional networkMode: NetworkMode; -``` - -Defined in: [packages/query-core/src/types.ts:265](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L265) - -Controls whether a query is allowed to run based on the current network connectivity. - -Defaults to `'online'`. - -#### See - -[Network Mode](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) for more information. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`networkMode`](QueryObserverOptions.md#networkmode) - -*** - -### notifyOnChangeProps? - -```ts -optional notifyOnChangeProps: NotifyOnChangeProps; -``` - -Defined in: [packages/query-core/src/types.ts:484](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L484) - -If set, the component will only re-render if any of the listed properties change. -When set to `['data', 'error']`, the component will only re-render when the `data` or `error` properties change. -When set to `'all'`, the component will re-render whenever a query is updated. -When set to a function, the function will be executed to compute the list of properties. - -Defaults to `undefined`, in which case property access is tracked automatically, and the -component only re-renders when one of the tracked properties changes. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`notifyOnChangeProps`](QueryObserverOptions.md#notifyonchangeprops) - -*** - -### persister()? - -```ts -optional persister: (queryFn, context, query) => TQueryFnData | Promise; -``` - -Defined in: [packages/query-core/src/types.ts:290](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L290) - -This option can be used to persist the result of a query to an external storage, bypassing the need to actually -call the `queryFn`. Useful for persisting a query's data across e.g. server/client boundaries. - -#### Parameters - -##### queryFn - -[`QueryFunction`](../type-aliases/QueryFunction.md)\<`TQueryFnData`, `NoInfer`\<`TQueryKey`\>, `never`\> - -##### context - -###### client - -[`QueryClient`](../classes/QueryClient.md) - -###### direction? - -`unknown` - -**Deprecated** - -if you want access to the direction, you can add it to the pageParam - -###### meta - -`Record`\<`string`, `unknown`\> \| `undefined` - -###### pageParam? - -`unknown` - -###### queryKey - -`NoInfer`\<`TQueryKey`\> - -###### signal - -`AbortSignal` - -##### query - -[`Query`](../classes/Query.md) - -#### Returns - -`TQueryFnData` \| `Promise`\<`TQueryFnData`\> - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`persister`](QueryObserverOptions.md#persister) - -*** - -### placeholderData? - -```ts -optional placeholderData: - | NonFunctionGuard -| PlaceholderDataFunction, TError, NonFunctionGuard, TQueryKey>; -``` - -Defined in: [packages/query-core/src/types.ts:511](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L511) - -If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`placeholderData`](QueryObserverOptions.md#placeholderdata) - -*** - -### queryFn? - -```ts -optional queryFn: - | typeof skipToken -| QueryFunction; -``` - -Defined in: [packages/query-core/src/types.ts:285](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L285) - -The function that the query will use to request data. -Required, unless a default query function has been set via `queryClient.setQueryDefaults` or -`queryClient.setDefaultOptions`. -Receives a [QueryFunctionContext](../type-aliases/QueryFunctionContext.md). -Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`queryFn`](QueryObserverOptions.md#queryfn) - -*** - -### queryHash? - -```ts -optional queryHash: string; -``` - -Defined in: [packages/query-core/src/types.ts:295](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L295) - -The hashed form of `queryKey`, computed with `queryKeyHashFn` (or the default hashing function otherwise). Used -as the actual cache key internally. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`queryHash`](QueryObserverOptions.md#queryhash) - -*** - -### queryKey - -```ts -queryKey: TQueryKey & object; -``` - -Defined in: [packages/query-core/src/types.ts:304](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L304) - -The query key to use for this query. - -The query key will be hashed into a stable hash. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) -for more information. - -The query will automatically update when this key changes (as long as `enabled` is not set to `false`). - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`queryKey`](QueryObserverOptions.md#querykey) - -*** - -### queryKeyHashFn? - -```ts -optional queryKeyHashFn: QueryKeyHashFunction; -``` - -Defined in: [packages/query-core/src/types.ts:308](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L308) - -If specified, this function is used to hash the `queryKey` to a string. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`queryKeyHashFn`](QueryObserverOptions.md#querykeyhashfn) - -*** - -### refetchInterval? - -```ts -optional refetchInterval: number | false | (query) => number | false | undefined; -``` - -Defined in: [packages/query-core/src/types.ts:414](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L414) - -If set to a number, the query will continuously refetch at this frequency in milliseconds. -If set to a function, the function will be executed with the latest data and query to compute a frequency - -Defaults to `false`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchInterval`](QueryObserverOptions.md#refetchinterval) - -*** - -### refetchIntervalInBackground? - -```ts -optional refetchIntervalInBackground: boolean; -``` - -Defined in: [packages/query-core/src/types.ts:425](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L425) - -If set to `true`, the query will continue to refetch while their tab/window is in the background. - -Defaults to `false`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchIntervalInBackground`](QueryObserverOptions.md#refetchintervalinbackground) - -*** - -### refetchOnMount? - -```ts -optional refetchOnMount: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:462](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L462) - -If set to `true`, the query will refetch on mount if the data is stale. -If set to `false`, will disable additional instances of a query to trigger background refetch. -If set to `'always'`, the query will always refetch on mount (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value - -Defaults to `true`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchOnMount`](QueryObserverOptions.md#refetchonmount) - -*** - -### refetchOnReconnect? - -```ts -optional refetchOnReconnect: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:448](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L448) - -If set to `true`, the query will refetch on reconnect if the data is stale. -If set to `false`, the query will not refetch on reconnect. -If set to `'always'`, the query will always refetch on reconnect (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value. - -Defaults to `true` unless `networkMode` is `'always'`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchOnReconnect`](QueryObserverOptions.md#refetchonreconnect) - -*** - -### refetchOnWindowFocus? - -```ts -optional refetchOnWindowFocus: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:434](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L434) - -If set to `true`, the query will refetch on window focus if the data is stale. -If set to `false`, the query will not refetch on window focus. -If set to `'always'`, the query will always refetch on window focus (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value. - -Defaults to `true`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchOnWindowFocus`](QueryObserverOptions.md#refetchonwindowfocus) - -*** - -### retry? - -```ts -optional retry: RetryValue; -``` - -Defined in: [packages/query-core/src/types.ts:246](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L246) - -If `false`, failed queries will not retry by default. -If `true`, failed queries will retry infinitely. -If set to an integer number, e.g. 3, failed queries will retry until the failed query count meets that number. -If set to a function `(failureCount, error) => boolean` failed queries will retry until the function returns false. - -Defaults to `3` on the client and `0` on the server. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`retry`](QueryObserverOptions.md#retry) - -*** - -### retryDelay? - -```ts -optional retryDelay: RetryDelayValue; -``` - -Defined in: [packages/query-core/src/types.ts:258](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L258) - -This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the -next attempt in milliseconds. - -A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential -backoff. - -A function like `attempt => attempt * 1000` applies linear backoff. - -Defaults to a function that applies exponential backoff, capped at 30 seconds. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`retryDelay`](QueryObserverOptions.md#retrydelay) - -*** - -### retryOnMount? - -```ts -optional retryOnMount: QueryBooleanOption; -``` - -Defined in: [packages/query-core/src/types.ts:474](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L474) - -If set to `false`, the query will not be retried on mount if it contains an error. -If set to a function, the function will be executed with the query to compute the value. - -Defaults to `true`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`retryOnMount`](QueryObserverOptions.md#retryonmount) - -*** - -### select()? - -```ts -optional select: (data) => TData; -``` - -Defined in: [packages/query-core/src/types.ts:500](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L500) - -This option can be used to transform or select a part of the data returned by the query function. It affects -the returned `data` value, but does not affect what gets stored in the query cache. -The `select` function will only run if `data` changed, or if the reference to the `select` function itself -changes. To optimize, memoize the function so its reference stays stable across calls. - -#### Parameters - -##### data - -`TQueryData` - -#### Returns - -`TData` - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`select`](QueryObserverOptions.md#select) - -*** - -### staleTime? - -```ts -optional staleTime: StaleTimeFunction; -``` - -Defined in: [packages/query-core/src/types.ts:407](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L407) - -The time in milliseconds after data is considered stale. -If set to `Infinity`, the data will never be considered stale. -If set to `'static'`, the data will never be considered stale. -If set to a function, the function will be executed with the query to compute a `staleTime`. - -Defaults to `0`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`staleTime`](QueryObserverOptions.md#staletime) - -*** - -### structuralSharing? - -```ts -optional structuralSharing: boolean | (oldData, newData) => unknown; -``` - -Defined in: [packages/query-core/src/types.ts:330](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L330) - -Set this to `false` to disable structural sharing between query results. -Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. - -Defaults to `true`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`structuralSharing`](QueryObserverOptions.md#structuralsharing) - -*** - -### suspense? - -```ts -optional suspense: boolean; -``` - -Defined in: [packages/query-core/src/types.ts:507](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L507) - -If set to `true`, the query will suspend when `status === 'pending'` -and throw errors when `status === 'error'`. - -Defaults to `false`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`suspense`](QueryObserverOptions.md#suspense) - -*** - -### throwOnError? - -```ts -optional throwOnError: ThrowOnError; -``` - -Defined in: [packages/query-core/src/types.ts:493](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L493) - -Whether errors should be thrown instead of setting the `error` property. -If set to `true` or `suspense` is `true`, all errors will be thrown to the error boundary. -If set to `false` and `suspense` is `false`, errors are returned as state. -If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`). - -Defaults to `false`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`throwOnError`](QueryObserverOptions.md#throwonerror) diff --git a/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md index a56340bfd6c..f8fc15bbbc8 100644 --- a/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateInfiniteQueryOptions.md @@ -3,16 +3,11 @@ id: CreateInfiniteQueryOptions title: CreateInfiniteQueryOptions --- -Defined in: [packages/angular-query-experimental/src/types.ts:121](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L121) - -The options accepted by `injectInfiniteQuery`. Same as [CreateBaseQueryOptions](CreateBaseQueryOptions.md), minus `suspense` — -which `angular-query-experimental` doesn't support, unlike `react-query` — extends -[InfiniteQueryObserverOptions](InfiniteQueryObserverOptions.md) from `@tanstack/query-core` for the infinite-query-specific options -(`getNextPageParam`, `initialPageParam`, etc.). +Defined in: [packages/angular-query/src/types.ts:174](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L174) ## Extends -- [`OmitKeyof`](../type-aliases/OmitKeyof.md)\<[`InfiniteQueryObserverOptions`](InfiniteQueryObserverOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"suspense"`\> +- `OmitKeyof`\<`InfiniteQueryObserverOptions`\<`TQueryFnData`, `TError`, `TData`, `TQueryKey`, `TPageParam`\>, `"notifyOnChangeProps"` \| `"suspense"` \| `"throwOnError"`\> ## Type Parameters diff --git a/docs/framework/angular/reference/interfaces/CreateMutationOptions.md b/docs/framework/angular/reference/interfaces/CreateMutationOptions.md index b1f61fbbbb1..064705324d9 100644 --- a/docs/framework/angular/reference/interfaces/CreateMutationOptions.md +++ b/docs/framework/angular/reference/interfaces/CreateMutationOptions.md @@ -3,14 +3,11 @@ id: CreateMutationOptions title: CreateMutationOptions --- -Defined in: [packages/angular-query-experimental/src/types.ts:224](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L224) - -The options accepted by `injectMutation`. Same as [MutationObserverOptions](MutationObserverOptions.md) from -`@tanstack/query-core`, minus the internal `_defaulted` flag. +Defined in: [packages/angular-query/src/types.ts:241](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L241) ## Extends -- [`OmitKeyof`](../type-aliases/OmitKeyof.md)\<[`MutationObserverOptions`](MutationObserverOptions.md)\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"_defaulted"`\> +- `OmitKeyof`\<`MutationObserverOptions`\<`TData`, `TError`, `TVariables`, `TOnMutateResult`\>, `"_defaulted"` \| `"throwOnError"`\> ## Type Parameters diff --git a/docs/framework/angular/reference/interfaces/CreateQueryOptions.md b/docs/framework/angular/reference/interfaces/CreateQueryOptions.md deleted file mode 100644 index 1982a488ea3..00000000000 --- a/docs/framework/angular/reference/interfaces/CreateQueryOptions.md +++ /dev/null @@ -1,647 +0,0 @@ ---- -id: CreateQueryOptions -title: CreateQueryOptions ---- - -Defined in: [packages/angular-query-experimental/src/types.ts:58](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L58) - -The options accepted by `injectQuery`. Same as [CreateBaseQueryOptions](CreateBaseQueryOptions.md), minus `suspense` — which -`angular-query-experimental` doesn't support, unlike `react-query`. - -## Extends - -- [`OmitKeyof`](../type-aliases/OmitKeyof.md)\<[`CreateBaseQueryOptions`](CreateBaseQueryOptions.md)\<`TQueryFnData`, `TError`, `TData`, `TQueryFnData`, `TQueryKey`\>, `"suspense"`\> - -## Type Parameters - -### TQueryFnData - -`TQueryFnData` = `unknown` - -The type your `queryFn` resolves to. - -### TError - -`TError` = [`DefaultError`](../type-aliases/DefaultError.md) - -The type of errors your `queryFn` may throw. - -### TData - -`TData` = `TQueryFnData` - -The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no -`select` is used. - -### TQueryKey - -`TQueryKey` *extends* [`QueryKey`](../type-aliases/QueryKey.md) = [`QueryKey`](../type-aliases/QueryKey.md) - -The type of your `queryKey`. - -## Properties - -### enabled? - -```ts -optional enabled: QueryBooleanOption; -``` - -Defined in: [packages/query-core/src/types.ts:398](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L398) - -Set this to `false` or a function that returns `false` to disable automatic refetching when the query mounts or changes query keys. -To refetch the query, use the `refetch` method returned from the `useQuery` instance. -Accepts a boolean or function that returns a boolean. - -Defaults to `true`. - -#### Inherited from - -```ts -OmitKeyof.enabled -``` - -*** - -### gcTime? - -```ts -optional gcTime: number; -``` - -Defined in: [packages/query-core/src/types.ts:277](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L277) - -The time in milliseconds that unused/inactive cache data remains in memory. -When a query's cache becomes unused or inactive, that cache data will be garbage collected after this duration. -When different garbage collection times are specified, the longest one will be used. -Setting it to `Infinity` will disable garbage collection. - -Defaults to `5 * 60 * 1000` (5 minutes), or `Infinity` during SSR. - -Note: the maximum allowed time is about 24 days, imposed by `setTimeout`'s 32-bit signed integer delay — see -`timeoutManager.setTimeoutProvider` for a workaround. - -#### Inherited from - -[`QueryOptions`](QueryOptions.md).[`gcTime`](QueryOptions.md#gctime) - -*** - -### initialData? - -```ts -optional initialData: - | TQueryFnData -| InitialDataFunction; -``` - -Defined in: [packages/query-core/src/types.ts:317](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L317) - -If set, this value will be used as the initial data for the query cache (as long as the query hasn't been -created or cached yet). -If set to a function, the function will be called **once** during the shared/root query initialization, and be -expected to synchronously return the initial data. -Initial data is considered stale by default unless a `staleTime` has been set. -`initialData` **is persisted** to the cache. - -#### Inherited from - -```ts -OmitKeyof.initialData -``` - -*** - -### initialDataUpdatedAt? - -```ts -optional initialDataUpdatedAt: number | () => number | undefined; -``` - -Defined in: [packages/query-core/src/types.ts:321](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L321) - -If set, this value will be used as the time (in milliseconds) of when the `initialData` itself was last updated. - -#### Inherited from - -```ts -OmitKeyof.initialDataUpdatedAt -``` - -*** - -### maxPages? - -```ts -optional maxPages: number; -``` - -Defined in: [packages/query-core/src/types.ts:345](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L345) - -Maximum number of pages to store in the data of an infinite query. - -#### Inherited from - -[`QueryOptions`](QueryOptions.md).[`maxPages`](QueryOptions.md#maxpages) - -*** - -### meta? - -```ts -optional meta: Record; -``` - -Defined in: [packages/query-core/src/types.ts:341](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L341) - -Additional payload to be stored on each query. -Use this property to pass information that can be used in other places. - -#### Inherited from - -[`QueryOptions`](QueryOptions.md).[`meta`](QueryOptions.md#meta) - -*** - -### networkMode? - -```ts -optional networkMode: NetworkMode; -``` - -Defined in: [packages/query-core/src/types.ts:265](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L265) - -Controls whether a query is allowed to run based on the current network connectivity. - -Defaults to `'online'`. - -#### See - -[Network Mode](https://tanstack.com/query/latest/docs/framework/react/guides/network-mode) for more information. - -#### Inherited from - -[`QueryOptions`](QueryOptions.md).[`networkMode`](QueryOptions.md#networkmode) - -*** - -### notifyOnChangeProps? - -```ts -optional notifyOnChangeProps: NotifyOnChangeProps; -``` - -Defined in: [packages/query-core/src/types.ts:484](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L484) - -If set, the component will only re-render if any of the listed properties change. -When set to `['data', 'error']`, the component will only re-render when the `data` or `error` properties change. -When set to `'all'`, the component will re-render whenever a query is updated. -When set to a function, the function will be executed to compute the list of properties. - -Defaults to `undefined`, in which case property access is tracked automatically, and the -component only re-renders when one of the tracked properties changes. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`notifyOnChangeProps`](QueryObserverOptions.md#notifyonchangeprops) - -*** - -### persister()? - -```ts -optional persister: (queryFn, context, query) => TQueryFnData | Promise; -``` - -Defined in: [packages/query-core/src/types.ts:290](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L290) - -This option can be used to persist the result of a query to an external storage, bypassing the need to actually -call the `queryFn`. Useful for persisting a query's data across e.g. server/client boundaries. - -#### Parameters - -##### queryFn - -[`QueryFunction`](../type-aliases/QueryFunction.md)\<`TQueryFnData`, `NoInfer`\<`TQueryKey`\>, `never`\> - -##### context - -###### client - -[`QueryClient`](../classes/QueryClient.md) - -###### direction? - -`unknown` - -**Deprecated** - -if you want access to the direction, you can add it to the pageParam - -###### meta - -`Record`\<`string`, `unknown`\> \| `undefined` - -###### pageParam? - -`unknown` - -###### queryKey - -`NoInfer`\<`TQueryKey`\> - -###### signal - -`AbortSignal` - -##### query - -[`Query`](../classes/Query.md) - -#### Returns - -`TQueryFnData` \| `Promise`\<`TQueryFnData`\> - -#### Inherited from - -```ts -OmitKeyof.persister -``` - -*** - -### placeholderData? - -```ts -optional placeholderData: - | NonFunctionGuard -| PlaceholderDataFunction, TError, NonFunctionGuard, TQueryKey>; -``` - -Defined in: [packages/query-core/src/types.ts:511](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L511) - -If set, this value will be used as the placeholder data for this particular query observer while the query is still in the `loading` data and no initialData has been provided. - -#### Inherited from - -```ts -OmitKeyof.placeholderData -``` - -*** - -### queryFn? - -```ts -optional queryFn: - | typeof skipToken -| QueryFunction; -``` - -Defined in: [packages/query-core/src/types.ts:285](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L285) - -The function that the query will use to request data. -Required, unless a default query function has been set via `queryClient.setQueryDefaults` or -`queryClient.setDefaultOptions`. -Receives a [QueryFunctionContext](../type-aliases/QueryFunctionContext.md). -Must return a promise that will either resolve data or throw an error. The data cannot be `undefined`. - -#### Inherited from - -```ts -OmitKeyof.queryFn -``` - -*** - -### queryHash? - -```ts -optional queryHash: string; -``` - -Defined in: [packages/query-core/src/types.ts:295](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L295) - -The hashed form of `queryKey`, computed with `queryKeyHashFn` (or the default hashing function otherwise). Used -as the actual cache key internally. - -#### Inherited from - -[`QueryOptions`](QueryOptions.md).[`queryHash`](QueryOptions.md#queryhash) - -*** - -### queryKey - -```ts -queryKey: TQueryKey & object; -``` - -Defined in: [packages/query-core/src/types.ts:304](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L304) - -The query key to use for this query. - -The query key will be hashed into a stable hash. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) -for more information. - -The query will automatically update when this key changes (as long as `enabled` is not set to `false`). - -#### Inherited from - -```ts -OmitKeyof.queryKey -``` - -*** - -### queryKeyHashFn? - -```ts -optional queryKeyHashFn: QueryKeyHashFunction; -``` - -Defined in: [packages/query-core/src/types.ts:308](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L308) - -If specified, this function is used to hash the `queryKey` to a string. - -#### Inherited from - -```ts -OmitKeyof.queryKeyHashFn -``` - -*** - -### refetchInterval? - -```ts -optional refetchInterval: number | false | (query) => number | false | undefined; -``` - -Defined in: [packages/query-core/src/types.ts:414](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L414) - -If set to a number, the query will continuously refetch at this frequency in milliseconds. -If set to a function, the function will be executed with the latest data and query to compute a frequency - -Defaults to `false`. - -#### Inherited from - -```ts -OmitKeyof.refetchInterval -``` - -*** - -### refetchIntervalInBackground? - -```ts -optional refetchIntervalInBackground: boolean; -``` - -Defined in: [packages/query-core/src/types.ts:425](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L425) - -If set to `true`, the query will continue to refetch while their tab/window is in the background. - -Defaults to `false`. - -#### Inherited from - -[`QueryObserverOptions`](QueryObserverOptions.md).[`refetchIntervalInBackground`](QueryObserverOptions.md#refetchintervalinbackground) - -*** - -### refetchOnMount? - -```ts -optional refetchOnMount: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:462](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L462) - -If set to `true`, the query will refetch on mount if the data is stale. -If set to `false`, will disable additional instances of a query to trigger background refetch. -If set to `'always'`, the query will always refetch on mount (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value - -Defaults to `true`. - -#### Inherited from - -```ts -OmitKeyof.refetchOnMount -``` - -*** - -### refetchOnReconnect? - -```ts -optional refetchOnReconnect: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:448](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L448) - -If set to `true`, the query will refetch on reconnect if the data is stale. -If set to `false`, the query will not refetch on reconnect. -If set to `'always'`, the query will always refetch on reconnect (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value. - -Defaults to `true` unless `networkMode` is `'always'`. - -#### Inherited from - -```ts -OmitKeyof.refetchOnReconnect -``` - -*** - -### refetchOnWindowFocus? - -```ts -optional refetchOnWindowFocus: boolean | "always" | (query) => boolean | "always"; -``` - -Defined in: [packages/query-core/src/types.ts:434](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L434) - -If set to `true`, the query will refetch on window focus if the data is stale. -If set to `false`, the query will not refetch on window focus. -If set to `'always'`, the query will always refetch on window focus (except when `staleTime: 'static'` is used). -If set to a function, the function will be executed with the latest data and query to compute the value. - -Defaults to `true`. - -#### Inherited from - -```ts -OmitKeyof.refetchOnWindowFocus -``` - -*** - -### retry? - -```ts -optional retry: RetryValue; -``` - -Defined in: [packages/query-core/src/types.ts:246](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L246) - -If `false`, failed queries will not retry by default. -If `true`, failed queries will retry infinitely. -If set to an integer number, e.g. 3, failed queries will retry until the failed query count meets that number. -If set to a function `(failureCount, error) => boolean` failed queries will retry until the function returns false. - -Defaults to `3` on the client and `0` on the server. - -#### Inherited from - -```ts -OmitKeyof.retry -``` - -*** - -### retryDelay? - -```ts -optional retryDelay: RetryDelayValue; -``` - -Defined in: [packages/query-core/src/types.ts:258](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L258) - -This function receives a `retryAttempt` integer and the actual Error and returns the delay to apply before the -next attempt in milliseconds. - -A function like `attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000)` applies exponential -backoff. - -A function like `attempt => attempt * 1000` applies linear backoff. - -Defaults to a function that applies exponential backoff, capped at 30 seconds. - -#### Inherited from - -```ts -OmitKeyof.retryDelay -``` - -*** - -### retryOnMount? - -```ts -optional retryOnMount: QueryBooleanOption; -``` - -Defined in: [packages/query-core/src/types.ts:474](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L474) - -If set to `false`, the query will not be retried on mount if it contains an error. -If set to a function, the function will be executed with the query to compute the value. - -Defaults to `true`. - -#### Inherited from - -```ts -OmitKeyof.retryOnMount -``` - -*** - -### select()? - -```ts -optional select: (data) => TData; -``` - -Defined in: [packages/query-core/src/types.ts:500](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L500) - -This option can be used to transform or select a part of the data returned by the query function. It affects -the returned `data` value, but does not affect what gets stored in the query cache. -The `select` function will only run if `data` changed, or if the reference to the `select` function itself -changes. To optimize, memoize the function so its reference stays stable across calls. - -#### Parameters - -##### data - -`TQueryFnData` - -#### Returns - -`TData` - -#### Inherited from - -```ts -OmitKeyof.select -``` - -*** - -### staleTime? - -```ts -optional staleTime: StaleTimeFunction; -``` - -Defined in: [packages/query-core/src/types.ts:407](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L407) - -The time in milliseconds after data is considered stale. -If set to `Infinity`, the data will never be considered stale. -If set to `'static'`, the data will never be considered stale. -If set to a function, the function will be executed with the query to compute a `staleTime`. - -Defaults to `0`. - -#### Inherited from - -```ts -OmitKeyof.staleTime -``` - -*** - -### structuralSharing? - -```ts -optional structuralSharing: boolean | (oldData, newData) => unknown; -``` - -Defined in: [packages/query-core/src/types.ts:330](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L330) - -Set this to `false` to disable structural sharing between query results. -Set this to a function which accepts the old and new data and returns resolved data of the same type to implement custom structural sharing logic. - -Defaults to `true`. - -#### Inherited from - -```ts -OmitKeyof.structuralSharing -``` - -*** - -### throwOnError? - -```ts -optional throwOnError: ThrowOnError; -``` - -Defined in: [packages/query-core/src/types.ts:493](https://github.com/TanStack/query/blob/main/packages/query-core/src/types.ts#L493) - -Whether errors should be thrown instead of setting the `error` property. -If set to `true` or `suspense` is `true`, all errors will be thrown to the error boundary. -If set to `false` and `suspense` is `false`, errors are returned as state. -If set to a function, it will be passed the error and the query, and it should return a boolean indicating whether to show the error in an error boundary (`true`) or return the error as state (`false`). - -Defaults to `false`. - -#### Inherited from - -```ts -OmitKeyof.throwOnError -``` diff --git a/docs/framework/angular/reference/interfaces/DefinedInfiniteQueryNarrowing.md b/docs/framework/angular/reference/interfaces/DefinedInfiniteQueryNarrowing.md new file mode 100644 index 00000000000..a2057b81868 --- /dev/null +++ b/docs/framework/angular/reference/interfaces/DefinedInfiniteQueryNarrowing.md @@ -0,0 +1,46 @@ +--- +id: DefinedInfiniteQueryNarrowing +title: DefinedInfiniteQueryNarrowing +--- + +Defined in: [packages/angular-query/src/types.ts:147](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L147) + +## Type Parameters + +### TData + +`TData` = `unknown` + +### TError + +`TError` = `DefaultError` + +## Properties + +### isError + +```ts +isError: SignalFunction<(this) => this is DefinedCreateInfiniteQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:160](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L160) + +*** + +### isPending + +```ts +isPending: SignalFunction<(this) => this is never>; +``` + +Defined in: [packages/angular-query/src/types.ts:169](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L169) + +*** + +### isSuccess + +```ts +isSuccess: SignalFunction<(this) => this is DefinedCreateInfiniteQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:151](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L151) diff --git a/docs/framework/angular/reference/interfaces/DefinedQueryNarrowing.md b/docs/framework/angular/reference/interfaces/DefinedQueryNarrowing.md new file mode 100644 index 00000000000..ee6773e5df2 --- /dev/null +++ b/docs/framework/angular/reference/interfaces/DefinedQueryNarrowing.md @@ -0,0 +1,46 @@ +--- +id: DefinedQueryNarrowing +title: DefinedQueryNarrowing +--- + +Defined in: [packages/angular-query/src/types.ts:90](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L90) + +## Type Parameters + +### TData + +`TData` = `unknown` + +### TError + +`TError` = `DefaultError` + +## Properties + +### isError + +```ts +isError: SignalFunction<(this) => this is DefinedCreateQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:100](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L100) + +*** + +### isPending + +```ts +isPending: SignalFunction<(this) => this is never>; +``` + +Defined in: [packages/angular-query/src/types.ts:109](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L109) + +*** + +### isSuccess + +```ts +isSuccess: SignalFunction<(this) => this is DefinedCreateQueryResult>>; +``` + +Defined in: [packages/angular-query/src/types.ts:91](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L91) diff --git a/docs/framework/angular/reference/interfaces/InjectInfiniteQueryOptions.md b/docs/framework/angular/reference/interfaces/InjectInfiniteQueryOptions.md deleted file mode 100644 index a1f2d541d09..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectInfiniteQueryOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectInfiniteQueryOptions -title: InjectInfiniteQueryOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-infinite-query.ts:25](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L25) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-infinite-query.ts:31](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-infinite-query.ts#L31) - -The `Injector` in which to create the infinite query. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/InjectIsFetchingOptions.md b/docs/framework/angular/reference/interfaces/InjectIsFetchingOptions.md deleted file mode 100644 index 799b9a4498f..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectIsFetchingOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectIsFetchingOptions -title: InjectIsFetchingOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-is-fetching.ts:13](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-fetching.ts#L13) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-is-fetching.ts:19](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-fetching.ts#L19) - -The `Injector` in which to create the isFetching signal. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/InjectIsMutatingOptions.md b/docs/framework/angular/reference/interfaces/InjectIsMutatingOptions.md deleted file mode 100644 index 344f84f0d9c..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectIsMutatingOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectIsMutatingOptions -title: InjectIsMutatingOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-is-mutating.ts:13](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-mutating.ts#L13) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-is-mutating.ts:19](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-is-mutating.ts#L19) - -The `Injector` in which to create the isMutating signal. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/InjectMutationOptions.md b/docs/framework/angular/reference/interfaces/InjectMutationOptions.md deleted file mode 100644 index 8f012549d6c..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectMutationOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectMutationOptions -title: InjectMutationOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-mutation.ts:28](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L28) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-mutation.ts:34](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation.ts#L34) - -The `Injector` in which to create the mutation. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md b/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md deleted file mode 100644 index 8fe8ce53c0d..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectMutationStateOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectMutationStateOptions -title: InjectMutationStateOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-mutation-state.ts:40](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L40) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-mutation-state.ts:46](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-mutation-state.ts#L46) - -The `Injector` in which to create the mutation state signal. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/InjectQueriesOptions.md b/docs/framework/angular/reference/interfaces/InjectQueriesOptions.md new file mode 100644 index 00000000000..cf774321bac --- /dev/null +++ b/docs/framework/angular/reference/interfaces/InjectQueriesOptions.md @@ -0,0 +1,48 @@ +--- +id: InjectQueriesOptions +title: InjectQueriesOptions +--- + +Defined in: [packages/angular-query/src/inject-queries.types.ts:213](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.types.ts#L213) + +## Type Parameters + +### T + +`T` *extends* `any`[] + +### TCombinedResult + +`TCombinedResult` = [`QueriesResults`](../type-aliases/QueriesResults.md)\<`T`\> + +## Properties + +### combine()? + +```ts +optional combine: (result) => TCombinedResult; +``` + +Defined in: [packages/angular-query/src/inject-queries.types.ts:222](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.types.ts#L222) + +#### Parameters + +##### result + +`T` *extends* \[\] ? \[\] : `T` *extends* \[`Head`\] ? \[`GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<`Head`\>\[`"data"`\], `QueryObserverResult`\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>, `DefinedQueryObserverResult`\<`InferDataAndError`\<`Head`\>\[`"data"`\], `InferDataAndError`\<`Head`\>\[`"error"`\]\>\>\] : `T` *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...Tails[]`\] *extends* \[`Head`\] ? \[`GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<...\>\[`"data"`\], `QueryObserverResult`\<...\[...\], ...\[...\]\>, `DefinedQueryObserverResult`\<...\[...\], ...\[...\]\>\>, `GenericGetDefinedOrUndefinedQueryResult`\<`Head`, `InferDataAndError`\<...\>\[`"data"`\], `QueryObserverResult`\<...\[...\], ...\[...\]\>, `DefinedQueryObserverResult`\<...\[...\], ...\[...\]\>\>\] : \[`...Tails[]`\] *extends* \[`Head`, `...Tails[]`\] ? \[`...Tails[]`\] *extends* \[\] ? \[\] : \[`...(...)[]`\] *extends* \[...\] ? \[..., ..., ...\] : ... *extends* ... ? ... : ... : \[...\{ \[K in (...) \| (...) \| (...)\]: GenericGetDefinedOrUndefinedQueryResult\<(...), (...), (...), (...)\> \}\[\]\] : \{ \[K in string \| number \| symbol\]: GenericGetDefinedOrUndefinedQueryResult\\], InferDataAndError\\]\>\["data"\], QueryObserverResult\\["data"\], InferDataAndError\<(...)\[(...)\]\>\["error"\]\>, DefinedQueryObserverResult\\["data"\], InferDataAndError\<(...)\[(...)\]\>\["error"\]\>\> \} + +#### Returns + +`TCombinedResult` + +*** + +### queries + +```ts +queries: + | readonly [{ [K in string | number | symbol]: GetCreateQueryOptionsForCreateQueries]> }] + | readonly [T extends [] ? [] : T extends [Head] ? [GetCreateQueryOptionsForCreateQueries] : T extends [Head, ...Tails[]] ? [...Tails[]] extends [] ? [] : [...Tails[]] extends [Head] ? [GetCreateQueryOptionsForCreateQueries, GetCreateQueryOptionsForCreateQueries] : [...Tails[]] extends [Head, ...Tails[]] ? [...Tails[]] extends [] ? [] : [...(...)[]] extends [...] ? [..., ..., ...] : ... extends ... ? ... : ... : readonly unknown[] extends [...Tails[]] ? [...Tails[]] : [...(...)[]] extends ...[] ? ...[] : ...[] : readonly unknown[] extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]]; +``` + +Defined in: [packages/angular-query/src/inject-queries.types.ts:217](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.types.ts#L217) diff --git a/docs/framework/angular/reference/interfaces/InjectQueryOptions.md b/docs/framework/angular/reference/interfaces/InjectQueryOptions.md deleted file mode 100644 index 418835acb0d..00000000000 --- a/docs/framework/angular/reference/interfaces/InjectQueryOptions.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -id: InjectQueryOptions -title: InjectQueryOptions ---- - -Defined in: [packages/angular-query-experimental/src/inject-query.ts:20](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L20) - -## Properties - -### injector? - -```ts -optional injector: Injector; -``` - -Defined in: [packages/angular-query-experimental/src/inject-query.ts:26](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-query.ts#L26) - -The `Injector` in which to create the query. - -If this is not provided, the current injection context will be used instead (via `inject`). diff --git a/docs/framework/angular/reference/interfaces/QueryFeature.md b/docs/framework/angular/reference/interfaces/QueryFeature.md index 11d0aaf6bb7..c65d634a369 100644 --- a/docs/framework/angular/reference/interfaces/QueryFeature.md +++ b/docs/framework/angular/reference/interfaces/QueryFeature.md @@ -3,32 +3,15 @@ id: QueryFeature title: QueryFeature --- -Defined in: [packages/angular-query-experimental/src/providers.ts:132](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L132) - -Helper type to represent a Query feature. - -## Type Parameters - -### TFeatureKind - -`TFeatureKind` *extends* `QueryFeatureKind` - -## Properties - -### ɵkind - -```ts -ɵkind: TFeatureKind; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:133](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L133) - -*** - -### ɵproviders +An opaque configuration returned by Query feature functions. Pass these values to +`provideTanStackQuery`; application code does not construct or inspect them. ```ts -ɵproviders: Provider[]; +provideTanStackQuery( + () => new QueryClient(), + withHydrationKey('secondary-cache'), +) ``` -Defined in: [packages/angular-query-experimental/src/providers.ts:134](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L134) +See [provideTanStackQuery](../functions/provideTanStackQuery.md) for provider setup +and [SSR](../../guides/ssr.md) for hydration features. diff --git a/docs/framework/angular/reference/interfaces/QueryObserverOptions.md b/docs/framework/angular/reference/interfaces/QueryObserverOptions.md index 5e171652c26..02c4f7e05f8 100644 --- a/docs/framework/angular/reference/interfaces/QueryObserverOptions.md +++ b/docs/framework/angular/reference/interfaces/QueryObserverOptions.md @@ -12,7 +12,6 @@ Defined in: [packages/query-core/src/types.ts:380](https://github.com/TanStack/q ## Extended by - [`InfiniteQueryObserverOptions`](InfiniteQueryObserverOptions.md) -- [`CreateBaseQueryOptions`](CreateBaseQueryOptions.md) ## Type Parameters diff --git a/docs/framework/angular/reference/interfaces/QueryResource.md b/docs/framework/angular/reference/interfaces/QueryResource.md new file mode 100644 index 00000000000..dbfe89d3e72 --- /dev/null +++ b/docs/framework/angular/reference/interfaces/QueryResource.md @@ -0,0 +1,172 @@ +--- +id: QueryResource +title: QueryResource +--- + +Defined in: [packages/angular-query/src/query-resource.ts:6](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-resource.ts#L6) + +## Extends + +- `Resource`\<`TValue`\> + +## Type Parameters + +### TValue + +`TValue` + +## Properties + +### error + +```ts +readonly error: Signal; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:139 + +When in the `error` state, this returns the last known error from the `Resource`. + +#### Inherited from + +```ts +Resource.error +``` + +*** + +### isLoading + +```ts +readonly isLoading: Signal; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:143 + +Whether this resource is loading a new value (or reloading the existing one). + +#### Inherited from + +```ts +Resource.isLoading +``` + +*** + +### reload() + +```ts +reload: () => boolean; +``` + +Defined in: [packages/angular-query/src/query-resource.ts:20](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-resource.ts#L20) + +Requests a new query fetch only when the current query data is stale. + +Used for compatibility with Angular APIs that might want to reload a resource, +like Signal Forms' validateAsync resource interface. + +#### Returns + +`boolean` + +`true` if a reload was initiated, `false` if a reload was unnecessary or unsupported. + +*** + +### snapshot + +```ts +readonly snapshot: Signal>; +``` + +Defined in: [packages/angular-query/src/query-resource.ts:10](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-resource.ts#L10) + +The current query state as a single snapshot. + +*** + +### status + +```ts +readonly status: Signal; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:135 + +The current status of the `Resource`, which describes what the resource is currently doing and +what can be expected of its `value`. + +#### Inherited from + +```ts +Resource.status +``` + +*** + +### value + +```ts +readonly value: Signal; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:130 + +The current value of the `Resource`, or throws an error if the resource is in an error state. + +#### Inherited from + +```ts +Resource.value +``` + +## Methods + +### hasValue() + +#### Call Signature + +```ts +hasValue(this): this is Resource>; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:149 + +Whether this resource has a valid current value. + +This function is reactive. + +##### Parameters + +###### this + +`TValue` *extends* `undefined` ? `QueryResource`\<`TValue`\> : `never` + +##### Returns + +`this is Resource>` + +##### Inherited from + +```ts +Resource.hasValue +``` + +#### Call Signature + +```ts +hasValue(): boolean; +``` + +Defined in: node\_modules/.pnpm/@angular+core@20.3.18\_@angular+compiler@20.3.18\_rxjs@7.8.2\_zone.js@0.16.1/node\_modules/@angular/core/api.d.d.ts:150 + +##### Returns + +`boolean` + +##### Inherited from + +```ts +Resource.hasValue +``` diff --git a/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md b/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md index 30408338654..bc7ffa49731 100644 --- a/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateBaseMutationResult.md @@ -9,11 +9,7 @@ type CreateBaseMutationResult = Over }> & object; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:284](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L284) - -The pre-`Signal` shape [CreateMutationResult](CreateMutationResult.md) is built from — not what `injectMutation` actually -returns. Same as [MutationObserverResult](MutationObserverResult.md) from `@tanstack/query-core`, with `mutate` narrowed to the -fire-and-forget [CreateMutateFunction](CreateMutateFunction.md) signature, plus the added `mutateAsync`. +Defined in: [packages/angular-query/src/types.ts:269](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L269) ## Type Declaration diff --git a/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md index f632a3e04a7..def30c80476 100644 --- a/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateBaseQueryResult.md @@ -4,17 +4,10 @@ title: CreateBaseQueryResult --- ```ts -type CreateBaseQueryResult = BaseQueryNarrowing & MapToSignals>; +type CreateBaseQueryResult = BaseQueryNarrowing & MapToSignals, MethodKeys>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:149](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L149) - -The result of `injectQuery` when `initialData` isn't set — `data` may be `undefined` while the query is -`pending`. Same shape as [QueryObserverResult](QueryObserverResult.md) from `@tanstack/query-core`, but value fields (like -`data`, `error`, `status`) are exposed as a `Signal` — read them with `query.data()`, not `query.data` — -while function fields (like `refetch`) are called directly, unchanged. `isSuccess`/`isError`/`isPending` -are [BaseQueryNarrowing](../interfaces/BaseQueryNarrowing.md) type-guard methods rather than plain booleans. -`injectInfiniteQuery` returns [CreateInfiniteQueryResult](CreateInfiniteQueryResult.md) instead. +Defined in: [packages/angular-query/src/types.ts:191](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L191) ## Type Parameters @@ -32,4 +25,4 @@ The type of errors your `queryFn` may throw. ### TState -`TState` = [`QueryObserverResult`](QueryObserverResult.md)\<`TData`, `TError`\> +`TState` *extends* `QueryObserverResult`\<`TData`, `TError`\> = `QueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md index e0b6a6981f4..c09f572f8de 100644 --- a/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateInfiniteQueryResult.md @@ -4,15 +4,10 @@ title: CreateInfiniteQueryResult --- ```ts -type CreateInfiniteQueryResult = BaseQueryNarrowing & MapToSignals>; +type CreateInfiniteQueryResult = BaseInfiniteQueryNarrowing & MapToSignals>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:191](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L191) - -The result of `injectInfiniteQuery` when `initialData` isn't set — `data` may be `undefined` while the -query is `pending`. Same shape as [InfiniteQueryObserverResult](InfiniteQueryObserverResult.md) from `@tanstack/query-core`, but -value fields are exposed as a `Signal` while function fields (like `fetchNextPage`) are called directly, -unchanged. +Defined in: [packages/angular-query/src/types.ts:220](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L220) ## Type Parameters @@ -24,6 +19,8 @@ The type `data` ends up as after `select` runs. ### TError -`TError` = [`DefaultError`](DefaultError.md) +`TError` = `DefaultError` + +### TState -The type of errors your `queryFn` may throw. +`TState` *extends* `InfiniteQueryObserverResult`\<`TData`, `TError`\> = `InfiniteQueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md b/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md index fe72322f8cd..2d410fe52f6 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutateAsyncFunction.md @@ -7,10 +7,7 @@ title: CreateMutateAsyncFunction type CreateMutateAsyncFunction = MutateFunction; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:266](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L266) - -The type of `mutateAsync`, as returned by `injectMutation`. Similar to [CreateMutateFunction](CreateMutateFunction.md), but -returns a promise which can be awaited. +Defined in: [packages/angular-query/src/types.ts:262](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L262) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md b/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md index 9960203a068..09e59c09103 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutateFunction.md @@ -7,11 +7,7 @@ title: CreateMutateFunction type CreateMutateFunction = (...args) => void; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:245](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L245) - -The type of `mutate`, as returned by `injectMutation`. Forwards the variables (and an optional per-call -`onSuccess`/`onError`/`onSettled`) to the underlying `mutate` call. Fire-and-forget — errors are surfaced -through the mutation result, not thrown. +Defined in: [packages/angular-query/src/types.ts:251](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L251) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/CreateMutationResult.md b/docs/framework/angular/reference/type-aliases/CreateMutationResult.md index 7a167bee22c..d46f090d102 100644 --- a/docs/framework/angular/reference/type-aliases/CreateMutationResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateMutationResult.md @@ -4,15 +4,10 @@ title: CreateMutationResult --- ```ts -type CreateMutationResult = BaseMutationNarrowing & MapToSignals>; +type CreateMutationResult = BaseMutationNarrowing & MapToSignals, MethodKeys>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:416](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L416) - -The result of `injectMutation`. Based on [CreateBaseMutationResult](CreateBaseMutationResult.md), but value fields are exposed as -a `Signal` — read them with `mutation.data()`, not `mutation.data` — while function fields (`mutate`, -`mutateAsync`, `reset`) are called directly, unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are -[BaseMutationNarrowing](../interfaces/BaseMutationNarrowing.md) type-guard methods rather than plain booleans. +Defined in: [packages/angular-query/src/types.ts:373](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L373) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/CreateQueryOptions.md b/docs/framework/angular/reference/type-aliases/CreateQueryOptions.md new file mode 100644 index 00000000000..f71513d7083 --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/CreateQueryOptions.md @@ -0,0 +1,28 @@ +--- +id: CreateQueryOptions +title: CreateQueryOptions +--- + +```ts +type CreateQueryOptions = OmitKeyof, "notifyOnChangeProps" | "suspense" | "throwOnError">; +``` + +Defined in: [packages/angular-query/src/types.ts:23](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L23) + +## Type Parameters + +### TQueryFnData + +`TQueryFnData` = `unknown` + +### TError + +`TError` = `DefaultError` + +### TData + +`TData` = `TQueryFnData` + +### TQueryKey + +`TQueryKey` *extends* `QueryKey` = `QueryKey` diff --git a/docs/framework/angular/reference/type-aliases/CreateQueryResult.md b/docs/framework/angular/reference/type-aliases/CreateQueryResult.md index 2e57945064b..c289489f1d9 100644 --- a/docs/framework/angular/reference/type-aliases/CreateQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/CreateQueryResult.md @@ -7,9 +7,7 @@ title: CreateQueryResult type CreateQueryResult = CreateBaseQueryResult; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:162](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L162) - -The result of `injectQuery`. Same as [CreateBaseQueryResult](CreateBaseQueryResult.md). +Defined in: [packages/angular-query/src/types.ts:204](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L204) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md b/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md index 382b86bdbd1..c45e90f217e 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/DefinedCreateInfiniteQueryResult.md @@ -4,14 +4,10 @@ title: DefinedCreateInfiniteQueryResult --- ```ts -type DefinedCreateInfiniteQueryResult = MapToSignals; +type DefinedCreateInfiniteQueryResult = DefinedInfiniteQueryNarrowing & MapToSignals>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:205](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L205) - -The result of `injectInfiniteQuery` when `initialData` is set — `data` is never `undefined`. Same shape as -[DefinedInfiniteQueryObserverResult](DefinedInfiniteQueryObserverResult.md) from `@tanstack/query-core`, but value fields are exposed as a -`Signal` while function fields are called directly, unchanged. +Defined in: [packages/angular-query/src/types.ts:228](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L228) ## Type Parameters @@ -29,4 +25,4 @@ The type of errors your `queryFn` may throw. ### TDefinedInfiniteQueryObserver -`TDefinedInfiniteQueryObserver` = [`DefinedInfiniteQueryObserverResult`](DefinedInfiniteQueryObserverResult.md)\<`TData`, `TError`\> +`TDefinedInfiniteQueryObserver` *extends* `DefinedInfiniteQueryObserverResult`\<`TData`, `TError`\> = `DefinedInfiniteQueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md b/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md index 934f51485d1..fbeae6470c5 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md +++ b/docs/framework/angular/reference/type-aliases/DefinedCreateQueryResult.md @@ -4,14 +4,10 @@ title: DefinedCreateQueryResult --- ```ts -type DefinedCreateQueryResult = BaseQueryNarrowing & MapToSignals>; +type DefinedCreateQueryResult = DefinedQueryNarrowing & MapToSignals, MethodKeys>>; ``` -Defined in: [packages/angular-query-experimental/src/types.ts:175](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/types.ts#L175) - -The result of `injectQuery` when `initialData` is set — `data` is never `undefined`. Same shape as -[DefinedQueryObserverResult](DefinedQueryObserverResult.md) from `@tanstack/query-core`, but value fields are exposed as a -`Signal` while function fields are called directly, unchanged. +Defined in: [packages/angular-query/src/types.ts:209](https://github.com/TanStack/query/blob/main/packages/angular-query/src/types.ts#L209) ## Type Parameters @@ -29,4 +25,4 @@ The type of errors your `queryFn` may throw. ### TState -`TState` = [`DefinedQueryObserverResult`](DefinedQueryObserverResult.md)\<`TData`, `TError`\> +`TState` *extends* `DefinedQueryObserverResult`\<`TData`, `TError`\> = `DefinedQueryObserverResult`\<`TData`, `TError`\> diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md index d4b42457136..db4668bf7c8 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataInfiniteOptions.md @@ -7,10 +7,7 @@ title: DefinedInitialDataInfiniteOptions type DefinedInitialDataInfiniteOptions = CreateInfiniteQueryOptions & object; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:109](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L109) - -The options accepted by the `infiniteQueryOptions` overload selected when `initialData` is set — `data` is -never `undefined` (unless a `select` changes `TData` to include `undefined`). +Defined in: [packages/angular-query/src/infinite-query-options.ts:62](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L62) ## Type Declaration @@ -19,8 +16,7 @@ never `undefined` (unless a `select` changes `TData` to include `undefined`). ```ts initialData: | NonUndefinedGuard> - | () => NonUndefinedGuard> - | undefined; +| () => NonUndefinedGuard>; ``` If set, this value will be used as the initial data for the query cache (as long as the query hasn't been diff --git a/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md b/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md index e37476fa74d..c301220238c 100644 --- a/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md +++ b/docs/framework/angular/reference/type-aliases/DefinedInitialDataOptions.md @@ -4,13 +4,10 @@ title: DefinedInitialDataOptions --- ```ts -type DefinedInitialDataOptions = Omit, "queryFn"> & object; +type DefinedInitialDataOptions = CreateQueryOptions & object; ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:80](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L80) - -The options accepted by the `queryOptions` overload selected when `initialData` is set — `data` is never -`undefined` (unless a `select` changes `TData` to include `undefined`). +Defined in: [packages/angular-query/src/query-options.ts:39](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L39) ## Type Declaration @@ -22,22 +19,6 @@ initialData: | () => NonUndefinedGuard; ``` -If set, this value will be used as the initial data for the query cache (as long as the query hasn't been -created or cached yet). If set to a function, the function will be called **once** during the shared/root -query initialization, and be expected to synchronously return the initial data. Initial data is -considered stale by default unless a `staleTime` has been set. `initialData` **is persisted** to the -cache. - -### queryFn? - -```ts -optional queryFn: QueryFunction; -``` - -Optional here, but omitting it is only safe when no fetch will be attempted — for example with -`enabled: false`, or when a default query function has been defined. Otherwise, an enabled query with no -`queryFn` still tries to fetch and fails with a "Missing queryFn" error; `initialData` does not prevent this. - ## Type Parameters ### TQueryFnData diff --git a/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md b/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md deleted file mode 100644 index 2db12a400ab..00000000000 --- a/docs/framework/angular/reference/type-aliases/DevtoolsFeature.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -id: DevtoolsFeature -title: DevtoolsFeature ---- - -```ts -type DevtoolsFeature = QueryFeature<"Devtools">; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:155](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L155) - -A type alias that represents a feature which enables developer tools. -The type is used to describe the return value of the `withDevtools` function. - -## See - -withDevtools diff --git a/docs/framework/angular/reference/type-aliases/MutationStateOptions.md b/docs/framework/angular/reference/type-aliases/MutationStateOptions.md new file mode 100644 index 00000000000..dfd8018782b --- /dev/null +++ b/docs/framework/angular/reference/type-aliases/MutationStateOptions.md @@ -0,0 +1,50 @@ +--- +id: MutationStateOptions +title: MutationStateOptions +--- + +```ts +type MutationStateOptions = object; +``` + +Defined in: [packages/angular-query/src/inject-mutation-state.ts:23](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-mutation-state.ts#L23) + +## Type Parameters + +### TResult + +`TResult` = `MutationState` + +### TMutation + +`TMutation` *extends* `Mutation`\<`any`, `any`, `any`, `any`\> = `MutationTypeFromResult`\<`TResult`\> + +## Properties + +### filters? + +```ts +optional filters: MutationFilters; +``` + +Defined in: [packages/angular-query/src/inject-mutation-state.ts:28](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-mutation-state.ts#L28) + +*** + +### select()? + +```ts +optional select: (mutation) => TResult; +``` + +Defined in: [packages/angular-query/src/inject-mutation-state.ts:29](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-mutation-state.ts#L29) + +#### Parameters + +##### mutation + +`TMutation` + +#### Returns + +`TResult` diff --git a/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md b/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md deleted file mode 100644 index e094e32a970..00000000000 --- a/docs/framework/angular/reference/type-aliases/PersistQueryClientFeature.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -id: PersistQueryClientFeature -title: PersistQueryClientFeature ---- - -```ts -type PersistQueryClientFeature = QueryFeature<"PersistQueryClient">; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:161](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L161) - -A type alias that represents a feature which enables persistence. -The type is used to describe the return value of the `withPersistQueryClient` function. diff --git a/docs/framework/angular/reference/type-aliases/QueriesOptions.md b/docs/framework/angular/reference/type-aliases/QueriesOptions.md index a0f51826b76..411f34c036f 100644 --- a/docs/framework/angular/reference/type-aliases/QueriesOptions.md +++ b/docs/framework/angular/reference/type-aliases/QueriesOptions.md @@ -7,13 +7,7 @@ title: QueriesOptions type QueriesOptions = TDepth["length"] extends MAXIMUM_DEPTH ? QueryObserverOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueries] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions<[...Tails], [...TResults, GetCreateQueryOptionsForCreateQueries], [...TDepth, 1]> : ReadonlyArray extends T ? T : T extends QueryObserverOptionsForCreateQueries[] ? QueryObserverOptionsForCreateQueries[] : QueryObserverOptionsForCreateQueries[]; ``` -Defined in: [packages/angular-query-experimental/src/inject-queries.ts:154](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L154) - -The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's -`queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements — past that, tuple -recursion falls back to a single homogeneous options type. An opaque array (e.g. `unknown[]`) is returned -as-is; a non-tuple array of a known element type is mapped to that element type instead, with no such -limit. +Defined in: [packages/angular-query/src/inject-queries.types.ts:141](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.types.ts#L141) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/QueriesResults.md b/docs/framework/angular/reference/type-aliases/QueriesResults.md index e31cb61caef..8bf2b78c875 100644 --- a/docs/framework/angular/reference/type-aliases/QueriesResults.md +++ b/docs/framework/angular/reference/type-aliases/QueriesResults.md @@ -7,12 +7,7 @@ title: QueriesResults type QueriesResults = TDepth["length"] extends MAXIMUM_DEPTH ? CreateQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResult] : T extends [infer Head, ...(infer Tails)] ? QueriesResults<[...Tails], [...TResults, GetCreateQueryResult], [...TDepth, 1]> : { [K in keyof T]: GetCreateQueryResult }; ``` -Defined in: [packages/angular-query-experimental/src/inject-queries.ts:205](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/inject-queries.ts#L205) - -The result type returned by `injectQueries`, when no `combine` is provided. Mirrors [QueriesOptions](QueriesOptions.md): -each tuple element's result type is inferred individually, up to 20 elements — past that, tuple recursion -falls back to a single homogeneous [CreateQueryResult](CreateQueryResult.md) type. A non-tuple array is mapped per-element -instead, with no such limit — every entry keeps its individually inferred type regardless of array length. +Defined in: [packages/angular-query/src/inject-queries.types.ts:177](https://github.com/TanStack/query/blob/main/packages/angular-query/src/inject-queries.types.ts#L177) ## Type Parameters diff --git a/docs/framework/angular/reference/type-aliases/QueryFeatures.md b/docs/framework/angular/reference/type-aliases/QueryFeatures.md deleted file mode 100644 index ea04f398ffc..00000000000 --- a/docs/framework/angular/reference/type-aliases/QueryFeatures.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -id: QueryFeatures -title: QueryFeatures ---- - -```ts -type QueryFeatures = - | DevtoolsFeature - | PersistQueryClientFeature; -``` - -Defined in: [packages/angular-query-experimental/src/providers.ts:170](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/providers.ts#L170) - -A type alias that represents all Query features available for use with `provideTanStackQuery`. -Features can be enabled by adding special functions to the `provideTanStackQuery` call. -See documentation for each symbol to find corresponding function name. See also `provideTanStackQuery` -documentation on how to use those functions. - -## See - -[provideTanStackQuery](../functions/provideTanStackQuery.md) diff --git a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md index e657351693d..50e5e3feaa7 100644 --- a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataInfiniteOptions.md @@ -7,10 +7,7 @@ title: UndefinedInitialDataInfiniteOptions type UndefinedInitialDataInfiniteOptions = CreateInfiniteQueryOptions & object; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:24](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L24) - -The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set — `data` -may be `undefined` while the query is `pending`. +Defined in: [packages/angular-query/src/infinite-query-options.ts:13](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L13) ## Type Declaration diff --git a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md index 3814e441388..54d2db71cb2 100644 --- a/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md +++ b/docs/framework/angular/reference/type-aliases/UndefinedInitialDataOptions.md @@ -7,10 +7,7 @@ title: UndefinedInitialDataOptions type UndefinedInitialDataOptions = CreateQueryOptions & object; ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:22](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L22) - -The options accepted by the `queryOptions` overload selected when no `initialData` is set — `data` may be -`undefined` while the query is `pending`. +Defined in: [packages/angular-query/src/query-options.ts:12](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L12) ## Type Declaration diff --git a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md index ec9ab5c61dd..70c4640c4c1 100644 --- a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md +++ b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenInfiniteOptions.md @@ -7,11 +7,7 @@ title: UnusedSkipTokenInfiniteOptions type UnusedSkipTokenInfiniteOptions = OmitKeyof, "queryFn"> & object; ``` -Defined in: [packages/angular-query-experimental/src/infinite-query-options.ts:64](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/infinite-query-options.ts#L64) - -The options accepted by the `infiniteQueryOptions` overload selected when no `initialData` is set and -`queryFn` is not `skipToken` — same as [UndefinedInitialDataInfiniteOptions](UndefinedInitialDataInfiniteOptions.md), but `queryFn` may not be -`skipToken`. +Defined in: [packages/angular-query/src/infinite-query-options.ts:34](https://github.com/TanStack/query/blob/main/packages/angular-query/src/infinite-query-options.ts#L34) ## Type Declaration diff --git a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md index 854d773b68e..65c192aee95 100644 --- a/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md +++ b/docs/framework/angular/reference/type-aliases/UnusedSkipTokenOptions.md @@ -7,10 +7,7 @@ title: UnusedSkipTokenOptions type UnusedSkipTokenOptions = OmitKeyof, "queryFn"> & object; ``` -Defined in: [packages/angular-query-experimental/src/query-options.ts:50](https://github.com/TanStack/query/blob/main/packages/angular-query-experimental/src/query-options.ts#L50) - -The options accepted by the `queryOptions` overload selected when no `initialData` is set and `queryFn` is -not `skipToken` — same as [UndefinedInitialDataOptions](UndefinedInitialDataOptions.md), but `queryFn` may not be `skipToken`. +Defined in: [packages/angular-query/src/query-options.ts:24](https://github.com/TanStack/query/blob/main/packages/angular-query/src/query-options.ts#L24) ## Type Declaration diff --git a/docs/framework/angular/resource-api.md b/docs/framework/angular/resource-api.md new file mode 100644 index 00000000000..6463d4799e8 --- /dev/null +++ b/docs/framework/angular/resource-api.md @@ -0,0 +1,160 @@ +--- +id: resource-api +title: Resource API +--- + +Angular Query can convert any query result into an Angular +[`Resource`](https://angular.dev/api/core/Resource)-compatible view with `toResource`. The query +remains responsible for fetching and caching the server state while Angular APIs can consume the +familiar Resource shape. + +```ts +import { Component, input } from '@angular/core' +import { injectQuery, toResource } from '@tanstack/angular-query' + +@Component({ + selector: 'todo-detail', + template: ` + @if (todo.hasValue()) { +

{{ todo.value().title }}

+ } + `, +}) +export class TodoDetail { + readonly id = input.required() + + readonly todoQuery = injectQuery(() => { + const id = this.id() + return { + queryKey: ['todo', id], + queryFn: () => fetchTodo(id), + } + }) + + readonly todo = toResource(this.todoQuery) +} +``` + +The Resource view exposes these fields: + +| Field | Description | +| ----------- | --------------------------------------------------------------------- | +| `value` | The current query data | +| `status` | The query state as an Angular `ResourceStatus` | +| `error` | The current error, normalized to `Error` | +| `isLoading` | Whether the query is loading or reloading | +| `hasValue` | Whether a value is available | +| `snapshot` | The current Resource state as one signal | +| `reload` | Refetches when the query is stale and returns whether a fetch started | + +Use the query's field signals and methods for normal application code. Use `toResource(query)` when +an Angular API expects a Resource or when Resource terminology makes an integration clearer. Call +`query.refetch()` instead of `resource.reload()` when you need an unconditional refetch. + +## Errors + +`value()` throws when the Resource's status is `error`. Read `hasValue()` before accessing it, or +inspect `error()` and `snapshot()` to render an error state. A failed background refetch can leave +cached data available through `query.data()` while the Resource is in its error state. + +Resource errors are not automatically reported globally. See [Error handling](./guides/error-handling.md) +for an opt-in `ErrorHandler` integration. + +## Using Resource with Signal Forms + +> Signal Forms integration requires Angular 21 or newer. + +Angular Signal Forms' +[`validateAsync`](https://angular.dev/guide/forms/signals/async-operations#custom-async-validation-with-validateasync) +accepts a Resource factory. Passing `injectQuery(...)` to `toResource` lets async validation use +Angular Query caching, request deduplication, cancellation, retries, and stale-time behavior. + +The example below validates a username. The query is disabled while the field has no value, and the +factory returns `toResource(query)`. + +```ts +import { Component, inject, signal } from '@angular/core' +import { form, validateAsync } from '@angular/forms/signals' +import { injectQuery, toResource } from '@tanstack/angular-query' + +type UsernameValidation = { + available: boolean +} + +@Component({ + selector: 'registration-form', + template: '', +}) +export class RegistrationForm { + private readonly users = inject(UserService) + readonly model = signal({ username: '' }) + + readonly registrationForm = form(this.model, (path) => { + validateAsync(path.username, { + debounce: 300, + params: ({ value }) => value().trim() || undefined, + factory: (username) => + toResource( + injectQuery(() => { + const name = username() + return { + queryKey: ['username-availability', name], + enabled: !!name, + queryFn: (): Promise => + this.users.checkUsername(name!), + } + }), + ), + onSuccess: (result) => + result?.available + ? null + : { + kind: 'usernameTaken', + message: 'Username is already taken', + }, + onError: () => ({ + kind: 'usernameUnavailable', + message: 'Could not check username availability', + }), + }) + }) +} +``` + +Calling `reloadValidation()` on the field refetches the query only when its cached data is stale. + +## Comparison + +Choosing between Angular's built-in +[`resource`](https://angular.dev/guide/signals/resource) and TanStack Query depends on whether the +operation is isolated async state or server state shared across the application. + +This table focuses on built-in behavior and may not capture every custom implementation. Review +both APIs for the needs of your application. + +Feature/Capability Key: + +- ✅ 1st-class, built-in, and ready to use with no added configuration or code +- 🟡 Partial support +- 🟠 Supported through an additional package +- 🔶 Possible, but requires custom application code +- 🛑 Not built in + +| | Angular `resource` | TanStack Query | +| --------------------------------------- | ---------------------------------------------- | ----------------------------------------------------- | +| Reactive parameters and signal state | ✅ | ✅ | +| Request cancellation | ✅ | ✅ | +| Angular Resource interface | ✅ | ✅ Through `toResource(query)` | +| Shared cache by data identity | 🛑 | ✅ Query keys identify cached data | +| Request deduplication across components | 🛑 | ✅ For queries with the same key | +| Stale and garbage-collection timing | 🔶 | ✅ `staleTime` and `gcTime` | +| Cache invalidation and updates | 🔶 | ✅ `invalidateQueries` and `setQueryData` | +| Retries and background refetching | 🔶 | ✅ Including focus and reconnect refetching | +| Mutations and optimistic updates | 🔶 | ✅ `injectMutation` and QueryClient cache updates | +| SSR | ✅ A resource can transfer its value with `id` | ✅ The complete query cache is hydrated automatically | +| Persistence | 🔶 | 🟠 Through the persistence package | + +Angular `resource` is a good fit for a self-contained asynchronous task. It retains the value of +its own instance and can transfer that value during SSR, but it does not provide a shared, +query-keyed cache or coordinate invalidation and mutations across the application. Use TanStack +Query for server data that is reused, cached, invalidated, mutated, retried, or persisted. diff --git a/docs/framework/angular/typescript.md b/docs/framework/angular/typescript.md index 99781ffa50c..694be1f4cd8 100644 --- a/docs/framework/angular/typescript.md +++ b/docs/framework/angular/typescript.md @@ -6,12 +6,14 @@ replace: { 'useQuery': 'injectQuery', 'useMutation': 'injectMutation', - 'react-query': 'angular-query-experimental', - 'public API of React Query': 'public API of TanStack Query and - after the experimental phase, the angular-query package', + 'react-query': 'angular-query', + '@tanstack/angular-query': '@tanstack/angular-query', + 'public API of React Query': 'public API of TanStack Query and the Angular Query package', 'still follows': 'still follow', 'React Query': 'TanStack Query', '`success`': '`isSuccess()`', 'function:': 'function.', + "TanStack Query follows \\[DefinitelyTyped's support window\\]\\(https://github\\.com/DefinitelyTyped/DefinitelyTyped#support-window\\) and supports TypeScript versions released within the last 2 years\\. At the moment, that means TypeScript \\*\\*5\\.4\\*\\* and newer\\.": 'Angular Query supports the TypeScript versions required by your Angular version. See [Angular version compatibility](https://angular.dev/reference/versions).', } --- @@ -149,9 +151,9 @@ computed(() => { [//]: # 'RegisterErrorType' ```ts -import '@tanstack/angular-query-experimental' +import '@tanstack/angular-query' -declare module '@tanstack/angular-query-experimental' { +declare module '@tanstack/angular-query' { interface Register { // Use unknown so call sites must narrow explicitly. defaultError: unknown @@ -177,7 +179,7 @@ computed(() => { If you inline query options into `injectQuery`, you'll get automatic type inference. However, you might want to extract the query options into a separate function to share them between `injectQuery` and e.g. `queryClient.query`, or manage them in a service. In that case, you'd lose type inference. To get it back, you can use the `queryOptions` helper: ```ts -import { noop } from '@tanstack/angular-query-experimental' +import { noop } from '@tanstack/angular-query' @Injectable({ providedIn: 'root', diff --git a/docs/framework/angular/zoneless.md b/docs/framework/angular/zoneless.md deleted file mode 100644 index ee5811f1a86..00000000000 --- a/docs/framework/angular/zoneless.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: zoneless -title: Zoneless Angular ---- - -Because the Angular adapter for TanStack Query is built on signals, it fully supports Zoneless! - -Among Zoneless benefits are improved performance and debugging experience. For details see the [Angular documentation](https://angular.dev/guide/zoneless). - -> Besides Zoneless, ZoneJS change detection is also fully supported. - -> When using Zoneless, ensure you are on Angular v19 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations. diff --git a/examples/angular/auto-refetching/angular.json b/examples/angular/auto-refetching/angular.json index fb722b5a965..ac133cce756 100644 --- a/examples/angular/auto-refetching/angular.json +++ b/examples/angular/auto-refetching/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "auto-refetching:build:production" diff --git a/examples/angular/auto-refetching/package.json b/examples/angular/auto-refetching/package.json index 30da1c3991a..526a895de16 100644 --- a/examples/angular/auto-refetching/package.json +++ b/examples/angular/auto-refetching/package.json @@ -9,19 +9,20 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/auto-refetching/src/app/app.config.ts b/examples/angular/auto-refetching/src/app/app.config.ts index b9b2b6c36fa..ba6116f9f6b 100644 --- a/examples/angular/auto-refetching/src/app/app.config.ts +++ b/examples/angular/auto-refetching/src/app/app.config.ts @@ -3,11 +3,8 @@ import { withFetch, withInterceptors, } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import { mockInterceptor } from './interceptor/mock-api.interceptor' import type { ApplicationConfig } from '@angular/core' @@ -15,13 +12,14 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch(), withInterceptors([mockInterceptor])), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), ), ], diff --git a/examples/angular/auto-refetching/src/app/components/auto-refetching.component.ts b/examples/angular/auto-refetching/src/app/components/auto-refetching.component.ts index 00a0bdd022e..1838c357fd5 100644 --- a/examples/angular/auto-refetching/src/app/components/auto-refetching.component.ts +++ b/examples/angular/auto-refetching/src/app/components/auto-refetching.component.ts @@ -7,7 +7,7 @@ import { import { injectMutation, injectQuery, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { NgStyle } from '@angular/common' import { TasksService } from '../services/tasks.service' diff --git a/examples/angular/auto-refetching/src/app/services/tasks.service.ts b/examples/angular/auto-refetching/src/app/services/tasks.service.ts index 41d3dacb27d..a79bd5410fa 100644 --- a/examples/angular/auto-refetching/src/app/services/tasks.service.ts +++ b/examples/angular/auto-refetching/src/app/services/tasks.service.ts @@ -4,7 +4,7 @@ import { QueryClient, mutationOptions, queryOptions, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' diff --git a/examples/angular/basic-persister/angular.json b/examples/angular/basic-persister/angular.json index 64adfea7c48..9410e0f039f 100644 --- a/examples/angular/basic-persister/angular.json +++ b/examples/angular/basic-persister/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "basic-persister:build:production" diff --git a/examples/angular/basic-persister/package.json b/examples/angular/basic-persister/package.json index ac5a411bc39..8e3d9443c4a 100644 --- a/examples/angular/basic-persister/package.json +++ b/examples/angular/basic-persister/package.json @@ -9,21 +9,22 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", - "@tanstack/angular-query-persist-client": "^5.103.1", - "@tanstack/query-async-storage-persister": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "@tanstack/angular-query-persist-client": "workspace:*", + "@tanstack/query-async-storage-persister": "^5.100.14", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/basic-persister/src/app/app.config.ts b/examples/angular/basic-persister/src/app/app.config.ts index ff5634cbb63..669064869ef 100644 --- a/examples/angular/basic-persister/src/app/app.config.ts +++ b/examples/angular/basic-persister/src/app/app.config.ts @@ -1,10 +1,7 @@ import { provideHttpClient, withFetch } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' import { withPersistQueryClient } from '@tanstack/angular-query-persist-client' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { withDevtools } from '@tanstack/angular-query-devtools' import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' import type { ApplicationConfig } from '@angular/core' @@ -16,20 +13,21 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - staleTime: 1000 * 60, // 1 minute - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60, // 1 minute + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), - withPersistQueryClient({ + withPersistQueryClient(() => ({ persistOptions: { persister: localStoragePersister, }, - }), + })), ), ], } diff --git a/examples/angular/basic-persister/src/app/components/post.component.ts b/examples/angular/basic-persister/src/app/components/post.component.ts index 9eb32807db5..ff4c850b994 100644 --- a/examples/angular/basic-persister/src/app/components/post.component.ts +++ b/examples/angular/basic-persister/src/app/components/post.component.ts @@ -6,7 +6,7 @@ import { inject, input, } from '@angular/core' -import { QueryClient, injectQuery } from '@tanstack/angular-query-experimental' +import { QueryClient, injectQuery } from '@tanstack/angular-query' import { fromEvent, lastValueFrom, takeUntil } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/basic-persister/src/app/components/posts.component.ts b/examples/angular/basic-persister/src/app/components/posts.component.ts index 2f04558e9d0..a3d62756cc8 100644 --- a/examples/angular/basic-persister/src/app/components/posts.component.ts +++ b/examples/angular/basic-persister/src/app/components/posts.component.ts @@ -5,7 +5,7 @@ import { Output, inject, } from '@angular/core' -import { QueryClient, injectQuery } from '@tanstack/angular-query-experimental' +import { QueryClient, injectQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/basic/angular.json b/examples/angular/basic/angular.json index d6d3113a4df..6dae3a16eaf 100644 --- a/examples/angular/basic/angular.json +++ b/examples/angular/basic/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "basic:build:production" diff --git a/examples/angular/basic/package.json b/examples/angular/basic/package.json index 06d3ce92cbc..d874016e961 100644 --- a/examples/angular/basic/package.json +++ b/examples/angular/basic/package.json @@ -9,19 +9,20 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/basic/src/app/app.config.ts b/examples/angular/basic/src/app/app.config.ts index 77281147d1f..3c9a748815f 100644 --- a/examples/angular/basic/src/app/app.config.ts +++ b/examples/angular/basic/src/app/app.config.ts @@ -1,22 +1,20 @@ import { provideHttpClient, withFetch } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import type { ApplicationConfig } from '@angular/core' export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), ), ], diff --git a/examples/angular/basic/src/app/components/post.component.ts b/examples/angular/basic/src/app/components/post.component.ts index de80ffe6d36..27762afbb41 100644 --- a/examples/angular/basic/src/app/components/post.component.ts +++ b/examples/angular/basic/src/app/components/post.component.ts @@ -5,7 +5,7 @@ import { input, output, } from '@angular/core' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { fromEvent, lastValueFrom, takeUntil } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/basic/src/app/components/posts.component.ts b/examples/angular/basic/src/app/components/posts.component.ts index c40010a8245..f585557b0bd 100644 --- a/examples/angular/basic/src/app/components/posts.component.ts +++ b/examples/angular/basic/src/app/components/posts.component.ts @@ -4,7 +4,7 @@ import { inject, output, } from '@angular/core' -import { QueryClient, injectQuery } from '@tanstack/angular-query-experimental' +import { QueryClient, injectQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/devtools-panel/angular.json b/examples/angular/devtools-panel/angular.json index 1db304e1dbc..a7dcb1397af 100644 --- a/examples/angular/devtools-panel/angular.json +++ b/examples/angular/devtools-panel/angular.json @@ -80,6 +80,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "devtools-panel:build:production" diff --git a/examples/angular/devtools-panel/package.json b/examples/angular/devtools-panel/package.json index 0f45741d6dd..016575928a6 100644 --- a/examples/angular/devtools-panel/package.json +++ b/examples/angular/devtools-panel/package.json @@ -9,20 +9,21 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@angular/router": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/devtools-panel/src/app/app.config.ts b/examples/angular/devtools-panel/src/app/app.config.ts index b51d14cb81e..68222c873ee 100644 --- a/examples/angular/devtools-panel/src/app/app.config.ts +++ b/examples/angular/devtools-panel/src/app/app.config.ts @@ -1,10 +1,7 @@ import { provideHttpClient, withFetch } from '@angular/common/http' import { provideRouter } from '@angular/router' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' import { routes } from './app.routes' import type { ApplicationConfig } from '@angular/core' @@ -12,6 +9,6 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), provideRouter(routes), - provideTanStackQuery(new QueryClient()), + provideTanStackQuery(() => new QueryClient()), ], } diff --git a/examples/angular/devtools-panel/src/app/components/basic-devtools-panel-example.component.ts b/examples/angular/devtools-panel/src/app/components/basic-devtools-panel-example.component.ts index f0a56611eb0..8f3801c52d8 100644 --- a/examples/angular/devtools-panel/src/app/components/basic-devtools-panel-example.component.ts +++ b/examples/angular/devtools-panel/src/app/components/basic-devtools-panel-example.component.ts @@ -4,7 +4,7 @@ import { signal, viewChild, } from '@angular/core' -import { injectDevtoolsPanel } from '@tanstack/angular-query-experimental/devtools-panel' +import { injectDevtoolsPanel } from '@tanstack/angular-query-devtools/devtools-panel' import { ExampleQueryComponent } from './example-query.component' import type { ElementRef } from '@angular/core' diff --git a/examples/angular/devtools-panel/src/app/components/example-query.component.ts b/examples/angular/devtools-panel/src/app/components/example-query.component.ts index 1537167a948..c8ce491e990 100644 --- a/examples/angular/devtools-panel/src/app/components/example-query.component.ts +++ b/examples/angular/devtools-panel/src/app/components/example-query.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { HttpClient } from '@angular/common/http' import { lastValueFrom } from 'rxjs' diff --git a/examples/angular/devtools-panel/src/app/components/lazy-load-devtools-panel-example.component.ts b/examples/angular/devtools-panel/src/app/components/lazy-load-devtools-panel-example.component.ts index 9bb23b11924..47c8192d9b4 100644 --- a/examples/angular/devtools-panel/src/app/components/lazy-load-devtools-panel-example.component.ts +++ b/examples/angular/devtools-panel/src/app/components/lazy-load-devtools-panel-example.component.ts @@ -5,12 +5,13 @@ import { computed, effect, inject, + runInInjectionContext, signal, viewChild, } from '@angular/core' import { ExampleQueryComponent } from './example-query.component' import type { ElementRef } from '@angular/core' -import type { DevtoolsPanelRef } from '@tanstack/angular-query-experimental/devtools-panel' +import type { DevtoolsPanelRef } from '@tanstack/angular-query-devtools/devtools-panel' @Component({ selector: 'lazy-load-devtools-panel-example', @@ -49,11 +50,11 @@ export default class LazyLoadDevtoolsPanelExampleComponent { if (this.devtools()) return if (this.isOpen()) { this.devtools.set( - import('@tanstack/angular-query-experimental/devtools-panel').then( + import('@tanstack/angular-query-devtools/devtools-panel').then( ({ injectDevtoolsPanel }) => - injectDevtoolsPanel(this.devToolsOptions, { - injector: this.injector, - }), + runInInjectionContext(this.injector, () => + injectDevtoolsPanel(this.devToolsOptions), + ), ), ) } diff --git a/examples/angular/dynamic-devtools/.devcontainer/devcontainer.json b/examples/angular/dynamic-devtools/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..365adf8f4c3 --- /dev/null +++ b/examples/angular/dynamic-devtools/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Node.js", + "image": "mcr.microsoft.com/devcontainers/javascript-node:22" +} diff --git a/examples/angular/dynamic-devtools/.eslintrc.cjs b/examples/angular/dynamic-devtools/.eslintrc.cjs new file mode 100644 index 00000000000..cca134ce166 --- /dev/null +++ b/examples/angular/dynamic-devtools/.eslintrc.cjs @@ -0,0 +1,6 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = {} + +module.exports = config diff --git a/examples/angular/dynamic-devtools/README.md b/examples/angular/dynamic-devtools/README.md new file mode 100644 index 00000000000..fab7a9d9df2 --- /dev/null +++ b/examples/angular/dynamic-devtools/README.md @@ -0,0 +1,10 @@ +# TanStack Query Angular dynamic devtools example + +Devtools load automatically in development. In production builds, press **⌘ Ctrl Shift D** (macOS) to load them on demand. + +To run: + +- From the repo root: `pnpm install` then `pnpm --filter @tanstack/query-example-angular-dynamic-devtools start` +- From this folder: `pnpm start` + +For a production-like devserver (so you can try the shortcut), use `ng serve --configuration production`. diff --git a/examples/angular/dynamic-devtools/angular.json b/examples/angular/dynamic-devtools/angular.json new file mode 100644 index 00000000000..16d6a1b3a26 --- /dev/null +++ b/examples/angular/dynamic-devtools/angular.json @@ -0,0 +1,133 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "pnpm", + "analytics": false, + "cache": { + "enabled": false + } + }, + "newProjectRoot": "projects", + "projects": { + "dynamic-devtools": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "inlineTemplate": true, + "inlineStyle": true, + "skipTests": true + }, + "@schematics/angular:class": { + "skipTests": true + }, + "@schematics/angular:directive": { + "skipTests": true + }, + "@schematics/angular:guard": { + "skipTests": true + }, + "@schematics/angular:interceptor": { + "skipTests": true + }, + "@schematics/angular:pipe": { + "skipTests": true + }, + "@schematics/angular:resolver": { + "skipTests": true + }, + "@schematics/angular:service": { + "skipTests": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/dynamic-devtools", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "assets": ["src/favicon.ico"], + "styles": ["src/styles.css"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, + "configurations": { + "production": { + "buildTarget": "dynamic-devtools:build:production" + }, + "development": { + "buildTarget": "dynamic-devtools:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n", + "options": { + "buildTarget": "dynamic-devtools:build" + } + } + } + } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } + } +} diff --git a/examples/angular/dynamic-devtools/package.json b/examples/angular/dynamic-devtools/package.json new file mode 100644 index 00000000000..d2080678fe4 --- /dev/null +++ b/examples/angular/dynamic-devtools/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tanstack/query-example-angular-dynamic-devtools", + "type": "module", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development" + }, + "private": true, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "zone.js": "0.16.1" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" + } +} diff --git a/examples/angular/dynamic-devtools/src/app/app.component.ts b/examples/angular/dynamic-devtools/src/app/app.component.ts new file mode 100644 index 00000000000..4f389242fb8 --- /dev/null +++ b/examples/angular/dynamic-devtools/src/app/app.component.ts @@ -0,0 +1,10 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core' +import { DynamicDevtoolsExampleComponent } from './components/dynamic-devtools-example.component' + +@Component({ + selector: 'app-root', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [DynamicDevtoolsExampleComponent], + template: ``, +}) +export class AppComponent {} diff --git a/examples/angular/dynamic-devtools/src/app/app.config.ts b/examples/angular/dynamic-devtools/src/app/app.config.ts new file mode 100644 index 00000000000..30f1915f6aa --- /dev/null +++ b/examples/angular/dynamic-devtools/src/app/app.config.ts @@ -0,0 +1,18 @@ +import { provideHttpClient, withFetch } from '@angular/common/http' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools/production' +import { inject } from '@angular/core' +import type { ApplicationConfig } from '@angular/core' +import { DevtoolsOptionsManager } from './devtools-options.manager' + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(withFetch()), + provideTanStackQuery( + () => new QueryClient(), + withDevtools(() => ({ + loadDevtools: inject(DevtoolsOptionsManager).loadDevtools, + })), + ), + ], +} diff --git a/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.html b/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.html new file mode 100644 index 00000000000..9d68159ed16 --- /dev/null +++ b/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.html @@ -0,0 +1,18 @@ +

+ Devtools load in development automatically. For a production build, press + CtrlShiftD once to load them. +

+ +@if (query.isPending()) { +
Loading...
+} +@if (query.isError()) { +
An error has occurred: {{ query.error().message }}
+} +@if (query.data(); as data) { +

{{ data.name }}

+

{{ data.description }}

+ 👀 {{ data.subscribers_count }} + ✨ {{ data.stargazers_count }} + 🍴 {{ data.forks_count }} +} diff --git a/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.ts b/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.ts new file mode 100644 index 00000000000..e810f3ec773 --- /dev/null +++ b/examples/angular/dynamic-devtools/src/app/components/dynamic-devtools-example.component.ts @@ -0,0 +1,29 @@ +import { HttpClient } from '@angular/common/http' +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { injectQuery } from '@tanstack/angular-query' +import { lastValueFrom } from 'rxjs' + +interface Response { + name: string + description: string + subscribers_count: number + stargazers_count: number + forks_count: number +} + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'dynamic-devtools-example', + templateUrl: './dynamic-devtools-example.component.html', +}) +export class DynamicDevtoolsExampleComponent { + readonly #http = inject(HttpClient) + + readonly query = injectQuery(() => ({ + queryKey: ['repoData'], + queryFn: () => + lastValueFrom( + this.#http.get('https://api.github.com/repos/tanstack/query'), + ), + })) +} diff --git a/examples/angular/dynamic-devtools/src/app/devtools-options.manager.ts b/examples/angular/dynamic-devtools/src/app/devtools-options.manager.ts new file mode 100644 index 00000000000..23525f841ac --- /dev/null +++ b/examples/angular/dynamic-devtools/src/app/devtools-options.manager.ts @@ -0,0 +1,19 @@ +import { Injectable, isDevMode } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' +import { fromEvent, map, scan } from 'rxjs' + +@Injectable({ providedIn: 'root' }) +export class DevtoolsOptionsManager { + readonly loadDevtools = toSignal( + fromEvent(document, 'keydown').pipe( + map( + (event): boolean => + event.metaKey && event.ctrlKey && event.shiftKey && event.key === 'D', + ), + scan((acc, curr) => acc || curr, false), + ), + { + initialValue: false, + }, + ) +} diff --git a/integrations/angular-cli-20/public/favicon.ico b/examples/angular/dynamic-devtools/src/favicon.ico similarity index 100% rename from integrations/angular-cli-20/public/favicon.ico rename to examples/angular/dynamic-devtools/src/favicon.ico diff --git a/examples/angular/dynamic-devtools/src/index.html b/examples/angular/dynamic-devtools/src/index.html new file mode 100644 index 00000000000..9988a64448b --- /dev/null +++ b/examples/angular/dynamic-devtools/src/index.html @@ -0,0 +1,13 @@ + + + + + TanStack Query Angular dynamic devtools example + + + + + + + + diff --git a/examples/angular/dynamic-devtools/src/main.ts b/examples/angular/dynamic-devtools/src/main.ts new file mode 100644 index 00000000000..c3d8f9af997 --- /dev/null +++ b/examples/angular/dynamic-devtools/src/main.ts @@ -0,0 +1,5 @@ +import { bootstrapApplication } from '@angular/platform-browser' +import { appConfig } from './app/app.config' +import { AppComponent } from './app/app.component' + +bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)) diff --git a/examples/angular/dynamic-devtools/src/styles.css b/examples/angular/dynamic-devtools/src/styles.css new file mode 100644 index 00000000000..256d518a5e1 --- /dev/null +++ b/examples/angular/dynamic-devtools/src/styles.css @@ -0,0 +1,18 @@ +/* You can add global styles to this file, and also import other style files */ + +.hint { + font-size: 0.9rem; + color: #444; + max-width: 42rem; + line-height: 1.45; +} + +kbd { + display: inline-block; + padding: 0.1rem 0.35rem; + margin: 0 0.1rem; + font-size: 0.8rem; + border: 1px solid #ccc; + border-radius: 3px; + background: #f7f7f7; +} diff --git a/examples/angular/dynamic-devtools/tsconfig.app.json b/examples/angular/dynamic-devtools/tsconfig.app.json new file mode 100644 index 00000000000..5b9d3c5ecb0 --- /dev/null +++ b/examples/angular/dynamic-devtools/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/examples/angular/dynamic-devtools/tsconfig.json b/examples/angular/dynamic-devtools/tsconfig.json new file mode 100644 index 00000000000..44e0a5238b1 --- /dev/null +++ b/examples/angular/dynamic-devtools/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "esModuleInterop": true, + "sourceMap": true, + "declaration": false, + "experimentalDecorators": true, + "moduleResolution": "Bundler", + "importHelpers": true, + "target": "ES2022", + "module": "ES2022", + "useDefineForClassFields": false, + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictStandalone": true, + "strictTemplates": true + } +} diff --git a/examples/angular/dynamic-queries/README.md b/examples/angular/dynamic-queries/README.md new file mode 100644 index 00000000000..b2e897b81b7 --- /dev/null +++ b/examples/angular/dynamic-queries/README.md @@ -0,0 +1,10 @@ +# TanStack Query Angular dynamic queries example + +This example keeps a list of items in an Angular signal and creates one +`injectQueries` entry for each item. Add and remove items to see the query list +and its rendered results update together. + +To run this example: + +- `pnpm install` +- `pnpm start` diff --git a/examples/angular/dynamic-queries/angular.json b/examples/angular/dynamic-queries/angular.json new file mode 100644 index 00000000000..7db4b2559d3 --- /dev/null +++ b/examples/angular/dynamic-queries/angular.json @@ -0,0 +1,49 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { "packageManager": "pnpm", "analytics": false }, + "projects": { + "dynamic-queries": { + "projectType": "application", + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/dynamic-queries", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "assets": [], + "styles": ["src/styles.css"] + }, + "configurations": { + "production": { "outputHashing": "all" }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, + "configurations": { + "production": { "buildTarget": "dynamic-queries:build:production" }, + "development": { + "buildTarget": "dynamic-queries:build:development" + } + }, + "defaultConfiguration": "development" + } + } + } + } +} diff --git a/examples/angular/dynamic-queries/package.json b/examples/angular/dynamic-queries/package.json new file mode 100644 index 00000000000..757335c5767 --- /dev/null +++ b/examples/angular/dynamic-queries/package.json @@ -0,0 +1,28 @@ +{ + "name": "@tanstack/query-example-angular-dynamic-queries", + "private": true, + "type": "module", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development" + }, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "zone.js": "0.16.1" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" + } +} diff --git a/examples/angular/dynamic-queries/src/app/app.component.ts b/examples/angular/dynamic-queries/src/app/app.component.ts new file mode 100644 index 00000000000..f3cc5ecd8d4 --- /dev/null +++ b/examples/angular/dynamic-queries/src/app/app.component.ts @@ -0,0 +1,107 @@ +import { ChangeDetectionStrategy, Component, signal } from '@angular/core' +import { injectQueries } from '@tanstack/angular-query' + +type Item = { + id: number + name: string +} + +type ItemDetails = { + description: string + updatedAt: string +} + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-root', + template: ` +
+

Dynamic queries

+

+ Each item below owns one entry in injectQueries. Add or + remove items to change the query list. +

+ + + + @if (items().length === 0) { +

No items. Add one to start a query.

+ } @else { +
    + @for (item of items(); track item.id; let index = $index) { +
  • +
    + {{ item.name }} + @if (queries()[index].isPending()) { + Loading... + } @else if (queries()[index].isError()) { + Could not load this item. + } @else { + {{ queries()[index].data()?.description }} + } +
    + +
  • + } +
+ } +
+ `, + styles: ` + main { + font: + 16px/1.5 system-ui, + sans-serif; + margin: 3rem auto; + max-width: 42rem; + padding: 0 1rem; + } + + button { + cursor: pointer; + margin: 0.25rem; + padding: 0.45rem 0.7rem; + } + + li { + align-items: center; + display: flex; + justify-content: space-between; + margin: 0.75rem 0; + } + `, +}) +export class AppComponent { + readonly items = signal>([ + { id: 1, name: 'First item' }, + { id: 2, name: 'Second item' }, + ]) + + readonly queries = injectQueries(() => ({ + queries: this.items().map((item) => ({ + queryKey: ['item-details', item.id], + queryFn: () => this.loadItemDetails(item), + })), + })) + + #nextId = 3 + + addItem(): void { + const id = this.#nextId++ + this.items.update((items) => [...items, { id, name: `Item ${id}` }]) + } + + removeItem(id: number): void { + this.items.update((items) => items.filter((item) => item.id !== id)) + } + + private async loadItemDetails(item: Item): Promise { + await new Promise((resolve) => setTimeout(resolve, 350)) + return { + description: `${item.name} was loaded by its own query.`, + updatedAt: new Date().toLocaleTimeString(), + } + } +} diff --git a/examples/angular/dynamic-queries/src/app/app.config.ts b/examples/angular/dynamic-queries/src/app/app.config.ts new file mode 100644 index 00000000000..0038688124f --- /dev/null +++ b/examples/angular/dynamic-queries/src/app/app.config.ts @@ -0,0 +1,7 @@ +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' +import type { ApplicationConfig } from '@angular/core' + +export const appConfig: ApplicationConfig = { + providers: [provideTanStackQuery(() => new QueryClient(), withDevtools())], +} diff --git a/examples/angular/dynamic-queries/src/index.html b/examples/angular/dynamic-queries/src/index.html new file mode 100644 index 00000000000..cf7c7b4728d --- /dev/null +++ b/examples/angular/dynamic-queries/src/index.html @@ -0,0 +1,12 @@ + + + + + Dynamic Angular queries + + + + + + + diff --git a/examples/angular/dynamic-queries/src/main.ts b/examples/angular/dynamic-queries/src/main.ts new file mode 100644 index 00000000000..ef15666eff7 --- /dev/null +++ b/examples/angular/dynamic-queries/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser' +import { appConfig } from './app/app.config' +import { AppComponent } from './app/app.component' + +bootstrapApplication(AppComponent, appConfig).catch((error) => + console.error(error), +) diff --git a/examples/angular/dynamic-queries/src/styles.css b/examples/angular/dynamic-queries/src/styles.css new file mode 100644 index 00000000000..acd67bf82bc --- /dev/null +++ b/examples/angular/dynamic-queries/src/styles.css @@ -0,0 +1,5 @@ +body { + background: #f7f7f8; + color: #202124; + margin: 0; +} diff --git a/examples/angular/dynamic-queries/tsconfig.app.json b/examples/angular/dynamic-queries/tsconfig.app.json new file mode 100644 index 00000000000..a7da28e8cea --- /dev/null +++ b/examples/angular/dynamic-queries/tsconfig.app.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/examples/angular/dynamic-queries/tsconfig.json b/examples/angular/dynamic-queries/tsconfig.json new file mode 100644 index 00000000000..f42b458e4ac --- /dev/null +++ b/examples/angular/dynamic-queries/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "preserve", + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/examples/angular/infinite-query-with-max-pages/angular.json b/examples/angular/infinite-query-with-max-pages/angular.json index 702c1da4eda..53d8e9c61c8 100644 --- a/examples/angular/infinite-query-with-max-pages/angular.json +++ b/examples/angular/infinite-query-with-max-pages/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "infinite-query-with-max-pages:build:production" diff --git a/examples/angular/infinite-query-with-max-pages/package.json b/examples/angular/infinite-query-with-max-pages/package.json index 269aa20369d..69eeb135e3b 100644 --- a/examples/angular/infinite-query-with-max-pages/package.json +++ b/examples/angular/infinite-query-with-max-pages/package.json @@ -9,19 +9,20 @@ "watch": "ng build --watch --configuration development" }, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/infinite-query-with-max-pages/src/app/app.config.ts b/examples/angular/infinite-query-with-max-pages/src/app/app.config.ts index 06854c4a7d4..87870fdbbb2 100644 --- a/examples/angular/infinite-query-with-max-pages/src/app/app.config.ts +++ b/examples/angular/infinite-query-with-max-pages/src/app/app.config.ts @@ -3,11 +3,8 @@ import { withFetch, withInterceptors, } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import { projectsMockInterceptor } from './api/projects-mock.interceptor' import type { ApplicationConfig } from '@angular/core' @@ -15,13 +12,14 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptors([projectsMockInterceptor]), withFetch()), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), ), ], diff --git a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts index 71c141e3e46..d9d04cc9f24 100644 --- a/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts +++ b/examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts @@ -4,7 +4,7 @@ import { computed, inject, } from '@angular/core' -import { injectInfiniteQuery } from '@tanstack/angular-query-experimental' +import { injectInfiniteQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { ProjectStyleDirective } from '../directives/project-style.directive' import { ProjectsService } from '../services/projects.service' diff --git a/examples/angular/optimistic-updates/angular.json b/examples/angular/optimistic-updates/angular.json index 7051bc8815e..47e04f26d66 100644 --- a/examples/angular/optimistic-updates/angular.json +++ b/examples/angular/optimistic-updates/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "optimistic-updates:build:production" diff --git a/examples/angular/optimistic-updates/package.json b/examples/angular/optimistic-updates/package.json index 845ee416f2b..7192e04c8f8 100644 --- a/examples/angular/optimistic-updates/package.json +++ b/examples/angular/optimistic-updates/package.json @@ -9,20 +9,21 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/forms": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/optimistic-updates/src/app/app.config.ts b/examples/angular/optimistic-updates/src/app/app.config.ts index b9b2b6c36fa..ba6116f9f6b 100644 --- a/examples/angular/optimistic-updates/src/app/app.config.ts +++ b/examples/angular/optimistic-updates/src/app/app.config.ts @@ -3,11 +3,8 @@ import { withFetch, withInterceptors, } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import { mockInterceptor } from './interceptor/mock-api.interceptor' import type { ApplicationConfig } from '@angular/core' @@ -15,13 +12,14 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch(), withInterceptors([mockInterceptor])), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), ), ], diff --git a/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts b/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts index 2b0b4cc1c4f..cbb01f0c0d5 100644 --- a/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts +++ b/examples/angular/optimistic-updates/src/app/components/optimistic-updates.component.ts @@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { injectMutation, injectQuery, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { FormsModule } from '@angular/forms' import { DatePipe } from '@angular/common' import { TasksService } from '../services/tasks.service' diff --git a/examples/angular/optimistic-updates/src/app/services/tasks.service.ts b/examples/angular/optimistic-updates/src/app/services/tasks.service.ts index d9a2d62428d..2da0dee782c 100644 --- a/examples/angular/optimistic-updates/src/app/services/tasks.service.ts +++ b/examples/angular/optimistic-updates/src/app/services/tasks.service.ts @@ -4,7 +4,7 @@ import { QueryClient, mutationOptions, queryOptions, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' diff --git a/examples/angular/pagination/angular.json b/examples/angular/pagination/angular.json index 222be20143e..5f1e19ba0db 100644 --- a/examples/angular/pagination/angular.json +++ b/examples/angular/pagination/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "pagination:build:production" diff --git a/examples/angular/pagination/package.json b/examples/angular/pagination/package.json index 2a92c4f9c1f..57e6dcfb28b 100644 --- a/examples/angular/pagination/package.json +++ b/examples/angular/pagination/package.json @@ -9,19 +9,20 @@ "watch": "ng build --watch --configuration development" }, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/pagination/src/app/app.config.ts b/examples/angular/pagination/src/app/app.config.ts index dc3189ba84b..b36a88a566c 100644 --- a/examples/angular/pagination/src/app/app.config.ts +++ b/examples/angular/pagination/src/app/app.config.ts @@ -3,17 +3,14 @@ import { withFetch, withInterceptors, } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import { projectsMockInterceptor } from './api/projects-mock.interceptor' import type { ApplicationConfig } from '@angular/core' export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withInterceptors([projectsMockInterceptor]), withFetch()), - provideTanStackQuery(new QueryClient(), withDevtools()), + provideTanStackQuery(() => new QueryClient(), withDevtools()), ], } diff --git a/examples/angular/pagination/src/app/components/example.component.ts b/examples/angular/pagination/src/app/components/example.component.ts index 217d7d4e40b..a1b02eec349 100644 --- a/examples/angular/pagination/src/app/components/example.component.ts +++ b/examples/angular/pagination/src/app/components/example.component.ts @@ -10,8 +10,7 @@ import { QueryClient, injectQuery, keepPreviousData, - noop, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { ProjectsService } from '../services/projects.service' diff --git a/examples/angular/query-options-from-a-service/angular.json b/examples/angular/query-options-from-a-service/angular.json index 3815edc28d3..8c72de140c0 100644 --- a/examples/angular/query-options-from-a-service/angular.json +++ b/examples/angular/query-options-from-a-service/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "query-options-from-a-service:build:production" diff --git a/examples/angular/query-options-from-a-service/package.json b/examples/angular/query-options-from-a-service/package.json index c1216d44ef3..93ba8214cca 100644 --- a/examples/angular/query-options-from-a-service/package.json +++ b/examples/angular/query-options-from-a-service/package.json @@ -9,20 +9,21 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@angular/router": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/query-options-from-a-service/src/app/app.config.ts b/examples/angular/query-options-from-a-service/src/app/app.config.ts index dd6675f997b..06f5240bdfe 100644 --- a/examples/angular/query-options-from-a-service/src/app/app.config.ts +++ b/examples/angular/query-options-from-a-service/src/app/app.config.ts @@ -1,11 +1,8 @@ import { provideHttpClient, withFetch } from '@angular/common/http' import { provideRouter, withComponentInputBinding } from '@angular/router' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { withDevtools } from '@tanstack/angular-query-devtools' import { routes } from './app.routes' import type { ApplicationConfig } from '@angular/core' @@ -13,6 +10,6 @@ export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), provideRouter(routes, withComponentInputBinding()), - provideTanStackQuery(new QueryClient(), withDevtools()), + provideTanStackQuery(() => new QueryClient(), withDevtools()), ], } diff --git a/examples/angular/query-options-from-a-service/src/app/components/post.component.ts b/examples/angular/query-options-from-a-service/src/app/components/post.component.ts index 87bebeb44d0..68cc4bf1972 100644 --- a/examples/angular/query-options-from-a-service/src/app/components/post.component.ts +++ b/examples/angular/query-options-from-a-service/src/app/components/post.component.ts @@ -6,7 +6,7 @@ import { numberAttribute, } from '@angular/core' import { RouterLink } from '@angular/router' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { QueriesService } from '../services/queries-service' @Component({ diff --git a/examples/angular/query-options-from-a-service/src/app/components/posts.component.ts b/examples/angular/query-options-from-a-service/src/app/components/posts.component.ts index ed26b0aed7f..4d526c2fe61 100644 --- a/examples/angular/query-options-from-a-service/src/app/components/posts.component.ts +++ b/examples/angular/query-options-from-a-service/src/app/components/posts.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { RouterLink } from '@angular/router' -import { QueryClient, injectQuery } from '@tanstack/angular-query-experimental' +import { QueryClient, injectQuery } from '@tanstack/angular-query' import { QueriesService } from '../services/queries-service' @Component({ diff --git a/examples/angular/query-options-from-a-service/src/app/services/queries-service.ts b/examples/angular/query-options-from-a-service/src/app/services/queries-service.ts index 70ad35c5d79..c1f01e62b6a 100644 --- a/examples/angular/query-options-from-a-service/src/app/services/queries-service.ts +++ b/examples/angular/query-options-from-a-service/src/app/services/queries-service.ts @@ -1,6 +1,6 @@ import { Injectable, inject } from '@angular/core' import { lastValueFrom } from 'rxjs' -import { queryOptions } from '@tanstack/angular-query-experimental' +import { queryOptions } from '@tanstack/angular-query' import { HttpClient } from '@angular/common/http' export interface Post { diff --git a/examples/angular/router/angular.json b/examples/angular/router/angular.json index b52ac654f31..3bcecbc602f 100644 --- a/examples/angular/router/angular.json +++ b/examples/angular/router/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "router:build:production" diff --git a/examples/angular/router/package.json b/examples/angular/router/package.json index 6ac7595f77a..3cdb8945092 100644 --- a/examples/angular/router/package.json +++ b/examples/angular/router/package.json @@ -9,20 +9,21 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@angular/router": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/router/src/app/app.config.ts b/examples/angular/router/src/app/app.config.ts index 3fbec800b1f..b5a2a890f7f 100644 --- a/examples/angular/router/src/app/app.config.ts +++ b/examples/angular/router/src/app/app.config.ts @@ -1,18 +1,15 @@ import { provideHttpClient, withFetch } from '@angular/common/http' import { provideRouter, withComponentInputBinding } from '@angular/router' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { withDevtools } from '@tanstack/angular-query-devtools' import { routes } from './app.routes' import type { ApplicationConfig } from '@angular/core' export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), - provideTanStackQuery(new QueryClient(), withDevtools()), + provideTanStackQuery(() => new QueryClient(), withDevtools()), provideRouter(routes, withComponentInputBinding()), ], } diff --git a/examples/angular/router/src/app/components/post.component.ts b/examples/angular/router/src/app/components/post.component.ts index 118adc42ba9..56191ea1bdd 100644 --- a/examples/angular/router/src/app/components/post.component.ts +++ b/examples/angular/router/src/app/components/post.component.ts @@ -6,7 +6,7 @@ import { numberAttribute, } from '@angular/core' import { RouterLink } from '@angular/router' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/router/src/app/components/posts.component.ts b/examples/angular/router/src/app/components/posts.component.ts index 5284a749085..7d65b5b922c 100644 --- a/examples/angular/router/src/app/components/posts.component.ts +++ b/examples/angular/router/src/app/components/posts.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' import { RouterLink } from '@angular/router' -import { QueryClient, injectQuery } from '@tanstack/angular-query-experimental' +import { QueryClient, injectQuery } from '@tanstack/angular-query' import { lastValueFrom } from 'rxjs' import { PostsService } from '../services/posts-service' diff --git a/examples/angular/rxjs/angular.json b/examples/angular/rxjs/angular.json index 9a79ecf1a36..d1b2d572342 100644 --- a/examples/angular/rxjs/angular.json +++ b/examples/angular/rxjs/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "rxjs:build:production" diff --git a/examples/angular/rxjs/package.json b/examples/angular/rxjs/package.json index 5b422c994bc..d169032dbf2 100644 --- a/examples/angular/rxjs/package.json +++ b/examples/angular/rxjs/package.json @@ -9,20 +9,21 @@ "watch": "ng build --watch --configuration development" }, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/forms": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/rxjs/src/app/app.config.ts b/examples/angular/rxjs/src/app/app.config.ts index 27eb15a53b1..8a2ebb37baf 100644 --- a/examples/angular/rxjs/src/app/app.config.ts +++ b/examples/angular/rxjs/src/app/app.config.ts @@ -3,11 +3,8 @@ import { withFetch, withInterceptors, } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import { autocompleteMockInterceptor } from './api/autocomplete-mock.interceptor' import type { ApplicationConfig } from '@angular/core' @@ -18,13 +15,14 @@ export const appConfig: ApplicationConfig = { withInterceptors([autocompleteMockInterceptor]), ), provideTanStackQuery( - new QueryClient({ - defaultOptions: { - queries: { - gcTime: 1000 * 60 * 60 * 24, // 24 hours + () => + new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + }, }, - }, - }), + }), withDevtools(), ), ], diff --git a/examples/angular/rxjs/src/app/components/example.component.ts b/examples/angular/rxjs/src/app/components/example.component.ts index 16e93735a98..e7aa3216215 100644 --- a/examples/angular/rxjs/src/app/components/example.component.ts +++ b/examples/angular/rxjs/src/app/components/example.component.ts @@ -4,7 +4,7 @@ import { NonNullableFormBuilder, ReactiveFormsModule } from '@angular/forms' import { injectQuery, keepPreviousData, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { debounceTime, distinctUntilChanged, lastValueFrom } from 'rxjs' import { AutocompleteService } from '../services/autocomplete-service' diff --git a/examples/angular/simple/angular.json b/examples/angular/simple/angular.json index 0075533a99a..7da2f4f0798 100644 --- a/examples/angular/simple/angular.json +++ b/examples/angular/simple/angular.json @@ -82,6 +82,9 @@ }, "serve": { "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, "configurations": { "production": { "buildTarget": "simple:build:production" diff --git a/examples/angular/simple/package.json b/examples/angular/simple/package.json index 2c26b0f8e3f..c945af1643d 100644 --- a/examples/angular/simple/package.json +++ b/examples/angular/simple/package.json @@ -9,19 +9,20 @@ }, "private": true, "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", "rxjs": "^7.8.2", "tslib": "^2.8.1", - "zone.js": "0.15.0" + "zone.js": "0.16.1" }, "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "5.8.3" + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" } } diff --git a/examples/angular/simple/src/app/app.config.ts b/examples/angular/simple/src/app/app.config.ts index 9c7eda25974..6d8eec79b17 100644 --- a/examples/angular/simple/src/app/app.config.ts +++ b/examples/angular/simple/src/app/app.config.ts @@ -1,14 +1,11 @@ import { provideHttpClient, withFetch } from '@angular/common/http' -import { - QueryClient, - provideTanStackQuery, -} from '@tanstack/angular-query-experimental' -import { withDevtools } from '@tanstack/angular-query-experimental/devtools' +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' import type { ApplicationConfig } from '@angular/core' export const appConfig: ApplicationConfig = { providers: [ provideHttpClient(withFetch()), - provideTanStackQuery(new QueryClient(), withDevtools()), + provideTanStackQuery(() => new QueryClient(), withDevtools()), ], } diff --git a/examples/angular/simple/src/app/components/simple-example.component.ts b/examples/angular/simple/src/app/components/simple-example.component.ts index fe43999eb00..461cfc65878 100644 --- a/examples/angular/simple/src/app/components/simple-example.component.ts +++ b/examples/angular/simple/src/app/components/simple-example.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, inject } from '@angular/core' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { HttpClient } from '@angular/common/http' import { lastValueFrom } from 'rxjs' diff --git a/examples/angular/ssr-persist/.devcontainer/devcontainer.json b/examples/angular/ssr-persist/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..365adf8f4c3 --- /dev/null +++ b/examples/angular/ssr-persist/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Node.js", + "image": "mcr.microsoft.com/devcontainers/javascript-node:22" +} diff --git a/examples/angular/ssr-persist/.eslintrc.cjs b/examples/angular/ssr-persist/.eslintrc.cjs new file mode 100644 index 00000000000..cca134ce166 --- /dev/null +++ b/examples/angular/ssr-persist/.eslintrc.cjs @@ -0,0 +1,6 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = {} + +module.exports = config diff --git a/examples/angular/ssr-persist/README.md b/examples/angular/ssr-persist/README.md new file mode 100644 index 00000000000..e981642c723 --- /dev/null +++ b/examples/angular/ssr-persist/README.md @@ -0,0 +1,14 @@ +# TanStack Query Angular SSR + persistence example + +Combines [SSR/hydration](https://tanstack.com/query/latest/docs/framework/angular/guides/ssr), a `localStorage` persister, and a **client-only island**: `ClientPersistDemoComponent` is mounted with `afterNextRender`, so its `injectQuery` `queryFn` does not run during SSR (unlike `@defer` main content, which is still rendered on the server for incremental hydration). + +- **Server:** same bootstrap config shape as the base SSR example; `withPersistQueryClient` uses a factory and skips work when not in the browser. +- **Client:** `showClientDemo` is set to `true` in `afterNextRender`, then `` is created — optional `dehydrateOptions.shouldDehydrateQuery` can scope persistence to `client-persist` query keys only. + +To run: + +- From the repo root: `pnpm install` then + `pnpm --filter @tanstack/query-example-angular-ssr-persist start` +- Production SSR server after build: + `pnpm --filter @tanstack/query-example-angular-ssr-persist run build` + then `pnpm --filter @tanstack/query-example-angular-ssr-persist run serve:ssr` diff --git a/examples/angular/ssr-persist/angular.json b/examples/angular/ssr-persist/angular.json new file mode 100644 index 00000000000..b4bdec992b9 --- /dev/null +++ b/examples/angular/ssr-persist/angular.json @@ -0,0 +1,140 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "pnpm", + "analytics": false, + "cache": { + "enabled": false + } + }, + "newProjectRoot": "projects", + "projects": { + "ssr-persist": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "inlineTemplate": true, + "inlineStyle": true, + "skipTests": true + }, + "@schematics/angular:class": { + "skipTests": true + }, + "@schematics/angular:directive": { + "skipTests": true + }, + "@schematics/angular:guard": { + "skipTests": true + }, + "@schematics/angular:interceptor": { + "skipTests": true + }, + "@schematics/angular:pipe": { + "skipTests": true + }, + "@schematics/angular:resolver": { + "skipTests": true + }, + "@schematics/angular:service": { + "skipTests": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/ssr-persist", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "styles": ["src/styles.css"], + "scripts": [], + "server": "src/main.server.ts", + "outputMode": "server", + "security": { + "allowedHosts": ["localhost", "127.0.0.1"] + }, + "ssr": { + "entry": "src/server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, + "configurations": { + "production": { + "buildTarget": "ssr-persist:build:production" + }, + "development": { + "buildTarget": "ssr-persist:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n", + "options": { + "buildTarget": "ssr-persist:build" + } + } + } + } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } + } +} diff --git a/examples/angular/ssr-persist/package.json b/examples/angular/ssr-persist/package.json new file mode 100644 index 00000000000..547277b5e6f --- /dev/null +++ b/examples/angular/ssr-persist/package.json @@ -0,0 +1,35 @@ +{ + "name": "@tanstack/query-example-angular-ssr-persist", + "type": "module", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "serve:ssr": "node dist/ssr-persist/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/platform-server": "^22.0.0", + "@angular/ssr": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "@tanstack/angular-query-persist-client": "workspace:*", + "@tanstack/query-async-storage-persister": "workspace:*", + "express": "^5.1.0", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "zone.js": "0.16.1" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@types/express": "^5.0.1", + "typescript": "6.0.3" + } +} diff --git a/examples/angular/ssr-persist/src/app/app.component.ts b/examples/angular/ssr-persist/src/app/app.component.ts new file mode 100644 index 00000000000..ffdde21e388 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/app.component.ts @@ -0,0 +1,127 @@ +import { + ChangeDetectionStrategy, + Component, + afterNextRender, + signal, +} from '@angular/core' +import { ClientPersistDemoComponent } from './components/client-persist-demo.component' +import { PostsComponent } from './components/posts.component' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'ssr-persist-example', + imports: [ClientPersistDemoComponent, PostsComponent], + template: ` +
+
+

Angular SSR + client-only island + persistence

+

SSR queries vs client-only persisted queries

+

+ Posts use queryKey: ['posts'] and run on the server. + Configure the persister with + dehydrateOptions.shouldDehydrateQuery if you want only + client-persist keys in localStorage. +

+

+ The panel below is mounted only in the browser via + afterNextRender, so its query never runs during SSR. + After the first visit, hard-reload: the timestamp can be restored from + persistence while posts render from SSR again. +

+
+ +
+

Server data

+ +
+ +
+

Client-only (afterNextRender)

+ @if (showClientDemo()) { + + } @else { +

+ Server render: this placeholder has no + client-persist-demo component yet — it is created after + the first browser frame via afterNextRender. +

+ } +
+
+ `, + styles: ` + .page { + max-width: 960px; + margin: 0 auto; + padding: 48px 20px 72px; + } + + .hero { + margin-bottom: 32px; + } + + .eyebrow { + margin: 0 0 8px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: #9b5c00; + } + + h1 { + margin: 0 0 12px; + font-size: clamp(2.2rem, 5vw, 4rem); + line-height: 1; + } + + .lede { + max-width: 640px; + margin: 0 0 12px; + font-size: 1rem; + line-height: 1.6; + color: #4b5563; + } + + .hint { + font-size: 0.95rem; + color: #6b7280; + } + + code { + font-size: 0.88em; + padding: 0.1em 0.35em; + border-radius: 4px; + background: #f3f4f6; + } + + .region { + margin-bottom: 40px; + } + + .region-title { + margin: 0 0 16px; + font-size: 1.25rem; + color: #1f2937; + } + + .placeholder { + margin: 0; + padding: 16px; + border-radius: 12px; + background: #f9fafb; + color: #6b7280; + line-height: 1.5; + } + `, +}) +export class SsrPersistExampleComponent { + /** When `true`, `ClientPersistDemoComponent` exists only in the browser (not during SSR). */ + readonly showClientDemo = signal(false) + + constructor() { + afterNextRender(() => { + this.showClientDemo.set(true) + }) + } +} diff --git a/examples/angular/ssr-persist/src/app/app.config.server.ts b/examples/angular/ssr-persist/src/app/app.config.server.ts new file mode 100644 index 00000000000..a1a957f51c4 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/app.config.server.ts @@ -0,0 +1,8 @@ +import { mergeApplicationConfig } from '@angular/core' +import { provideServerRendering, withRoutes } from '@angular/ssr' +import { appConfig } from './app.config' +import { serverRoutes } from './app.routes.server' + +export const serverConfig = mergeApplicationConfig(appConfig, { + providers: [provideServerRendering(withRoutes(serverRoutes))], +}) diff --git a/examples/angular/ssr-persist/src/app/app.config.ts b/examples/angular/ssr-persist/src/app/app.config.ts new file mode 100644 index 00000000000..d8bb855fe3a --- /dev/null +++ b/examples/angular/ssr-persist/src/app/app.config.ts @@ -0,0 +1,31 @@ +import { provideHttpClient, withFetch } from '@angular/common/http' +import type { ApplicationConfig } from '@angular/core' +import { + provideClientHydration, + withEventReplay, +} from '@angular/platform-browser' +import { provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' +import { withPersistQueryClient } from '@tanstack/angular-query-persist-client' +import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister' +import { PERSIST_STORAGE_KEY, createQueryClient } from './query-client' + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(withFetch()), + provideClientHydration(withEventReplay()), + provideTanStackQuery( + createQueryClient, + withDevtools(), + withPersistQueryClient(() => ({ + persistOptions: { + persister: createAsyncStoragePersister({ + storage: localStorage, + key: PERSIST_STORAGE_KEY, + throttleTime: 1000, + }), + }, + })), + ), + ], +} diff --git a/examples/angular/ssr-persist/src/app/app.routes.server.ts b/examples/angular/ssr-persist/src/app/app.routes.server.ts new file mode 100644 index 00000000000..09fcfa3cc95 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/app.routes.server.ts @@ -0,0 +1,9 @@ +import { RenderMode } from '@angular/ssr' +import type { ServerRoute } from '@angular/ssr' + +export const serverRoutes: Array = [ + { + path: '**', + renderMode: RenderMode.Server, + }, +] diff --git a/examples/angular/ssr-persist/src/app/components/client-persist-demo.component.ts b/examples/angular/ssr-persist/src/app/components/client-persist-demo.component.ts new file mode 100644 index 00000000000..41ba96cda48 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/components/client-persist-demo.component.ts @@ -0,0 +1,84 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core' +import { injectQuery } from '@tanstack/angular-query' +import { CLIENT_PERSIST_QUERY_ROOT } from '../query-persist-scope' + +/** + * Mounted only in the browser (parent uses `afterNextRender`), so `queryFn` is not run during SSR. + * Pair with `dehydrateOptions.shouldDehydrateQuery` if you want only `client-persist` keys in storage. + */ +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'client-persist-demo', + template: ` +
+

Client-only island

+

Client-only persisted query

+ @if (demo.isPending()) { +

Loading client-only data…

+ } @else if (demo.isError()) { +

Failed to load.

+ } @else if (demo.data(); as data) { +

{{ data.createdAt }}

+

+ This value is stored under the client-persist query key. + Hard-reload the page: it should reappear from persistence while the + posts list above is rendered from SSR again. +

+ } +
+ `, + styles: ` + .panel { + margin-top: 8px; + padding: 20px; + border-radius: 18px; + border: 1px dashed #c4b5a0; + background: #fffefb; + } + + .badge { + margin: 0 0 8px; + font-size: 11px; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; + color: #7c5a10; + } + + h3 { + margin: 0 0 12px; + font-size: 1.1rem; + } + + .mono { + margin: 0 0 12px; + font-family: ui-monospace, monospace; + font-size: 0.85rem; + word-break: break-all; + } + + .note { + margin: 0; + font-size: 0.9rem; + line-height: 1.5; + color: #4b5563; + } + + code { + font-size: 0.88em; + padding: 0.1em 0.35em; + border-radius: 4px; + background: #f3f4f6; + } + `, +}) +export class ClientPersistDemoComponent { + readonly demo = injectQuery(() => ({ + queryKey: [CLIENT_PERSIST_QUERY_ROOT, 'timestamp-demo'], + queryFn: async () => { + await new Promise((r) => setTimeout(r, 100)) + return { createdAt: new Date().toISOString() } + }, + staleTime: Infinity, + })) +} diff --git a/examples/angular/ssr-persist/src/app/components/posts.component.ts b/examples/angular/ssr-persist/src/app/components/posts.component.ts new file mode 100644 index 00000000000..e9b13754d57 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/components/posts.component.ts @@ -0,0 +1,70 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { injectQuery } from '@tanstack/angular-query' +import { lastValueFrom } from 'rxjs' +import { PostsService } from '../services/posts.service' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'posts', + template: ` + @if (postsQuery.isPending()) { +

Loading posts...

+ } @else if (postsQuery.isError()) { +

Failed to load posts.

+ } @else { +
+ @for (post of postsQuery.data(); track post.id) { +
+

Post #{{ post.id }}

+

{{ post.title }}

+

{{ post.body }}

+
+ } +
+ } + `, + styles: ` + .grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + } + + .card { + padding: 20px; + border: 1px solid #eadfcb; + border-radius: 18px; + background: #fffdf8; + box-shadow: 0 12px 30px rgba(123, 88, 31, 0.08); + } + + .meta { + margin: 0 0 10px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #8b5e34; + } + + h2 { + margin: 0 0 10px; + font-size: 1.1rem; + line-height: 1.3; + } + + p { + margin: 0; + line-height: 1.5; + color: #374151; + } + `, +}) +export class PostsComponent { + readonly #postsService = inject(PostsService) + + readonly postsQuery = injectQuery(() => ({ + queryKey: ['posts'], + queryFn: () => lastValueFrom(this.#postsService.allPosts$()), + })) +} diff --git a/examples/angular/ssr-persist/src/app/query-client.ts b/examples/angular/ssr-persist/src/app/query-client.ts new file mode 100644 index 00000000000..de7b2e1930b --- /dev/null +++ b/examples/angular/ssr-persist/src/app/query-client.ts @@ -0,0 +1,21 @@ +import { isPlatformServer } from '@angular/common' +import { PLATFORM_ID, inject } from '@angular/core' +import { QueryClient } from '@tanstack/angular-query' + +export const PERSIST_STORAGE_KEY = 'tanstack-query-angular-ssr-persist-example' + +export const SHARED_QUERY_DEFAULTS = { + staleTime: 1000 * 30, + gcTime: 1000 * 60 * 60 * 24, +} as const + +export function createQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + ...SHARED_QUERY_DEFAULTS, + ...(isPlatformServer(inject(PLATFORM_ID)) ? { retry: false } : {}), + }, + }, + }) +} diff --git a/examples/angular/ssr-persist/src/app/query-persist-scope.ts b/examples/angular/ssr-persist/src/app/query-persist-scope.ts new file mode 100644 index 00000000000..08a420f7932 --- /dev/null +++ b/examples/angular/ssr-persist/src/app/query-persist-scope.ts @@ -0,0 +1,5 @@ +/** + * Query key prefix for data that should be written to `localStorage` by the global persister. + * SSR queries (e.g. `['posts']`) use different keys and are excluded in `dehydrateOptions`. + */ +export const CLIENT_PERSIST_QUERY_ROOT = 'client-persist' as const diff --git a/examples/angular/ssr-persist/src/app/services/posts.service.ts b/examples/angular/ssr-persist/src/app/services/posts.service.ts new file mode 100644 index 00000000000..edb8e084f0e --- /dev/null +++ b/examples/angular/ssr-persist/src/app/services/posts.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@angular/core' +import { of, tap } from 'rxjs' +import { delay } from 'rxjs/operators' + +@Injectable({ + providedIn: 'root', +}) +export class PostsService { + allPosts$ = () => + of(posts).pipe( + tap(() => console.log('fetching posts')), + delay(50), + ) +} + +export interface Post { + id: number + title: string + body: string +} + +const posts: Array = [ + { + id: 1, + title: 'Render on the server', + body: 'The initial HTML is produced by Angular SSR before the browser bootstraps the app.', + }, + { + id: 2, + title: 'Hydrate on the client', + body: 'Angular reuses the rendered DOM and turns it into a live client application.', + }, + { + id: 3, + title: 'Query on both sides', + body: 'TanStack Query runs during SSR, hydrates on the client, and the persisted cache can speed up the next full load.', + }, + { + id: 3.5, + title: 'Persist after hydration', + body: 'withPersistQueryClient uses a factory so localStorage is only touched in the browser, not during server render.', + }, + { + id: 4, + title: 'No API needed', + body: 'This example uses a deterministic in-memory data source so the SSR example works without external network access.', + }, + { + id: 5, + title: 'Keep the setup small', + body: 'Only the Angular CLI SSR pieces remain: main.server.ts, server.ts and the server application config.', + }, + { + id: 6, + title: 'Match the other examples', + body: 'The rest of the app keeps the same lightweight structure as the existing Angular examples in this repo.', + }, + { + id: 7, + title: 'Persist after hydration', + body: 'withPersistQueryClient uses a factory so localStorage is only touched in the browser, not during server render.', + }, +] diff --git a/examples/angular/ssr-persist/src/index.html b/examples/angular/ssr-persist/src/index.html new file mode 100644 index 00000000000..c00f1bf3ca5 --- /dev/null +++ b/examples/angular/ssr-persist/src/index.html @@ -0,0 +1,12 @@ + + + + + TanStack Query Angular SSR + persist example + + + + + + + diff --git a/examples/angular/ssr-persist/src/main.server.ts b/examples/angular/ssr-persist/src/main.server.ts new file mode 100644 index 00000000000..1bdb237f23f --- /dev/null +++ b/examples/angular/ssr-persist/src/main.server.ts @@ -0,0 +1,11 @@ +import { + bootstrapApplication, + type BootstrapContext, +} from '@angular/platform-browser' +import { serverConfig } from './app/app.config.server' +import { SsrPersistExampleComponent } from './app/app.component' + +const bootstrap = (context: BootstrapContext) => + bootstrapApplication(SsrPersistExampleComponent, serverConfig, context) + +export default bootstrap diff --git a/examples/angular/ssr-persist/src/main.ts b/examples/angular/ssr-persist/src/main.ts new file mode 100644 index 00000000000..9c2c5ea1079 --- /dev/null +++ b/examples/angular/ssr-persist/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser' +import { appConfig } from './app/app.config' +import { SsrPersistExampleComponent } from './app/app.component' + +bootstrapApplication(SsrPersistExampleComponent, appConfig).catch((err) => + console.error(err), +) diff --git a/examples/angular/ssr-persist/src/server.ts b/examples/angular/ssr-persist/src/server.ts new file mode 100644 index 00000000000..db670c6eb0d --- /dev/null +++ b/examples/angular/ssr-persist/src/server.ts @@ -0,0 +1,43 @@ +import { + AngularNodeAppEngine, + createNodeRequestHandler, + isMainModule, + writeResponseToNodeResponse, +} from '@angular/ssr/node' +import express from 'express' +import { join } from 'node:path' + +const browserDistFolder = join(import.meta.dirname, '../browser') + +const app = express() +const angularApp = new AngularNodeAppEngine() + +app.use( + express.static(browserDistFolder, { + maxAge: '1y', + index: false, + redirect: false, + }), +) + +app.use((req, res, next) => { + angularApp + .handle(req) + .then((response) => + response ? writeResponseToNodeResponse(response, res) : next(), + ) + .catch(next) +}) + +if (isMainModule(import.meta.url)) { + const port = process.env['PORT'] || 4000 + app.listen(port, (error) => { + if (error) { + throw error + } + + console.log(`Node Express server listening on http://localhost:${port}`) + }) +} + +export const reqHandler = createNodeRequestHandler(app) diff --git a/examples/angular/ssr-persist/src/styles.css b/examples/angular/ssr-persist/src/styles.css new file mode 100644 index 00000000000..6718c269c49 --- /dev/null +++ b/examples/angular/ssr-persist/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0; + font-family: + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; + background: #faf8f2; + color: #1b1f23; +} + +button { + font: inherit; +} diff --git a/examples/angular/ssr-persist/tsconfig.app.json b/examples/angular/ssr-persist/tsconfig.app.json new file mode 100644 index 00000000000..00e9b764198 --- /dev/null +++ b/examples/angular/ssr-persist/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/examples/angular/ssr-persist/tsconfig.json b/examples/angular/ssr-persist/tsconfig.json new file mode 100644 index 00000000000..5aa05d2d231 --- /dev/null +++ b/examples/angular/ssr-persist/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/examples/angular/ssr/.devcontainer/devcontainer.json b/examples/angular/ssr/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..365adf8f4c3 --- /dev/null +++ b/examples/angular/ssr/.devcontainer/devcontainer.json @@ -0,0 +1,4 @@ +{ + "name": "Node.js", + "image": "mcr.microsoft.com/devcontainers/javascript-node:22" +} diff --git a/examples/angular/ssr/.eslintrc.cjs b/examples/angular/ssr/.eslintrc.cjs new file mode 100644 index 00000000000..cca134ce166 --- /dev/null +++ b/examples/angular/ssr/.eslintrc.cjs @@ -0,0 +1,6 @@ +// @ts-check + +/** @type {import('eslint').Linter.Config} */ +const config = {} + +module.exports = config diff --git a/examples/angular/ssr/README.md b/examples/angular/ssr/README.md new file mode 100644 index 00000000000..b15e6f599d1 --- /dev/null +++ b/examples/angular/ssr/README.md @@ -0,0 +1,6 @@ +# TanStack Query Angular SSR example + +To run this example: + +- `npm install` or `yarn` or `pnpm i` or `bun i` +- `npm run start` or `yarn start` or `pnpm start` or `bun start` diff --git a/examples/angular/ssr/angular.json b/examples/angular/ssr/angular.json new file mode 100644 index 00000000000..1e393174cf2 --- /dev/null +++ b/examples/angular/ssr/angular.json @@ -0,0 +1,140 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { + "packageManager": "pnpm", + "analytics": false, + "cache": { + "enabled": false + } + }, + "newProjectRoot": "projects", + "projects": { + "ssr": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "inlineTemplate": true, + "inlineStyle": true, + "skipTests": true + }, + "@schematics/angular:class": { + "skipTests": true + }, + "@schematics/angular:directive": { + "skipTests": true + }, + "@schematics/angular:guard": { + "skipTests": true + }, + "@schematics/angular:interceptor": { + "skipTests": true + }, + "@schematics/angular:pipe": { + "skipTests": true + }, + "@schematics/angular:resolver": { + "skipTests": true + }, + "@schematics/angular:service": { + "skipTests": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/ssr", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "styles": ["src/styles.css"], + "scripts": [], + "server": "src/main.server.ts", + "outputMode": "server", + "security": { + "allowedHosts": ["localhost", "127.0.0.1"] + }, + "ssr": { + "entry": "src/server.ts" + } + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "prebundle": { "exclude": ["@angular"] } + }, + "configurations": { + "production": { + "buildTarget": "ssr:build:production" + }, + "development": { + "buildTarget": "ssr:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n", + "options": { + "buildTarget": "ssr:build" + } + } + } + } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } + } +} diff --git a/examples/angular/ssr/package.json b/examples/angular/ssr/package.json new file mode 100644 index 00000000000..167ba73635b --- /dev/null +++ b/examples/angular/ssr/package.json @@ -0,0 +1,33 @@ +{ + "name": "@tanstack/query-example-angular-ssr", + "type": "module", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "serve:ssr": "node dist/ssr/server/server.mjs" + }, + "private": true, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/platform-server": "^22.0.0", + "@angular/ssr": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "express": "^5.1.0", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "zone.js": "0.16.1" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@types/express": "^5.0.1", + "typescript": "6.0.3" + } +} diff --git a/examples/angular/ssr/src/app/app.component.ts b/examples/angular/ssr/src/app/app.component.ts new file mode 100644 index 00000000000..1f17c32569c --- /dev/null +++ b/examples/angular/ssr/src/app/app.component.ts @@ -0,0 +1,57 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core' +import { PostsComponent } from './components/posts.component' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'ssr-example', + imports: [PostsComponent], + template: ` +
+
+

Angular SSR

+

Server-rendered posts with TanStack Query

+

+ The first render comes from the server. Angular then hydrates the app + on the client and TanStack Query continues from a fresh client cache. +

+
+ + +
+ `, + styles: ` + .page { + max-width: 960px; + margin: 0 auto; + padding: 48px 20px 72px; + } + + .hero { + margin-bottom: 32px; + } + + .eyebrow { + margin: 0 0 8px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.12em; + text-transform: uppercase; + color: #9b5c00; + } + + h1 { + margin: 0 0 12px; + font-size: clamp(2.2rem, 5vw, 4rem); + line-height: 1; + } + + .lede { + max-width: 640px; + margin: 0; + font-size: 1rem; + line-height: 1.6; + color: #4b5563; + } + `, +}) +export class SsrExampleComponent {} diff --git a/examples/angular/ssr/src/app/app.config.server.ts b/examples/angular/ssr/src/app/app.config.server.ts new file mode 100644 index 00000000000..a1a957f51c4 --- /dev/null +++ b/examples/angular/ssr/src/app/app.config.server.ts @@ -0,0 +1,8 @@ +import { mergeApplicationConfig } from '@angular/core' +import { provideServerRendering, withRoutes } from '@angular/ssr' +import { appConfig } from './app.config' +import { serverRoutes } from './app.routes.server' + +export const serverConfig = mergeApplicationConfig(appConfig, { + providers: [provideServerRendering(withRoutes(serverRoutes))], +}) diff --git a/examples/angular/ssr/src/app/app.config.ts b/examples/angular/ssr/src/app/app.config.ts new file mode 100644 index 00000000000..05322a559a5 --- /dev/null +++ b/examples/angular/ssr/src/app/app.config.ts @@ -0,0 +1,17 @@ +import { provideHttpClient, withFetch } from '@angular/common/http' +import type { ApplicationConfig } from '@angular/core' +import { + provideClientHydration, + withEventReplay, +} from '@angular/platform-browser' +import { provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' +import { createQueryClient } from './query-client' + +export const appConfig: ApplicationConfig = { + providers: [ + provideHttpClient(withFetch()), + provideClientHydration(withEventReplay()), + provideTanStackQuery(createQueryClient, withDevtools()), + ], +} diff --git a/examples/angular/ssr/src/app/app.routes.server.ts b/examples/angular/ssr/src/app/app.routes.server.ts new file mode 100644 index 00000000000..09fcfa3cc95 --- /dev/null +++ b/examples/angular/ssr/src/app/app.routes.server.ts @@ -0,0 +1,9 @@ +import { RenderMode } from '@angular/ssr' +import type { ServerRoute } from '@angular/ssr' + +export const serverRoutes: Array = [ + { + path: '**', + renderMode: RenderMode.Server, + }, +] diff --git a/examples/angular/ssr/src/app/components/posts.component.ts b/examples/angular/ssr/src/app/components/posts.component.ts new file mode 100644 index 00000000000..e9b13754d57 --- /dev/null +++ b/examples/angular/ssr/src/app/components/posts.component.ts @@ -0,0 +1,70 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core' +import { injectQuery } from '@tanstack/angular-query' +import { lastValueFrom } from 'rxjs' +import { PostsService } from '../services/posts.service' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'posts', + template: ` + @if (postsQuery.isPending()) { +

Loading posts...

+ } @else if (postsQuery.isError()) { +

Failed to load posts.

+ } @else { +
+ @for (post of postsQuery.data(); track post.id) { +
+

Post #{{ post.id }}

+

{{ post.title }}

+

{{ post.body }}

+
+ } +
+ } + `, + styles: ` + .grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + } + + .card { + padding: 20px; + border: 1px solid #eadfcb; + border-radius: 18px; + background: #fffdf8; + box-shadow: 0 12px 30px rgba(123, 88, 31, 0.08); + } + + .meta { + margin: 0 0 10px; + font-size: 12px; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: #8b5e34; + } + + h2 { + margin: 0 0 10px; + font-size: 1.1rem; + line-height: 1.3; + } + + p { + margin: 0; + line-height: 1.5; + color: #374151; + } + `, +}) +export class PostsComponent { + readonly #postsService = inject(PostsService) + + readonly postsQuery = injectQuery(() => ({ + queryKey: ['posts'], + queryFn: () => lastValueFrom(this.#postsService.allPosts$()), + })) +} diff --git a/examples/angular/ssr/src/app/query-client.ts b/examples/angular/ssr/src/app/query-client.ts new file mode 100644 index 00000000000..16bf58ab9ca --- /dev/null +++ b/examples/angular/ssr/src/app/query-client.ts @@ -0,0 +1,19 @@ +import { isPlatformServer } from '@angular/common' +import { PLATFORM_ID, inject } from '@angular/core' +import { QueryClient } from '@tanstack/angular-query' + +export const SHARED_QUERY_DEFAULTS = { + staleTime: 1000 * 30, + gcTime: 1000 * 60 * 60 * 24, +} as const + +export function createQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + ...SHARED_QUERY_DEFAULTS, + ...(isPlatformServer(inject(PLATFORM_ID)) ? { retry: false } : {}), + }, + }, + }) +} diff --git a/examples/angular/ssr/src/app/services/posts.service.ts b/examples/angular/ssr/src/app/services/posts.service.ts new file mode 100644 index 00000000000..a650707f686 --- /dev/null +++ b/examples/angular/ssr/src/app/services/posts.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core' +import { of, tap } from 'rxjs' +import { delay } from 'rxjs/operators' + +@Injectable({ + providedIn: 'root', +}) +export class PostsService { + allPosts$ = () => + of(posts).pipe( + tap(() => console.log('fetching posts')), + delay(50), + ) +} + +export interface Post { + id: number + title: string + body: string +} + +const posts: Array = [ + { + id: 1, + title: 'Render on the server', + body: 'The initial HTML is produced by Angular SSR before the browser bootstraps the app.', + }, + { + id: 2, + title: 'Hydrate on the client', + body: 'Angular reuses the rendered DOM and turns it into a live client application.', + }, + { + id: 3, + title: 'Query on both sides', + body: 'TanStack Query resolves during server rendering, then transfers the hydrated cache to the browser.', + }, + { + id: 4, + title: 'No API needed', + body: 'This example uses a deterministic in-memory data source so the SSR example works without external network access.', + }, + { + id: 5, + title: 'Keep the setup small', + body: 'Only the Angular CLI SSR pieces remain: main.server.ts, server.ts and the server application config.', + }, + { + id: 6, + title: 'Match the other examples', + body: 'The rest of the app keeps the same lightweight structure as the existing Angular examples in this repo.', + }, +] diff --git a/examples/angular/ssr/src/index.html b/examples/angular/ssr/src/index.html new file mode 100644 index 00000000000..ab7606ebbf6 --- /dev/null +++ b/examples/angular/ssr/src/index.html @@ -0,0 +1,12 @@ + + + + + TanStack Query Angular SSR Example + + + + + + + diff --git a/examples/angular/ssr/src/main.server.ts b/examples/angular/ssr/src/main.server.ts new file mode 100644 index 00000000000..df44a7c47b7 --- /dev/null +++ b/examples/angular/ssr/src/main.server.ts @@ -0,0 +1,11 @@ +import { + bootstrapApplication, + type BootstrapContext, +} from '@angular/platform-browser' +import { serverConfig } from './app/app.config.server' +import { SsrExampleComponent } from './app/app.component' + +const bootstrap = (context: BootstrapContext) => + bootstrapApplication(SsrExampleComponent, serverConfig, context) + +export default bootstrap diff --git a/examples/angular/ssr/src/main.ts b/examples/angular/ssr/src/main.ts new file mode 100644 index 00000000000..f63fdacda8d --- /dev/null +++ b/examples/angular/ssr/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser' +import { appConfig } from './app/app.config' +import { SsrExampleComponent } from './app/app.component' + +bootstrapApplication(SsrExampleComponent, appConfig).catch((err) => + console.error(err), +) diff --git a/examples/angular/ssr/src/server.ts b/examples/angular/ssr/src/server.ts new file mode 100644 index 00000000000..db670c6eb0d --- /dev/null +++ b/examples/angular/ssr/src/server.ts @@ -0,0 +1,43 @@ +import { + AngularNodeAppEngine, + createNodeRequestHandler, + isMainModule, + writeResponseToNodeResponse, +} from '@angular/ssr/node' +import express from 'express' +import { join } from 'node:path' + +const browserDistFolder = join(import.meta.dirname, '../browser') + +const app = express() +const angularApp = new AngularNodeAppEngine() + +app.use( + express.static(browserDistFolder, { + maxAge: '1y', + index: false, + redirect: false, + }), +) + +app.use((req, res, next) => { + angularApp + .handle(req) + .then((response) => + response ? writeResponseToNodeResponse(response, res) : next(), + ) + .catch(next) +}) + +if (isMainModule(import.meta.url)) { + const port = process.env['PORT'] || 4000 + app.listen(port, (error) => { + if (error) { + throw error + } + + console.log(`Node Express server listening on http://localhost:${port}`) + }) +} + +export const reqHandler = createNodeRequestHandler(app) diff --git a/examples/angular/ssr/src/styles.css b/examples/angular/ssr/src/styles.css new file mode 100644 index 00000000000..6718c269c49 --- /dev/null +++ b/examples/angular/ssr/src/styles.css @@ -0,0 +1,16 @@ +body { + margin: 0; + font-family: + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; + background: #faf8f2; + color: #1b1f23; +} + +button { + font: inherit; +} diff --git a/examples/angular/ssr/tsconfig.app.json b/examples/angular/ssr/tsconfig.app.json new file mode 100644 index 00000000000..00e9b764198 --- /dev/null +++ b/examples/angular/ssr/tsconfig.app.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/examples/angular/ssr/tsconfig.json b/examples/angular/ssr/tsconfig.json new file mode 100644 index 00000000000..5aa05d2d231 --- /dev/null +++ b/examples/angular/ssr/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + } + ] +} diff --git a/examples/angular/to-resource-signal-forms/README.md b/examples/angular/to-resource-signal-forms/README.md new file mode 100644 index 00000000000..e6c8b844c2f --- /dev/null +++ b/examples/angular/to-resource-signal-forms/README.md @@ -0,0 +1,21 @@ +# TanStack Query Angular `toResource` with Signal Forms example + +This example uses Angular Signal Forms' `validateAsync` with a Resource created +from `injectQuery` and `toResource`. The username check is cached by its query +key, debounced while typing, and cancelled when the value changes. The example +also adds an invite-code field dynamically, giving it its own async validator, +and includes a button that calls Signal Forms' `reloadValidation()` API. The +invite-code validator is applied with `applyEach`, so its Resource factory is +created only when an invite-code item is added to the form array. Query keys and +request options live in `ValidationQueriesService`; the validators spread those +options and add `enabled` based on the current field value. +Submitting the form runs a mock `injectMutation`, disables the submit button while it is pending, +and displays the variables passed to the mutation. + +This example intentionally uses Angular 22 because Signal Forms' `validateAsync` +API is stable starting in Angular 22. + +To run this example: + +- `pnpm install` +- `pnpm start` diff --git a/examples/angular/to-resource-signal-forms/angular.json b/examples/angular/to-resource-signal-forms/angular.json new file mode 100644 index 00000000000..8b7a5fde388 --- /dev/null +++ b/examples/angular/to-resource-signal-forms/angular.json @@ -0,0 +1,58 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "cli": { "packageManager": "pnpm", "analytics": false }, + "projects": { + "to-resource-signal-forms": { + "projectType": "application", + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "outputPath": "dist/to-resource-signal-forms", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "assets": [], + "styles": ["src/styles.css"] + }, + "configurations": { + "production": { "outputHashing": "all" }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "options": { + "prebundle": { + "exclude": [ + "@angular/common", + "@angular/core", + "@angular/forms", + "@angular/platform-browser" + ] + } + }, + "configurations": { + "production": { + "buildTarget": "to-resource-signal-forms:build:production" + }, + "development": { + "buildTarget": "to-resource-signal-forms:build:development" + } + }, + "defaultConfiguration": "development" + } + } + } + } +} diff --git a/examples/angular/to-resource-signal-forms/package.json b/examples/angular/to-resource-signal-forms/package.json new file mode 100644 index 00000000000..eed160329fb --- /dev/null +++ b/examples/angular/to-resource-signal-forms/package.json @@ -0,0 +1,29 @@ +{ + "name": "@tanstack/query-example-angular-to-resource-signal-forms", + "private": true, + "type": "module", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development" + }, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query-devtools": "workspace:*", + "@tanstack/angular-query": "workspace:*", + "rxjs": "^7.8.2", + "tslib": "^2.8.1", + "zone.js": "0.16.1" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" + } +} diff --git a/examples/angular/to-resource-signal-forms/src/app/app.component.css b/examples/angular/to-resource-signal-forms/src/app/app.component.css new file mode 100644 index 00000000000..665ce6ad670 --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/app/app.component.css @@ -0,0 +1,75 @@ +:host { + display: block; +} + +main { + margin: 3rem auto; + max-width: 42rem; + padding: 0 1rem; +} + +form, +label, +.invite-code-row { + display: grid; + gap: 0.5rem; +} + +label { + max-width: 22rem; +} + +input, +button { + font: inherit; +} + +input { + padding: 0.55rem 0.7rem; +} + +button { + padding: 0.275rem 0.7rem; +} + +button { + justify-self: start; +} + +.hint { + color: #5f6368; + font-size: 0.9rem; +} + +.checking { + margin: 0; + padding: 0.2rem 0.45rem; + color: #805b00; +} + +.error { + margin: 0; + padding: 0.2rem 0.45rem; + color: #b42318; +} + +.success { + margin: 0; + padding: 0.2rem 0.45rem; + color: #067647; +} + +.invite-code-row { + grid-template-columns: minmax(0, 1fr) auto; + align-items: end; + border-left: 3px solid #d0d5dd; + padding-left: 0.75rem; +} + +.invite-code-row > button { + margin-bottom: 0; +} + +.invite-code-row > p { + grid-column: 1 / -1; +} diff --git a/examples/angular/to-resource-signal-forms/src/app/app.component.ts b/examples/angular/to-resource-signal-forms/src/app/app.component.ts new file mode 100644 index 00000000000..b4dfd78390e --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/app/app.component.ts @@ -0,0 +1,249 @@ +import { + ChangeDetectionStrategy, + Component, + inject, + signal, +} from '@angular/core' +import { + FormField, + applyEach, + form, + minLength, + required, + submit as submitForm, + validateAsync, +} from '@angular/forms/signals' +import { + injectMutation, + injectQuery, + toResource, +} from '@tanstack/angular-query' +import { ValidationQueriesService } from './validation-queries.service' + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-root', + imports: [FormField], + styleUrl: './app.component.css', + template: ` +
+

Username availability

+

+ Signal Forms uses a Resource backed by TanStack Query for async + validation. Try taken or any other username. +

+ +
+ + + @if (registrationForm.username().pending()) { +

Checking availability...

+ } + + @if ( + registrationForm.username().touched() && + registrationForm.username().invalid() + ) { + @for (error of registrationForm.username().errors(); track $index) { +

{{ error.message }}

+ } + } + + @if ( + registrationForm.username().touched() && + registrationForm.username().valid() + ) { +

Username is available.

+ } + +

+ Invite-code validators are added with each array item. The query + resource does not exist until an invite code is added. +

+ + + + @for ( + inviteCode of registrationForm.inviteCodes; + track $index; + let index = $index + ) { +
+ + + + + @if (inviteCode().pending()) { +

Checking invite code...

+ } + + @if (inviteCode().touched() && inviteCode().invalid()) { + @for (error of inviteCode().errors(); track $index) { +

{{ error.message }}

+ } + } + + @if (inviteCode().touched() && inviteCode().valid()) { +

Invite code is valid.

+ } +
+ } + + + + + + @if (registrationMutation.isError()) { +

Could not submit the registration.

+ } + + @if ( + registrationMutation.isSuccess() && registrationMutation.data().ok + ) { +

+ Registration accepted for + {{ registrationMutation.variables().username }}. +

+

+ Mutation variables included + {{ registrationMutation.variables().inviteCodes.length }} + invite code(s). +

+ } +
+
+ `, +}) +export class AppComponent { + private readonly queries = inject(ValidationQueriesService) + readonly model = signal({ username: '', inviteCodes: [] as Array }) + readonly registrationMutation = injectMutation(() => + this.queries.submitRegistration(), + ) + + readonly registrationForm = form(this.model, (path) => { + required(path.username, { message: 'Username is required' }) + minLength(path.username, 3, { + message: 'Username must be at least 3 characters', + }) + + validateAsync(path.username, { + debounce: 300, + params: ({ value }) => value().trim() || undefined, + factory: (username) => { + const query = injectQuery(() => { + const name = username() ?? '' + return { + ...this.queries.usernameAvailability(name), + enabled: !!name, + } + }) + + return toResource(query) + }, + onSuccess: (result) => + result.available + ? null + : { + kind: 'usernameTaken', + message: 'Username is already taken', + }, + onError: () => ({ + kind: 'usernameUnavailable', + message: 'Could not check username availability', + }), + }) + + applyEach(path.inviteCodes, (inviteCode) => { + required(inviteCode, { message: 'Invite code is required' }) + minLength(inviteCode, 3, { + message: 'Invite code must be at least 3 characters', + }) + + validateAsync(inviteCode, { + debounce: 300, + params: ({ value }) => value().trim() || undefined, + factory: (code) => { + const query = injectQuery(() => { + const value = code() ?? '' + return { + ...this.queries.inviteCodeValidation(value), + enabled: !!value, + } + }) + + return toResource(query) + }, + onSuccess: (result) => + result.valid + ? null + : { + kind: 'invalidInviteCode', + message: 'Invite code must start with TEAM-', + }, + onError: () => ({ + kind: 'inviteCodeUnavailable', + message: 'Could not check the invite code', + }), + }) + }) + }) + + addInviteCode() { + this.model.update((model) => ({ + ...model, + inviteCodes: [...model.inviteCodes, ''], + })) + } + + removeInviteCode(index: number) { + this.model.update((model) => ({ + ...model, + inviteCodes: model.inviteCodes.filter( + (_, itemIndex) => itemIndex !== index, + ), + })) + } + + async submit(event: SubmitEvent) { + event.preventDefault() + + const success = await submitForm(this.registrationForm, { + ignoreValidators: 'none', + action: async (field) => { + const result = await this.registrationMutation.mutateAsync( + field().value(), + ) + + if (!result.ok) { + return result.errors.map((error) => ({ + fieldTree: + error.fieldPath[0] === 'username' + ? this.registrationForm.username + : this.registrationForm.inviteCodes[error.fieldPath[1]], + kind: 'server', + message: error.error, + })) + } + + return undefined + }, + }) + if (success) { + this.model.set({ username: '', inviteCodes: [] }) + } + } +} diff --git a/examples/angular/to-resource-signal-forms/src/app/app.config.ts b/examples/angular/to-resource-signal-forms/src/app/app.config.ts new file mode 100644 index 00000000000..0038688124f --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/app/app.config.ts @@ -0,0 +1,7 @@ +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' +import type { ApplicationConfig } from '@angular/core' + +export const appConfig: ApplicationConfig = { + providers: [provideTanStackQuery(() => new QueryClient(), withDevtools())], +} diff --git a/examples/angular/to-resource-signal-forms/src/app/validation-queries.service.ts b/examples/angular/to-resource-signal-forms/src/app/validation-queries.service.ts new file mode 100644 index 00000000000..85d5529e666 --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/app/validation-queries.service.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core' +import { mutationOptions, queryOptions } from '@tanstack/angular-query' + +export type UsernameValidation = { + available: boolean +} + +export type InviteCodeValidation = { + valid: boolean +} + +export type RegistrationData = { + username: string + inviteCodes: Array +} + +export type RegistrationMutationResult = + | { ok: true } + | { + ok: false + errors: Array<{ + fieldPath: ['username'] | ['inviteCodes', number] + error: string + }> + } + +@Injectable({ providedIn: 'root' }) +export class ValidationQueriesService { + usernameAvailability(username: string) { + return queryOptions({ + queryKey: ['username-availability', username], + gcTime: 15_000, + queryFn: async (): Promise => { + await this.delay() + return { available: username.toLowerCase() !== 'taken' } + }, + }) + } + + inviteCodeValidation(code: string) { + return queryOptions({ + queryKey: ['invite-code-validation', code], + gcTime: 15_000, + staleTime: 30_000, + queryFn: async (): Promise => { + await this.delay() + return { valid: code.toUpperCase().startsWith('TEAM-') } + }, + }) + } + + submitRegistration() { + return mutationOptions({ + gcTime: 20_000, + mutationFn: async ( + variables: RegistrationData, + ): Promise => { + await this.delay() + + const errors: Array<{ + fieldPath: ['username'] | ['inviteCodes', number] + error: string + }> = [] + + if (variables.username.toLowerCase().includes('error')) { + errors.push({ + fieldPath: ['username'], + error: 'This username cannot be registered', + }) + } + + variables.inviteCodes.forEach((inviteCode, index) => { + if (inviteCode.toLowerCase().includes('error')) { + errors.push({ + fieldPath: ['inviteCodes', index], + error: 'This invite code cannot be registered', + }) + } + }) + + return errors.length > 0 ? { ok: false, errors } : { ok: true } + }, + }) + } + + private delay() { + return new Promise((resolve) => setTimeout(resolve, 450)) + } +} diff --git a/examples/angular/to-resource-signal-forms/src/index.html b/examples/angular/to-resource-signal-forms/src/index.html new file mode 100644 index 00000000000..fac15051cbe --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/index.html @@ -0,0 +1,12 @@ + + + + + toResource with Signal Forms + + + + + + + diff --git a/examples/angular/to-resource-signal-forms/src/main.ts b/examples/angular/to-resource-signal-forms/src/main.ts new file mode 100644 index 00000000000..ef15666eff7 --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/main.ts @@ -0,0 +1,7 @@ +import { bootstrapApplication } from '@angular/platform-browser' +import { appConfig } from './app/app.config' +import { AppComponent } from './app/app.component' + +bootstrapApplication(AppComponent, appConfig).catch((error) => + console.error(error), +) diff --git a/examples/angular/to-resource-signal-forms/src/styles.css b/examples/angular/to-resource-signal-forms/src/styles.css new file mode 100644 index 00000000000..be398679351 --- /dev/null +++ b/examples/angular/to-resource-signal-forms/src/styles.css @@ -0,0 +1,8 @@ +body { + background: #f7f7f8; + color: #202124; + font: + 16px/1.5 system-ui, + sans-serif; + margin: 0; +} diff --git a/examples/angular/to-resource-signal-forms/tsconfig.app.json b/examples/angular/to-resource-signal-forms/tsconfig.app.json new file mode 100644 index 00000000000..a7da28e8cea --- /dev/null +++ b/examples/angular/to-resource-signal-forms/tsconfig.app.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/examples/angular/to-resource-signal-forms/tsconfig.json b/examples/angular/to-resource-signal-forms/tsconfig.json new file mode 100644 index 00000000000..f42b458e4ac --- /dev/null +++ b/examples/angular/to-resource-signal-forms/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "moduleResolution": "bundler", + "importHelpers": true, + "target": "ES2022", + "module": "preserve", + "lib": ["ES2022", "dom"] + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/integrations/angular-cli-20/package.json b/integrations/angular-cli-20/package.json deleted file mode 100644 index 3c900750972..00000000000 --- a/integrations/angular-cli-20/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "angular-cli-20", - "private": true, - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "watch": "ng build --watch --configuration development" - }, - "dependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/forms": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@angular/router": "^20.0.0", - "@tanstack/angular-query-experimental": "^5.103.1", - "rxjs": "~7.8.0", - "tslib": "^2.3.0", - "zone.js": "~0.15.0" - }, - "devDependencies": { - "@angular/build": "^20.0.0", - "@angular/cli": "^20.0.0", - "@angular/compiler-cli": "^20.0.0", - "typescript": "~5.8.2" - } -} diff --git a/integrations/angular-cli-20/.gitignore b/integrations/angular-cli/.gitignore similarity index 100% rename from integrations/angular-cli-20/.gitignore rename to integrations/angular-cli/.gitignore diff --git a/integrations/angular-cli-20/.vscode/extensions.json b/integrations/angular-cli/.vscode/extensions.json similarity index 100% rename from integrations/angular-cli-20/.vscode/extensions.json rename to integrations/angular-cli/.vscode/extensions.json diff --git a/integrations/angular-cli-20/.vscode/launch.json b/integrations/angular-cli/.vscode/launch.json similarity index 100% rename from integrations/angular-cli-20/.vscode/launch.json rename to integrations/angular-cli/.vscode/launch.json diff --git a/integrations/angular-cli-20/.vscode/tasks.json b/integrations/angular-cli/.vscode/tasks.json similarity index 100% rename from integrations/angular-cli-20/.vscode/tasks.json rename to integrations/angular-cli/.vscode/tasks.json diff --git a/integrations/angular-cli-20/README.md b/integrations/angular-cli/README.md similarity index 96% rename from integrations/angular-cli-20/README.md rename to integrations/angular-cli/README.md index e2977417aad..a09dcb7a448 100644 --- a/integrations/angular-cli-20/README.md +++ b/integrations/angular-cli/README.md @@ -1,6 +1,6 @@ -# AngularCli20 +# Angular CLI -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.0.0-rc.2. +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 22. ## Development server diff --git a/integrations/angular-cli-20/angular.json b/integrations/angular-cli/angular.json similarity index 94% rename from integrations/angular-cli-20/angular.json rename to integrations/angular-cli/angular.json index ec9adeb50f6..9efece00d0b 100644 --- a/integrations/angular-cli-20/angular.json +++ b/integrations/angular-cli/angular.json @@ -10,7 +10,7 @@ }, "newProjectRoot": "projects", "projects": { - "angular-cli-20": { + "angular-cli": { "projectType": "application", "schematics": { "@schematics/angular:component": { @@ -85,10 +85,10 @@ "builder": "@angular/build:dev-server", "configurations": { "production": { - "buildTarget": "angular-cli-20:build:production" + "buildTarget": "angular-cli:build:production" }, "development": { - "buildTarget": "angular-cli-20:build:development" + "buildTarget": "angular-cli:build:development" } }, "defaultConfiguration": "development" diff --git a/integrations/angular-cli/package.json b/integrations/angular-cli/package.json new file mode 100644 index 00000000000..671b9adc74f --- /dev/null +++ b/integrations/angular-cli/package.json @@ -0,0 +1,29 @@ +{ + "name": "angular-cli", + "private": true, + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development" + }, + "dependencies": { + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/forms": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "@tanstack/angular-query": "workspace:*", + "@tanstack/angular-query-devtools": "workspace:*", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.16.0" + }, + "devDependencies": { + "@angular/build": "^22.0.0", + "@angular/cli": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "typescript": "6.0.3" + } +} diff --git a/integrations/angular-cli/public/favicon.ico b/integrations/angular-cli/public/favicon.ico new file mode 100644 index 00000000000..57614f9c967 Binary files /dev/null and b/integrations/angular-cli/public/favicon.ico differ diff --git a/integrations/angular-cli-20/src/app/app.config.ts b/integrations/angular-cli/src/app/app.config.ts similarity index 56% rename from integrations/angular-cli-20/src/app/app.config.ts rename to integrations/angular-cli/src/app/app.config.ts index 8383c690fd7..9e46b692246 100644 --- a/integrations/angular-cli-20/src/app/app.config.ts +++ b/integrations/angular-cli/src/app/app.config.ts @@ -3,15 +3,13 @@ import { provideBrowserGlobalErrorListeners, provideZonelessChangeDetection, } from '@angular/core' -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideZonelessChangeDetection(), - provideTanStackQuery(new QueryClient()), + provideTanStackQuery(() => new QueryClient(), withDevtools()), ], } diff --git a/integrations/angular-cli-20/src/app/app.ts b/integrations/angular-cli/src/app/app.ts similarity index 88% rename from integrations/angular-cli-20/src/app/app.ts rename to integrations/angular-cli/src/app/app.ts index a0f0a34ba44..b918333c774 100644 --- a/integrations/angular-cli-20/src/app/app.ts +++ b/integrations/angular-cli/src/app/app.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core' -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' @Component({ selector: 'app-root', diff --git a/integrations/angular-cli-20/src/index.html b/integrations/angular-cli/src/index.html similarity index 90% rename from integrations/angular-cli-20/src/index.html rename to integrations/angular-cli/src/index.html index 94f551d17bd..0928db7c056 100644 --- a/integrations/angular-cli-20/src/index.html +++ b/integrations/angular-cli/src/index.html @@ -2,7 +2,7 @@ - AngularCli20 + Angular CLI diff --git a/integrations/angular-cli-20/src/main.ts b/integrations/angular-cli/src/main.ts similarity index 100% rename from integrations/angular-cli-20/src/main.ts rename to integrations/angular-cli/src/main.ts diff --git a/integrations/angular-cli-20/src/styles.css b/integrations/angular-cli/src/styles.css similarity index 100% rename from integrations/angular-cli-20/src/styles.css rename to integrations/angular-cli/src/styles.css diff --git a/integrations/angular-cli-20/tsconfig.app.json b/integrations/angular-cli/tsconfig.app.json similarity index 100% rename from integrations/angular-cli-20/tsconfig.app.json rename to integrations/angular-cli/tsconfig.app.json diff --git a/integrations/angular-cli-20/tsconfig.json b/integrations/angular-cli/tsconfig.json similarity index 100% rename from integrations/angular-cli-20/tsconfig.json rename to integrations/angular-cli/tsconfig.json diff --git a/packages/angular-query-devtools/README.md b/packages/angular-query-devtools/README.md new file mode 100644 index 00000000000..1981576c796 --- /dev/null +++ b/packages/angular-query-devtools/README.md @@ -0,0 +1,23 @@ +# @tanstack/angular-query-devtools + +Developer tools for `@tanstack/angular-query`. + +```sh +pnpm add -D @tanstack/angular-query-devtools +``` + +```ts +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withDevtools } from '@tanstack/angular-query-devtools' + +export const appConfig: ApplicationConfig = { + providers: [provideTanStackQuery(() => new QueryClient(), withDevtools())], +} +``` + +The default import resolves to a stub in production builds. To opt in to the +real implementation in production, import `withDevtools` from +`@tanstack/angular-query-devtools/production`. + +The options callback runs inside an Angular injection context, so it can call +`inject()` and read signals directly. diff --git a/packages/angular-query-devtools/package.json b/packages/angular-query-devtools/package.json new file mode 100644 index 00000000000..7055d656b93 --- /dev/null +++ b/packages/angular-query-devtools/package.json @@ -0,0 +1,113 @@ +{ + "name": "@tanstack/angular-query-devtools", + "version": "4.0.0", + "description": "Developer tools to interact with and visualize the TanStack Angular Query cache", + "author": "Arnoud de Vries", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/TanStack/query.git", + "directory": "packages/angular-query-devtools" + }, + "homepage": "https://tanstack.com/query", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "angular", + "angular query", + "devtools", + "tanstack" + ], + "scripts": { + "clean": "premove ./dist ./coverage ./dist-ts", + "compile": "tsc --build", + "test:eslint": "eslint --concurrency=auto ./src", + "test:types": "npm-run-all --serial test:types:*", + "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build", + "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build", + "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build", + "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js --build", + "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js --build", + "test:types:tscurrent": "tsc --build", + "test:types:ts60": "node ../../node_modules/typescript60/lib/tsc.js --build", + "test:lib": "vitest", + "test:lib:dev": "pnpm run test:lib --watch", + "test:build": "trap 'premove ./dist/*.tgz' EXIT; pnpm pack && publint ./dist/*.tgz --strict && attw ./dist/*.tgz --ignore-rules cjs-resolves-to-esm", + "build": "vite build", + "prepack": "node scripts/prepack.js" + }, + "type": "module", + "types": "dist/index.d.ts", + "module": "dist/index.mjs", + "exports": { + ".": { + "@tanstack/custom-condition": "./src/index.ts", + "types": "./dist/index.d.ts", + "development": "./dist/index.mjs", + "default": "./dist/stub.mjs" + }, + "./package.json": "./package.json", + "./stub": { + "types": "./dist/stub.d.ts", + "default": "./dist/stub.mjs" + }, + "./production": { + "types": "./dist/production/index.d.ts", + "default": "./dist/index.mjs" + }, + "./devtools-panel": { + "@tanstack/custom-condition": "./src/devtools-panel/index.ts", + "types": "./dist/devtools-panel/index.d.ts", + "development": "./dist/devtools-panel/index.mjs", + "default": "./dist/devtools-panel/stub.mjs" + }, + "./devtools-panel/production": { + "types": "./dist/devtools-panel/production/index.d.ts", + "default": "./dist/devtools-panel/index.mjs" + }, + "./devtools-panel/stub": { + "types": "./dist/devtools-panel/stub.d.ts", + "default": "./dist/devtools-panel/stub.mjs" + } + }, + "sideEffects": false, + "files": [ + "**/*.d.ts", + "**/*.mjs", + "**/*.mjs.map" + ], + "dependencies": { + "@tanstack/query-core": "workspace:*", + "@tanstack/query-devtools": "workspace:*" + }, + "devDependencies": { + "@analogjs/vite-plugin-angular": "^2.3.1", + "@angular/build": "^22.0.0", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@tanstack/angular-query": "workspace:*", + "@testing-library/jest-dom": "^6.8.0", + "npm-run-all2": "^5.0.0", + "rxjs": "^7.8.2", + "typescript": "5.9.3", + "vite-plugin-dts": "4.2.3", + "vite-plugin-externalize-deps": "^0.9.0", + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^4.0.18", + "zone.js": "^0.16.0" + }, + "peerDependencies": { + "@angular/common": ">=22.0.0", + "@angular/core": ">=22.0.0", + "@tanstack/angular-query": "workspace:^" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": false + } +} diff --git a/packages/angular-query-experimental/scripts/prepack.js b/packages/angular-query-devtools/scripts/prepack.js similarity index 89% rename from packages/angular-query-experimental/scripts/prepack.js rename to packages/angular-query-devtools/scripts/prepack.js index bf4bef0cc75..b01eaa65a0d 100644 --- a/packages/angular-query-experimental/scripts/prepack.js +++ b/packages/angular-query-devtools/scripts/prepack.js @@ -50,14 +50,16 @@ function replaceDist(filePath) { */ function processExports(exports) { return Object.fromEntries( - Object.entries(exports).map(([key, value]) => [ - key, - typeof value === 'string' - ? replaceDist(value) - : typeof value === 'object' && value !== null - ? processExports(value) - : value, - ]), + Object.entries(exports) + .filter(([key]) => key !== '@tanstack/custom-condition') + .map(([key, value]) => [ + key, + typeof value === 'string' + ? replaceDist(value) + : typeof value === 'object' && value !== null + ? processExports(value) + : value, + ]), ) } diff --git a/packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts b/packages/angular-query-devtools/src/__tests__/inject-devtools-panel.test.ts similarity index 66% rename from packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts rename to packages/angular-query-devtools/src/__tests__/inject-devtools-panel.test.ts index 3a1fc4bca89..35ec2ab7dae 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts +++ b/packages/angular-query-devtools/src/__tests__/inject-devtools-panel.test.ts @@ -1,13 +1,9 @@ -import { - ElementRef, - provideZonelessChangeDetection, - signal, -} from '@angular/core' +import { ApplicationRef, ElementRef, signal } from '@angular/core' import { TestBed } from '@angular/core/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { QueryClient } from '@tanstack/query-core' -import { provideTanStackQuery } from '../providers' import { injectDevtoolsPanel } from '../devtools-panel' +import { setupTanStackQueryTestBed } from './test-utils' const mockDevtoolsPanelInstance = { mount: vi.fn(), @@ -45,12 +41,8 @@ describe('injectDevtoolsPanel', () => { vi.clearAllMocks() queryClient = new QueryClient() mockElementRef = new ElementRef(document.createElement('div')) - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - { provide: ElementRef, useValue: signal(mockElementRef) }, - ], + setupTanStackQueryTestBed(queryClient, { + providers: [{ provide: ElementRef, useValue: signal(mockElementRef) }], }) }) @@ -77,7 +69,7 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() @@ -91,17 +83,53 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() + + await waitForDevtoolsToBeCreated() + + result.destroy() + + expect(mockDevtoolsPanelInstance.unmount).toHaveBeenCalledTimes(1) + }) + it('should stop reacting after it is manually destroyed', async () => { + const hostElement = signal(mockElementRef) + const result = TestBed.runInInjectionContext(() => + injectDevtoolsPanel(() => ({ + hostElement: hostElement(), + })), + ) + + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() result.destroy() + hostElement.set(undefined) + hostElement.set(mockElementRef) + await TestBed.inject(ApplicationRef).whenStable() + expect(mocks.mockTanstackQueryDevtoolsPanel).toHaveBeenCalledTimes(1) + expect(mockDevtoolsPanelInstance.mount).toHaveBeenCalledTimes(1) expect(mockDevtoolsPanelInstance.unmount).toHaveBeenCalledTimes(1) }) + it('should not mount when destroyed while the devtools import is pending', async () => { + const result = TestBed.runInInjectionContext(() => + injectDevtoolsPanel(() => ({ + hostElement: mockElementRef, + })), + ) + + TestBed.tick() + result.destroy() + await TestBed.inject(ApplicationRef).whenStable() + + expect(mocks.mockTanstackQueryDevtoolsPanel).not.toHaveBeenCalled() + expect(mockDevtoolsPanelInstance.mount).not.toHaveBeenCalled() + }) + it('should destroy TanstackQueryDevtoolsPanel when hostElement is removed', async () => { - const hostElement = signal(mockElementRef) + const hostElement = signal(mockElementRef) TestBed.runInInjectionContext(() => { return injectDevtoolsPanel(() => ({ @@ -109,15 +137,15 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() expect(mockDevtoolsPanelInstance.unmount).toHaveBeenCalledTimes(0) - hostElement.set(null as unknown as ElementRef) + hostElement.set(null) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsPanelInstance.unmount).toHaveBeenCalledTimes(1) }) @@ -132,7 +160,7 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() @@ -140,7 +168,7 @@ describe('injectDevtoolsPanel', () => { client.set(new QueryClient()) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsPanelInstance.setClient).toHaveBeenCalledTimes(1) }) @@ -155,7 +183,7 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() @@ -163,7 +191,7 @@ describe('injectDevtoolsPanel', () => { errorTypes.set([]) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsPanelInstance.setErrorTypes).toHaveBeenCalledTimes(1) }) @@ -181,7 +209,7 @@ describe('injectDevtoolsPanel', () => { })) }) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() await waitForDevtoolsToBeCreated() @@ -189,7 +217,7 @@ describe('injectDevtoolsPanel', () => { onClose.set(functionB) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsPanelInstance.setOnClose).toHaveBeenCalledTimes(1) }) diff --git a/packages/angular-query-devtools/src/__tests__/test-utils.ts b/packages/angular-query-devtools/src/__tests__/test-utils.ts new file mode 100644 index 00000000000..88e2296713a --- /dev/null +++ b/packages/angular-query-devtools/src/__tests__/test-utils.ts @@ -0,0 +1,19 @@ +import { provideZonelessChangeDetection } from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { provideTanStackQuery } from '@tanstack/angular-query' +import type { QueryClient } from '@tanstack/query-core' +import type { EnvironmentProviders, Provider } from '@angular/core' + +export function setupTanStackQueryTestBed( + queryClient: QueryClient, + options: { providers?: Array } = {}, +) { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideZonelessChangeDetection(), + provideTanStackQuery(() => queryClient), + ...(options.providers ?? []), + ], + }) +} diff --git a/packages/angular-query-experimental/src/__tests__/with-devtools.test.ts b/packages/angular-query-devtools/src/__tests__/with-devtools.test.ts similarity index 68% rename from packages/angular-query-experimental/src/__tests__/with-devtools.test.ts rename to packages/angular-query-devtools/src/__tests__/with-devtools.test.ts index 47ce1173440..264c1a1752e 100644 --- a/packages/angular-query-experimental/src/__tests__/with-devtools.test.ts +++ b/packages/angular-query-devtools/src/__tests__/with-devtools.test.ts @@ -2,41 +2,47 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { QueryClient } from '@tanstack/query-core' import { TestBed } from '@angular/core/testing' import { + ApplicationRef, ENVIRONMENT_INITIALIZER, EnvironmentInjector, InjectionToken, PLATFORM_ID, createEnvironmentInjector, + inject, isDevMode, provideZonelessChangeDetection, signal, } from '@angular/core' -import { provideTanStackQuery } from '../providers' -import { withDevtools } from '../devtools' +import { provideTanStackQuery } from '@tanstack/angular-query' +import { getQueryFeatureProviders } from '@tanstack/angular-query/internal' +import { withDevtools } from '../index' import type { DevtoolsButtonPosition, DevtoolsErrorType, DevtoolsPosition, Theme, } from '@tanstack/query-devtools' -import type { DevtoolsOptions } from '../devtools' - -const mockDevtoolsInstance = { - mount: vi.fn(), - unmount: vi.fn(), - setClient: vi.fn(), - setPosition: vi.fn(), - setErrorTypes: vi.fn(), - setButtonPosition: vi.fn(), - setInitialIsOpen: vi.fn(), - setTheme: vi.fn(), -} - -function MockTanstackQueryDevtools() { - return mockDevtoolsInstance -} +import type { DevtoolsOptions } from '../types' + +const { mockDevtoolsInstance, mockTanstackQueryDevtools } = vi.hoisted(() => { + const instance = { + mount: vi.fn(), + unmount: vi.fn(), + setClient: vi.fn(), + setPosition: vi.fn(), + setErrorTypes: vi.fn(), + setButtonPosition: vi.fn(), + setInitialIsOpen: vi.fn(), + setTheme: vi.fn(), + } -const mockTanstackQueryDevtools = vi.fn(MockTanstackQueryDevtools) + return { + mockDevtoolsInstance: instance, + mockTanstackQueryDevtools: vi.fn(function MockTanstackQueryDevtools() { + return instance + }), + } +}) vi.mock('@tanstack/query-devtools', () => ({ TanstackQueryDevtools: mockTanstackQueryDevtools, @@ -55,13 +61,11 @@ const mockIsDevMode = vi.mocked(isDevMode) describe('withDevtools feature', () => { beforeEach(() => { vi.clearAllMocks() - vi.useFakeTimers() }) afterEach(() => { vi.restoreAllMocks() TestBed.resetTestingModule() - vi.useRealTimers() }) it.each([ @@ -123,7 +127,7 @@ describe('withDevtools feature', () => { const providers = [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), loadDevtools !== undefined ? withDevtools( () => @@ -140,54 +144,22 @@ describe('withDevtools feature', () => { }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - TestBed.tick() - await vi.dynamicImportSettled() - TestBed.tick() - await vi.dynamicImportSettled() + expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) + + await TestBed.inject(ApplicationRef).whenStable() - expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes( - expectedCalled ? 1 : 0, - ) expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes( expectedCalled ? 1 : 0, ) }, ) - it("should throw 'No QueryClient found' when 'loadDevtools' is 'true' and no 'QueryClient' is provided", async () => { - const consoleErrorSpy = vi - .spyOn(console, 'error') - .mockImplementation(() => undefined) - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - withDevtools(() => ({ - loadDevtools: true, - })).ɵproviders, - ], - }) - - TestBed.inject(ENVIRONMENT_INITIALIZER) - TestBed.tick() - await vi.dynamicImportSettled() - - expect(mockTanstackQueryDevtools).not.toHaveBeenCalled() - expect(consoleErrorSpy).toHaveBeenCalledWith( - 'Install @tanstack/query-devtools or reinstall without --omit=optional.', - expect.objectContaining({ message: 'No QueryClient found' }), - ) - - consoleErrorSpy.mockRestore() - }) - it('should not continue loading devtools after injector is destroyed', async () => { TestBed.configureTestingModule({ providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, })), @@ -195,13 +167,15 @@ describe('withDevtools feature', () => { ], }) + const app = TestBed.inject(ApplicationRef) TestBed.inject(ENVIRONMENT_INITIALIZER) + const stable = app.whenStable() // Destroys injector TestBed.resetTestingModule() - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() + await stable - expect(mockTanstackQueryDevtools).not.toHaveBeenCalled() + expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) + expect(mockDevtoolsInstance.mount).not.toHaveBeenCalled() }) it('should not create devtools again when already provided', async () => { @@ -209,7 +183,7 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, })), @@ -218,8 +192,7 @@ describe('withDevtools feature', () => { }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) @@ -227,15 +200,17 @@ describe('withDevtools feature', () => { createEnvironmentInjector( [ - withDevtools(() => ({ - loadDevtools: true, - })).ɵproviders, + getQueryFeatureProviders( + withDevtools(() => ({ + loadDevtools: true, + })), + ), ], injector, ) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) + await TestBed.inject(ApplicationRef).whenStable() expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) }) @@ -249,7 +224,7 @@ describe('withDevtools feature', () => { }, provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, })), @@ -258,7 +233,7 @@ describe('withDevtools feature', () => { }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.runAllTimersAsync() + await TestBed.inject(ApplicationRef).whenStable() expect(mockTanstackQueryDevtools).not.toHaveBeenCalled() }) @@ -270,20 +245,17 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - errorTypes: errorTypes(), + errorTypes, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledTimes(0) @@ -295,8 +267,7 @@ describe('withDevtools feature', () => { ] errorTypes.set(newErrorTypes) - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledWith( @@ -311,27 +282,23 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - client: client(), + client, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setClient).toHaveBeenCalledTimes(0) const newClient = new QueryClient() client.set(newClient) - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setClient).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setClient).toHaveBeenCalledWith(newClient) @@ -344,26 +311,22 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - position: position(), + position, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledTimes(0) position.set('left') - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledWith('left') @@ -376,26 +339,22 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - buttonPosition: buttonPosition(), + buttonPosition, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledTimes(0) buttonPosition.set('bottom-right') - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledWith( @@ -410,26 +369,22 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - initialIsOpen: initialIsOpen(), + initialIsOpen, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledTimes(0) initialIsOpen.set(true) - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledWith(true) @@ -442,31 +397,59 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, - theme: theme(), + theme, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledTimes(0) theme.set('dark') - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledWith('dark') }) + it('should pass construction-only options to the devtools', async () => { + const shadowDOMTarget = document + .createElement('div') + .attachShadow({ mode: 'open' }) + + TestBed.configureTestingModule({ + providers: [ + provideZonelessChangeDetection(), + provideTanStackQuery( + () => new QueryClient(), + withDevtools(() => ({ + loadDevtools: true, + styleNonce: 'nonce', + shadowDOMTarget, + hideDisabledQueries: true, + })), + ), + ], + }) + + TestBed.inject(ENVIRONMENT_INITIALIZER) + await TestBed.inject(ApplicationRef).whenStable() + + expect(mockTanstackQueryDevtools).toHaveBeenCalledWith( + expect.objectContaining({ + styleNonce: 'nonce', + shadowDOMTarget, + hideDisabledQueries: true, + }), + ) + }) + it('should destroy devtools', async () => { const loadDevtools = signal(true) @@ -474,24 +457,22 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ - loadDevtools: loadDevtools(), + loadDevtools, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(0) loadDevtools.set(false) - - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1) }) @@ -501,7 +482,7 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ loadDevtools: true, })), @@ -510,9 +491,7 @@ describe('withDevtools feature', () => { }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - TestBed.tick() - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() expect(mockTanstackQueryDevtools).toHaveBeenCalled() expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1) @@ -531,107 +510,89 @@ describe('withDevtools feature', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - new QueryClient(), + () => new QueryClient(), withDevtools(() => ({ - loadDevtools: loadDevtools(), + loadDevtools, })), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) + await TestBed.inject(ApplicationRef).whenStable() - expect(mockTanstackQueryDevtools).not.toHaveBeenCalled() + expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.mount).not.toHaveBeenCalled() loadDevtools.set(true) - TestBed.tick() - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() - expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.unmount).not.toHaveBeenCalled() loadDevtools.set(false) - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1) expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1) loadDevtools.set(true) - TestBed.tick() - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() // Should remount (mount called twice now) expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(2) expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1) }) - describe('deps parameter', () => { - it('should inject dependencies and pass them to withDevtoolsFn in correct order', async () => { - const mockService1 = { value: 'service1' } - const mockService2 = { value: 'service2' } - const mockService1Token = new InjectionToken('MockService1') - const mockService2Token = new InjectionToken('MockService2') - const withDevtoolsFn = vi.fn().mockReturnValue({ loadDevtools: true }) - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - { - provide: mockService1Token, - useValue: mockService1, - }, - { - provide: mockService2Token, - useValue: mockService2, - }, - provideTanStackQuery( - new QueryClient(), - withDevtools(withDevtoolsFn, { - deps: [mockService1Token, mockService2Token], - }), - ), - ], + describe('injection context', () => { + it('should inject dependencies directly in the callback', async () => { + const firstService = { value: 'first' } + const secondService = { value: 'second' } + const firstServiceToken = new InjectionToken('FirstService') + const secondServiceToken = new InjectionToken('SecondService') + const withDevtoolsFn = vi.fn(() => { + const first = inject(firstServiceToken) + const second = inject(secondServiceToken) + + return { + loadDevtools: true, + initialIsOpen: first === firstService && second === secondService, + } }) - TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) - - expect(withDevtoolsFn).toHaveBeenCalledWith(mockService1, mockService2) - }) - - it('should work with empty deps array', async () => { - const withDevtoolsFn = vi.fn().mockReturnValue({ loadDevtools: true }) - TestBed.configureTestingModule({ providers: [ provideZonelessChangeDetection(), + { provide: firstServiceToken, useValue: firstService }, + { provide: secondServiceToken, useValue: secondService }, provideTanStackQuery( - new QueryClient(), - withDevtools(withDevtoolsFn, { - deps: [], - }), + () => new QueryClient(), + withDevtools(withDevtoolsFn), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) + await TestBed.inject(ApplicationRef).whenStable() - expect(withDevtoolsFn).toHaveBeenCalledWith() + expect(withDevtoolsFn).toHaveBeenCalled() + expect(mockTanstackQueryDevtools).toHaveBeenCalledWith( + expect.objectContaining({ initialIsOpen: true }), + ) }) - it('should reactively update when injected services change', async () => { + it('should reactively update when an injected service changes', async () => { class ReactiveService { enabled = signal(false) position = signal('bottom') } - const withDevtoolsFn = (service: ReactiveService) => ({ - loadDevtools: service.enabled(), - position: service.position(), + const withDevtoolsFn = vi.fn(() => { + const service = inject(ReactiveService) + return { + loadDevtools: service.enabled, + position: service.position, + } }) TestBed.configureTestingModule({ @@ -639,26 +600,23 @@ describe('withDevtools feature', () => { provideZonelessChangeDetection(), ReactiveService, provideTanStackQuery( - new QueryClient(), - withDevtools(withDevtoolsFn, { - deps: [ReactiveService], - }), + () => new QueryClient(), + withDevtools(withDevtoolsFn), ), ], }) TestBed.inject(ENVIRONMENT_INITIALIZER) - await vi.advanceTimersByTimeAsync(0) + await TestBed.inject(ApplicationRef).whenStable() const service = TestBed.inject(ReactiveService) - expect(mockTanstackQueryDevtools).not.toHaveBeenCalled() + expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) + expect(mockDevtoolsInstance.mount).not.toHaveBeenCalled() service.enabled.set(true) - TestBed.tick() - await vi.dynamicImportSettled() + await TestBed.inject(ApplicationRef).whenStable() - expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1) expect(mockTanstackQueryDevtools).toHaveBeenCalledWith( expect.objectContaining({ position: 'bottom', @@ -666,9 +624,10 @@ describe('withDevtools feature', () => { ) service.position.set('top') - TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledWith('top') + expect(withDevtoolsFn).toHaveBeenCalledTimes(1) }) }) }) diff --git a/packages/angular-query-experimental/src/devtools-panel/index.ts b/packages/angular-query-devtools/src/devtools-panel/index.ts similarity index 84% rename from packages/angular-query-experimental/src/devtools-panel/index.ts rename to packages/angular-query-devtools/src/devtools-panel/index.ts index 2742a154692..b5ed79fa96a 100644 --- a/packages/angular-query-experimental/src/devtools-panel/index.ts +++ b/packages/angular-query-devtools/src/devtools-panel/index.ts @@ -1,7 +1,6 @@ export type { InjectDevtoolsPanel, DevtoolsPanelOptions, - InjectDevtoolsPanelOptions, DevtoolsPanelRef, } from './types' diff --git a/packages/angular-query-devtools/src/devtools-panel/inject-devtools-panel.ts b/packages/angular-query-devtools/src/devtools-panel/inject-devtools-panel.ts new file mode 100644 index 00000000000..c8dc50b9db8 --- /dev/null +++ b/packages/angular-query-devtools/src/devtools-panel/inject-devtools-panel.ts @@ -0,0 +1,116 @@ +import { + DestroyRef, + PLATFORM_ID, + assertInInjectionContext, + computed, + effect, + inject, + untracked, +} from '@angular/core' +import { QueryClient, onlineManager } from '@tanstack/query-core' +import { isPlatformBrowser } from '@angular/common' +import type { TanstackQueryDevtoolsPanel } from '@tanstack/query-devtools' +import type { DevtoolsPanelOptions, InjectDevtoolsPanel } from './types' + +/** + * Inject a TanStack Query devtools panel and render it in the DOM. + * + * Devtools panel allows programmatic control over the devtools, for example if you want to render + * the devtools as part of your own devtools. + * + * Consider `withDevtools` instead if you don't need this. + * @param injectDevtoolsPanelFn - A function that returns devtools panel options. + * @returns DevtoolsPanelRef + * @see https://tanstack.com/query/v5/docs/framework/angular/devtools + */ +export const injectDevtoolsPanel: InjectDevtoolsPanel = ( + injectDevtoolsPanelFn: () => DevtoolsPanelOptions, +) => { + assertInInjectionContext(injectDevtoolsPanel) + const destroyRef = inject(DestroyRef) + const isBrowser = isPlatformBrowser(inject(PLATFORM_ID)) + const injectedClient = inject(QueryClient, { optional: true }) + + const queryOptions = computed(injectDevtoolsPanelFn) + let devtools: TanstackQueryDevtoolsPanel | null = null + let optionsEffect: ReturnType | undefined + let manuallyDestroyed = false + let loadGeneration = 0 + + const unmount = () => { + devtools?.unmount() + devtools = null + } + + const destroy = () => { + if (manuallyDestroyed) return + + manuallyDestroyed = true + loadGeneration++ + optionsEffect?.destroy() + unmount() + } + + if (!isBrowser) + return { + destroy, + } + + optionsEffect = effect(() => { + const generation = ++loadGeneration + const { + client = injectedClient, + errorTypes = [], + styleNonce, + shadowDOMTarget, + onClose, + hostElement, + } = queryOptions() + + untracked(() => { + if (!client) throw new Error('No QueryClient found') + if (!devtools && hostElement) { + import('@tanstack/query-devtools') + .then((queryDevtools) => { + if ( + manuallyDestroyed || + destroyRef.destroyed || + generation !== loadGeneration + ) { + return + } + + devtools = new queryDevtools.TanstackQueryDevtoolsPanel({ + client, + queryFlavor: 'Angular Query', + version: '5', + buttonPosition: 'bottom-left', + position: 'bottom', + initialIsOpen: true, + errorTypes, + styleNonce, + shadowDOMTarget, + onClose, + onlineManager, + }) + devtools.mount(hostElement.nativeElement) + }) + .catch((error) => { + console.error('Failed to load @tanstack/query-devtools.', error) + }) + } else if (devtools && hostElement) { + devtools.setClient(client) + devtools.setErrorTypes(errorTypes) + onClose && devtools.setOnClose(onClose) + } else if (devtools && !hostElement) { + unmount() + } + }) + }) + + destroyRef.onDestroy(destroy) + + return { + destroy, + } +} diff --git a/packages/angular-query-devtools/src/devtools-panel/production/index.ts b/packages/angular-query-devtools/src/devtools-panel/production/index.ts new file mode 100644 index 00000000000..90be57a57c8 --- /dev/null +++ b/packages/angular-query-devtools/src/devtools-panel/production/index.ts @@ -0,0 +1,6 @@ +export type { + DevtoolsPanelOptions, + DevtoolsPanelRef, + InjectDevtoolsPanel, +} from '../types' +export { injectDevtoolsPanel } from '../inject-devtools-panel' diff --git a/packages/angular-query-experimental/src/devtools-panel/stub.ts b/packages/angular-query-devtools/src/devtools-panel/stub.ts similarity index 51% rename from packages/angular-query-experimental/src/devtools-panel/stub.ts rename to packages/angular-query-devtools/src/devtools-panel/stub.ts index 63e836eb5c8..7da1608a6d5 100644 --- a/packages/angular-query-experimental/src/devtools-panel/stub.ts +++ b/packages/angular-query-devtools/src/devtools-panel/stub.ts @@ -1,7 +1,17 @@ import { noop } from '@tanstack/query-core' -import type { InjectDevtoolsPanel } from './types' +import type { + DevtoolsPanelOptions, + DevtoolsPanelRef, + InjectDevtoolsPanel, +} from './types' // Stub which replaces `injectDevtoolsPanel` in production builds export const injectDevtoolsPanel: InjectDevtoolsPanel = () => ({ destroy: noop, }) + +export type { + InjectDevtoolsPanel, + DevtoolsPanelOptions, + DevtoolsPanelRef, +} diff --git a/packages/angular-query-experimental/src/devtools-panel/types.ts b/packages/angular-query-devtools/src/devtools-panel/types.ts similarity index 77% rename from packages/angular-query-experimental/src/devtools-panel/types.ts rename to packages/angular-query-devtools/src/devtools-panel/types.ts index f91974ee403..0de859d7152 100644 --- a/packages/angular-query-experimental/src/devtools-panel/types.ts +++ b/packages/angular-query-devtools/src/devtools-panel/types.ts @@ -1,16 +1,7 @@ import type { DevtoolsErrorType } from '@tanstack/query-devtools' -import type { ElementRef, Injector } from '@angular/core' +import type { ElementRef } from '@angular/core' import type { QueryClient } from '@tanstack/query-core' -export interface InjectDevtoolsPanelOptions { - /** - * The `Injector` in which to create the devtools panel. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - /** * A devtools panel, which can be manually destroyed. */ @@ -42,16 +33,15 @@ export interface DevtoolsPanelOptions { /** * Callback function that is called when the devtools panel is closed */ - onClose?: () => void + onClose?: () => unknown /** * Element where to render the devtools panel. When set to undefined or null, the devtools panel will not be created, or destroyed if existing. * If changed from undefined to a ElementRef, the devtools panel will be created. */ - hostElement?: ElementRef + hostElement?: ElementRef | null } export type InjectDevtoolsPanel = ( injectDevtoolsPanelFn: () => DevtoolsPanelOptions, - options?: InjectDevtoolsPanelOptions, ) => DevtoolsPanelRef diff --git a/packages/angular-query-devtools/src/index.ts b/packages/angular-query-devtools/src/index.ts new file mode 100644 index 00000000000..bf0f9e7b156 --- /dev/null +++ b/packages/angular-query-devtools/src/index.ts @@ -0,0 +1,3 @@ +export type { DevtoolsOptions, WithDevtools, WithDevtoolsFn } from './types' + +export { withDevtools } from './with-devtools' diff --git a/packages/angular-query-devtools/src/production/index.ts b/packages/angular-query-devtools/src/production/index.ts new file mode 100644 index 00000000000..002179bb9b2 --- /dev/null +++ b/packages/angular-query-devtools/src/production/index.ts @@ -0,0 +1,2 @@ +export type { DevtoolsOptions, WithDevtools, WithDevtoolsFn } from '../types' +export { withDevtools } from '../with-devtools' diff --git a/packages/angular-query-devtools/src/stub.ts b/packages/angular-query-devtools/src/stub.ts new file mode 100644 index 00000000000..da5031e3814 --- /dev/null +++ b/packages/angular-query-devtools/src/stub.ts @@ -0,0 +1,9 @@ +import { makeEnvironmentProviders } from '@angular/core' +import { queryFeature } from '@tanstack/angular-query/internal' +import type { DevtoolsOptions, WithDevtools, WithDevtoolsFn } from './types' + +// Stub which replaces `withDevtools` in production builds +export const withDevtools: WithDevtools = () => + queryFeature(makeEnvironmentProviders([])) + +export type { DevtoolsOptions, WithDevtools, WithDevtoolsFn } diff --git a/packages/angular-query-devtools/src/types.ts b/packages/angular-query-devtools/src/types.ts new file mode 100644 index 00000000000..5aff112b33d --- /dev/null +++ b/packages/angular-query-devtools/src/types.ts @@ -0,0 +1,92 @@ +import type { QueryClient } from '@tanstack/query-core' +import type { + DevtoolsButtonPosition, + DevtoolsErrorType, + DevtoolsPosition, + Theme, +} from '@tanstack/query-devtools' +import type { QueryFeature } from '@tanstack/angular-query' +import type { Signal } from '@angular/core' + +/** A static devtools option or an Angular signal containing that option. */ +type MaybeSignal = T | Signal + +/** + * Options for configuring the TanStack Query devtools. + */ +export interface DevtoolsOptions { + /** + * Set this true if you want the devtools to default to being open + */ + initialIsOpen?: MaybeSignal + /** + * The position of the TanStack logo to open and close the devtools panel. + * `top-left` | `top-right` | `bottom-left` | `bottom-right` | `relative` + * Defaults to `bottom-right`. + */ + buttonPosition?: MaybeSignal + /** + * The position of the TanStack Query devtools panel. + * `top` | `bottom` | `left` | `right` + * Defaults to `bottom`. + */ + position?: MaybeSignal + /** + * Custom instance of QueryClient + */ + client?: MaybeSignal + /** + * Use this so you can define custom errors that can be shown in the devtools. + */ + errorTypes?: MaybeSignal> + /** + * Use this to pass a nonce to the style tag that is added to the document head. This is useful if you are using a Content Security Policy (CSP) nonce to allow inline styles. + */ + styleNonce?: string + /** + * Use this so you can attach the devtool's styles to a specific element in the DOM. + */ + shadowDOMTarget?: ShadowRoot + /** + * Set this to true to hide disabled queries from the devtools panel. + */ + hideDisabledQueries?: boolean + /** + * Set this to 'light', 'dark', or 'system' to change the theme of the devtools panel. + * Defaults to 'system'. + */ + theme?: MaybeSignal + + /** + * Whether the developer tools should be rendered. + * - `auto`- (Default) Renders devtools in development mode. Skips rendering in production mode. + * - `true`- Always render the devtools, regardless of the environment. + * - `false`- Never render the devtools, regardless of the environment. + * + * You can use `true` and `false` to override loading developer tools from an environment file. + * For example, a test environment might run in production mode but you may want to load developer tools. + * + * You can pass a signal to dynamically render the devtools based on a + * condition. For example, the signal could be created from an RxJS + * observable that listens for a keyboard shortcut. + * + * **Example** + * ```ts + * withDevtools(() => ({ + * initialIsOpen: true, + * loadDevtools: inject(ExampleService).loadDevtools, + * })) + * ``` + */ + loadDevtools?: MaybeSignal<'auto' | boolean> +} + +/** + * Returns devtools options. The function runs once in an Angular injection + * context, so it can call `inject()`. Pass signals as option values to make + * mutable options reactive. `styleNonce`, `shadowDOMTarget`, and + * `hideDisabledQueries` are read only when the devtools are constructed. + */ +export type WithDevtoolsFn = () => DevtoolsOptions + +export type WithDevtools = (withDevtoolsFn?: WithDevtoolsFn) => QueryFeature diff --git a/packages/angular-query-devtools/src/with-devtools.ts b/packages/angular-query-devtools/src/with-devtools.ts new file mode 100644 index 00000000000..7614b5be269 --- /dev/null +++ b/packages/angular-query-devtools/src/with-devtools.ts @@ -0,0 +1,172 @@ +import { isPlatformBrowser } from '@angular/common' +import { + DestroyRef, + InjectionToken, + PLATFORM_ID, + afterNextRender, + afterRenderEffect, + inject, + isDevMode, + isSignal, + makeEnvironmentProviders, + provideEnvironmentInitializer, +} from '@angular/core' +import { queryFeature } from '@tanstack/angular-query/internal' +import { QueryClient, onlineManager } from '@tanstack/query-core' +import { TanstackQueryDevtools } from '@tanstack/query-devtools' +import type { Signal } from '@angular/core' +import type { DevtoolsOptions, WithDevtools } from './types' + +/** + * Internal token used to prevent double providing of devtools in child injectors + */ +const DEVTOOLS_PROVIDED = new InjectionToken('', { + factory: () => ({ + isProvided: false, + }), +}) + +/** Internal token for providing devtools options. */ +const DEVTOOLS_OPTIONS = new InjectionToken('') + +function resolveOption(option: T | Signal | undefined) { + return isSignal(option) ? option() : option +} + +/** + * Enables developer tools in Angular development builds. + * + * **Example** + * + * ```ts + * export const appConfig: ApplicationConfig = { + * providers: [ + * provideTanStackQuery(() => new QueryClient(), withDevtools()), + * ] + * } + * ``` + * The devtools will be rendered in ``. + * + * If you need more control over when devtools are loaded, you can use the `loadDevtools` option. + * + * If you need more control over where devtools are rendered, consider `injectDevtoolsPanel`. This allows rendering devtools inside your own devtools for example. + * @param withDevtoolsFn - A function that returns `DevtoolsOptions`. It runs + * once in an Angular injection context and can call `inject()`. Return signals + * as individual mutable option values to update them reactively. + * @returns A set of providers for use with `provideTanStackQuery`. + * @see {@link provideTanStackQuery} + * @see {@link DevtoolsOptions} + */ +export const withDevtools: WithDevtools = (withDevtoolsFn) => + queryFeature( + makeEnvironmentProviders([ + { + provide: DEVTOOLS_OPTIONS, + useFactory: () => withDevtoolsFn?.() ?? {}, + }, + provideEnvironmentInitializer(() => { + const devtoolsProvided = inject(DEVTOOLS_PROVIDED) + if ( + !isPlatformBrowser(inject(PLATFORM_ID)) || + devtoolsProvided.isProvided + ) + return + + devtoolsProvided.isProvided = true + + const destroyRef = inject(DestroyRef) + const injectedClient = inject(QueryClient, { optional: true }) + const options = inject(DEVTOOLS_OPTIONS) + const client = resolveOption(options.client) ?? injectedClient + + if (!client) throw new Error('No QueryClient found') + + const devtools = new TanstackQueryDevtools({ + client, + queryFlavor: 'Angular Query', + version: '5', + onlineManager, + buttonPosition: resolveOption(options.buttonPosition), + position: resolveOption(options.position), + initialIsOpen: resolveOption(options.initialIsOpen), + errorTypes: resolveOption(options.errorTypes), + styleNonce: options.styleNonce, + shadowDOMTarget: options.shadowDOMTarget, + hideDisabledQueries: options.hideDisabledQueries, + theme: resolveOption(options.theme), + }) + + let renderCompleted = false + let element: HTMLElement | null = null + + const shouldMount = () => { + const loadDevtools = resolveOption(options.loadDevtools) + return typeof loadDevtools === 'boolean' ? loadDevtools : isDevMode() + } + + const mount = () => { + if (element || !renderCompleted || !shouldMount()) return + + element = document.body.appendChild(document.createElement('div')) + element.classList.add('tsqd-parent-container') + devtools.mount(element) + } + + const unmount = () => { + if (!element) return + + devtools.unmount() + element.remove() + element = null + } + + afterNextRender({ + write: () => { + if (destroyRef.destroyed) return + + renderCompleted = true + mount() + destroyRef.onDestroy(unmount) + }, + }) + + afterRenderEffect({ + write: () => { + if (!renderCompleted) return + shouldMount() ? mount() : unmount() + }, + }) + + const registerOptionEffect = ( + option: T | Signal | undefined, + update: (value: T | undefined) => void, + ) => { + if (!isSignal(option)) return + afterRenderEffect({ + write: () => { + update(option()) + }, + }) + } + + registerOptionEffect(options.client, (value) => { + devtools.setClient(value ?? injectedClient!) + }) + registerOptionEffect(options.buttonPosition, (value) => { + devtools.setButtonPosition(value ?? 'bottom-right') + }) + registerOptionEffect(options.position, (value) => { + devtools.setPosition(value ?? 'bottom') + }) + registerOptionEffect(options.initialIsOpen, (value) => { + devtools.setInitialIsOpen(value ?? false) + }) + registerOptionEffect(options.errorTypes, (value) => { + devtools.setErrorTypes(value ?? []) + }) + registerOptionEffect(options.theme, (value) => { + devtools.setTheme(value) + }) + }), + ]), + ) diff --git a/packages/angular-query-experimental/test-setup.ts b/packages/angular-query-devtools/test-setup.ts similarity index 91% rename from packages/angular-query-experimental/test-setup.ts rename to packages/angular-query-devtools/test-setup.ts index cb8519a824c..eb69f3f05a9 100644 --- a/packages/angular-query-experimental/test-setup.ts +++ b/packages/angular-query-devtools/test-setup.ts @@ -1,4 +1,5 @@ import '@testing-library/jest-dom/vitest' +import '@angular/compiler' import { getTestBed } from '@angular/core/testing' import { BrowserTestingModule, diff --git a/packages/angular-query-devtools/tsconfig.json b/packages/angular-query-devtools/tsconfig.json new file mode 100644 index 00000000000..bec09d17d91 --- /dev/null +++ b/packages/angular-query-devtools/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist-ts", + "rootDir": ".", + "noFallthroughCasesInSwitch": true, + "useDefineForClassFields": false, + "target": "ES2022" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictStandalone": true, + "strictTemplates": true + }, + "include": ["src", "scripts", "test-setup.ts", "*.config.*", "package.json"], + "references": [ + { "path": "../angular-query" }, + { "path": "../query-core" }, + { "path": "../query-devtools" } + ] +} diff --git a/packages/angular-query-devtools/tsconfig.prod.json b/packages/angular-query-devtools/tsconfig.prod.json new file mode 100644 index 00000000000..b470042ddc1 --- /dev/null +++ b/packages/angular-query-devtools/tsconfig.prod.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "incremental": false, + "composite": false, + "rootDir": "../../", + "customConditions": null + } +} diff --git a/packages/angular-query-devtools/tsconfig.spec.json b/packages/angular-query-devtools/tsconfig.spec.json new file mode 100644 index 00000000000..b15a2a37c7b --- /dev/null +++ b/packages/angular-query-devtools/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["vitest/globals", "node"], + "noEmit": false, + "emitDeclarationOnly": false, + "declaration": false, + "declarationMap": false, + "composite": false, + "target": "ES2022" + }, + "files": ["test-setup.ts"], + "include": ["src/**/*.test.ts", "src/**/*.test-d.ts"], + "references": [{ "path": "./tsconfig.prod.json" }] +} diff --git a/packages/angular-query-devtools/vite.config.ts b/packages/angular-query-devtools/vite.config.ts new file mode 100644 index 00000000000..cac0f34027f --- /dev/null +++ b/packages/angular-query-devtools/vite.config.ts @@ -0,0 +1,126 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import { defineConfig, mergeConfig } from 'vite' +import { externalizeDeps } from 'vite-plugin-externalize-deps' +import tsconfigPaths from 'vite-tsconfig-paths' +import dts from 'vite-plugin-dts' +import type { Options } from '@tanstack/vite-config' + +const packageDir = path.dirname(fileURLToPath(import.meta.url)) +const angularQueryEntry = path.join( + packageDir, + '../angular-query/dist/index.mjs', +) +const angularQueryInternalEntry = path.join( + packageDir, + '../angular-query/dist/internal.mjs', +) +const queryDevtoolsEntry = path.join( + packageDir, + '../query-devtools/src/index.ts', +) + +function ensureImportFileExtension({ + content, + extension, +}: { + content: string + extension: string +}) { + content = content.replace( + /(im|ex)port\s[\w{}/*\s,]+from\s['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm, + `$&.${extension}`, + ) + + content = content.replace( + /import\(['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm, + `$&.${extension}`, + ) + return content +} + +const config = defineConfig({ + resolve: { + conditions: ['@tanstack/custom-condition'], + ...(process.env.VITEST === 'true' + ? { + alias: { + '@tanstack/angular-query/internal': angularQueryInternalEntry, + '@tanstack/angular-query': angularQueryEntry, + '@tanstack/query-devtools': queryDevtoolsEntry, + }, + } + : {}), + }, +}) + +export const tanstackViteConfig = (options: Options) => { + const outDir = options.outDir ?? 'dist' + const cjs = options.cjs ?? true + + return defineConfig({ + plugins: [ + externalizeDeps({ include: options.externalDeps ?? [] }), + tsconfigPaths({ + projects: options.tsconfigPath ? [options.tsconfigPath] : undefined, + }), + dts({ + outDir, + entryRoot: options.srcDir, + include: options.srcDir, + exclude: options.exclude, + tsconfigPath: options.tsconfigPath, + compilerOptions: { + module: 99, // ESNext + declarationMap: false, + }, + beforeWriteFile: (filePath, content) => { + return { + filePath, + content: ensureImportFileExtension({ content, extension: 'js' }), + } + }, + afterDiagnostic: (diagnostics) => { + if (diagnostics.length > 0) { + console.error('Please fix the above type errors') + process.exit(1) + } + }, + }), + ], + build: { + outDir, + minify: false, + sourcemap: true, + lib: { + entry: options.entry, + formats: cjs ? ['es', 'cjs'] : ['es'], + fileName: () => '[name].mjs', + }, + rollupOptions: { + output: { + preserveModules: true, + preserveModulesRoot: 'src', + }, + }, + }, + }) +} + +export default mergeConfig( + config, + tanstackViteConfig({ + cjs: false, + entry: [ + './src/index.ts', + './src/stub.ts', + './src/production/index.ts', + './src/devtools-panel/index.ts', + './src/devtools-panel/stub.ts', + './src/devtools-panel/production/index.ts', + ], + exclude: ['src/__tests__'], + srcDir: './src', + tsconfigPath: 'tsconfig.prod.json', + }), +) diff --git a/packages/angular-query-devtools/vitest.config.ts b/packages/angular-query-devtools/vitest.config.ts new file mode 100644 index 00000000000..7f5ed075a45 --- /dev/null +++ b/packages/angular-query-devtools/vitest.config.ts @@ -0,0 +1,43 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' +import angular from '@analogjs/vite-plugin-angular' +import { defineConfig } from 'vitest/config' +import packageJson from './package.json' with { type: 'json' } + +const packageDir = path.dirname(fileURLToPath(import.meta.url)) +const angularQueryEntry = path.join( + packageDir, + '../angular-query/dist/index.mjs', +) +const angularQueryInternalEntry = path.join( + packageDir, + '../angular-query/dist/internal.mjs', +) + +export default defineConfig({ + esbuild: { + target: 'es2022', + }, + plugins: [angular({ tsconfig: './tsconfig.spec.json' })], + resolve: { + alias: { + '@tanstack/angular-query/internal': angularQueryInternalEntry, + '@tanstack/angular-query': angularQueryEntry, + '@tanstack/query-devtools': path.join( + packageDir, + '../query-devtools/src/index.ts', + ), + }, + }, + test: { + name: packageJson.name, + dir: './src', + watch: false, + environment: 'jsdom', + setupFiles: ['./test-setup.ts'], + coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] }, + include: ['**/*.{test,spec}.{ts,mts,cts,tsx,js,mjs,cjs,jsx}'], + globals: true, + restoreMocks: true, + }, +}) diff --git a/packages/angular-query-experimental/CHANGELOG.md b/packages/angular-query-experimental/CHANGELOG.md deleted file mode 100644 index db38797a5f1..00000000000 --- a/packages/angular-query-experimental/CHANGELOG.md +++ /dev/null @@ -1,492 +0,0 @@ -# @tanstack/angular-query-experimental - -## 5.103.1 - -### Patch Changes - -- Updated dependencies [[`8330b2f`](https://github.com/TanStack/query/commit/8330b2f2d431f226d7bb95eb727c637be9d4c6d8), [`3212966`](https://github.com/TanStack/query/commit/3212966a3681e02d14eb79e9cce0d3265b046877)]: - - @tanstack/query-core@5.103.1 - -## 5.103.0 - -### Patch Changes - -- Updated dependencies [[`e57f816`](https://github.com/TanStack/query/commit/e57f8163b18ab3abfe52ad69cdbdda4d31453393), [`fdae2ce`](https://github.com/TanStack/query/commit/fdae2ce4e5382af7326c7c38ce5d5a12751ada12), [`a1119e5`](https://github.com/TanStack/query/commit/a1119e5a3ffa52534de7390f17c7183d17658051), [`cbf77bf`](https://github.com/TanStack/query/commit/cbf77bf8e1a9ce09caef0cdd9a8ed10fef57edf0)]: - - @tanstack/query-core@5.103.0 - -## 5.102.8 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.102.8 - -## 5.102.7 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.102.7 - -## 5.102.6 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.102.6 - -## 5.102.5 - -### Patch Changes - -- Updated dependencies [[`578e5c2`](https://github.com/TanStack/query/commit/578e5c26e8ebd0d7351b4b8e2bafba695e672b8d)]: - - @tanstack/query-core@5.102.5 - -## 5.102.4 - -### Patch Changes - -- Updated dependencies [[`a05df6a`](https://github.com/TanStack/query/commit/a05df6aefb0e2489ec2c879ae16e2ee7cb3123ec)]: - - @tanstack/query-core@5.102.4 - -## 5.102.3 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.102.3 - -## 5.102.2 - -### Patch Changes - -- Updated dependencies [[`80fbf73`](https://github.com/TanStack/query/commit/80fbf73e77892d702c107e14a84c219a8ed825dc)]: - - @tanstack/query-core@5.102.2 - -## 5.102.1 - -### Patch Changes - -- Updated dependencies [[`134890d`](https://github.com/TanStack/query/commit/134890dbdc60e4fb0313e44b512d29284ca82f96)]: - - @tanstack/query-core@5.102.1 - -## 5.102.0 - -### Patch Changes - -- [#11224](https://github.com/TanStack/query/pull/11224) [`294d4e6`](https://github.com/TanStack/query/commit/294d4e62c4f7d674574a4903eef2a1bc3dd1413b) - Fix `queryOptions` and `infiniteQueryOptions` return types so exported inferred options can be emitted in declaration files without leaking internal data tag symbols. - -- [#8737](https://github.com/TanStack/query/pull/8737) [`2215bb0`](https://github.com/TanStack/query/commit/2215bb031139cdc8a84751b37a485c38ca9d2b6e) - fix: make mutation variables optional when `undefined extends TVariables` - -- [#11221](https://github.com/TanStack/query/pull/11221) [`1ef4208`](https://github.com/TanStack/query/commit/1ef42087c9a266c2137d2ef645cbcc662f60ac93) - Remove experimental render-time prefetching and the `promise` property from query results. - -- Updated dependencies [[`34f7cee`](https://github.com/TanStack/query/commit/34f7ceed09c10e4a3aa2df31a106ddf02ec4e787), [`b4368c4`](https://github.com/TanStack/query/commit/b4368c43792349f6c29d1fb41f7ee1ef3a8bdd2c), [`5bb089d`](https://github.com/TanStack/query/commit/5bb089d153d33933983b7ea80f8d69b51d423698), [`ba4650c`](https://github.com/TanStack/query/commit/ba4650c05e61e33c609e051b948c9f7d31ce70d1), [`294d4e6`](https://github.com/TanStack/query/commit/294d4e62c4f7d674574a4903eef2a1bc3dd1413b), [`1f631b3`](https://github.com/TanStack/query/commit/1f631b3604aab6567fb3f6c90646a5a304641546), [`01a02bf`](https://github.com/TanStack/query/commit/01a02bfad32f9efbc17796be31d6399f1197a655), [`18c1c1e`](https://github.com/TanStack/query/commit/18c1c1ef94883e781ad36d36f9ea4a043ce4260b), [`5448063`](https://github.com/TanStack/query/commit/5448063f828d2270dccd41ae375e1afde35e91f2), [`2215bb0`](https://github.com/TanStack/query/commit/2215bb031139cdc8a84751b37a485c38ca9d2b6e), [`1ef4208`](https://github.com/TanStack/query/commit/1ef42087c9a266c2137d2ef645cbcc662f60ac93), [`5981771`](https://github.com/TanStack/query/commit/5981771abec9330b344a2617543eb05ba7c99e24), [`4a9bef6`](https://github.com/TanStack/query/commit/4a9bef6cf19b1cd6b014032d696129f12a848185), [`bef4bc7`](https://github.com/TanStack/query/commit/bef4bc780ce7cca32b7e3dea85f77d92f82a62a2), [`9656dc4`](https://github.com/TanStack/query/commit/9656dc4e5fef5f8c502579b52459f9e8c72e787c), [`326aaf1`](https://github.com/TanStack/query/commit/326aaf1333e5d9cbc46569c53f218d31684162d2), [`3e83601`](https://github.com/TanStack/query/commit/3e836010032c2586e9f1c66a271fa9114a6401f9), [`c6fc17c`](https://github.com/TanStack/query/commit/c6fc17cfad6e452261c585fabfb8c210e60531ed)]: - - @tanstack/query-core@5.102.0 - -## 5.101.4 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.101.4 - -## 5.101.3 - -### Patch Changes - -- Updated dependencies [[`7e3c822`](https://github.com/TanStack/query/commit/7e3c822a10896f41a8f1031c16b85096277af677)]: - - @tanstack/query-core@5.101.3 - -## 5.101.2 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.101.2 - -## 5.101.1 - -### Patch Changes - -- Updated dependencies [[`9eff92e`](https://github.com/TanStack/query/commit/9eff92ed86e284ec0125b3a3539d028688235bd1)]: - - @tanstack/query-core@5.101.1 - -## 5.101.0 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.101.0 - -## 5.100.14 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.14 - -## 5.100.13 - -### Patch Changes - -- Updated dependencies [[`d423168`](https://github.com/TanStack/query/commit/d423168f6261a5cb3d353e53b27c8150cc271151)]: - - @tanstack/query-core@5.100.13 - -## 5.100.12 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.12 - -## 5.100.11 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.11 - -## 5.100.10 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.10 - -## 5.100.9 - -### Patch Changes - -- Add theme option support to Angular floating devtools. ([#10609](https://github.com/TanStack/query/pull/10609)) - -- Updated dependencies [[`fcee7bd`](https://github.com/TanStack/query/commit/fcee7bdc429385ae8ffa224fa8a7a9ec7b8ee380)]: - - @tanstack/query-core@5.100.9 - -## 5.100.8 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.8 - -## 5.100.7 - -### Patch Changes - -- docs(devtools): align logo, panel, and 'buttonPosition' union descriptions across docs and JSDoc ([#10617](https://github.com/TanStack/query/pull/10617)) - -- Updated dependencies []: - - @tanstack/query-core@5.100.7 - -## 5.100.6 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.100.6 - -## 5.100.5 - -### Patch Changes - -- Updated dependencies [[`a53ef97`](https://github.com/TanStack/query/commit/a53ef97f87decb8ea2431710f5199431d3c94c8d)]: - - @tanstack/query-core@5.100.5 - -## 5.100.4 - -### Patch Changes - -- fix(devtools): change onClose callback type from () => unknown to () => void ([#10118](https://github.com/TanStack/query/pull/10118)) - -- Updated dependencies []: - - @tanstack/query-core@5.100.4 - -## 5.100.3 - -### Patch Changes - -- Updated dependencies [[`f85d825`](https://github.com/TanStack/query/commit/f85d825e02efbbff02e2081528ed28f5e5382f7a)]: - - @tanstack/query-core@5.100.3 - -## 5.100.2 - -### Patch Changes - -- Updated dependencies [[`ea4497e`](https://github.com/TanStack/query/commit/ea4497e8aa00d8c1c3a36fb1e17563a889d6ab31), [`d6a7bf3`](https://github.com/TanStack/query/commit/d6a7bf3e3e024c1a77d0536813238cc8007a5fa7), [`645d5d1`](https://github.com/TanStack/query/commit/645d5d130f5e8017cb1bf1a37987f7b980aed705)]: - - @tanstack/query-core@5.100.2 - -## 5.100.1 - -### Patch Changes - -- Updated dependencies [[`1bb0d23`](https://github.com/TanStack/query/commit/1bb0d234280fd4ae1725c439088426a20593a8df)]: - - @tanstack/query-core@5.100.1 - -## 5.100.0 - -### Patch Changes - -- Updated dependencies [[`6540a41`](https://github.com/TanStack/query/commit/6540a4126b1c087d86d64525e78f32d9920dcd31)]: - - @tanstack/query-core@5.100.0 - -## 5.99.2 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.99.2 - -## 5.99.1 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.99.1 - -## 5.99.0 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.99.0 - -## 5.98.0 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.98.0 - -## 5.97.0 - -### Patch Changes - -- Updated dependencies [[`2bfb12c`](https://github.com/TanStack/query/commit/2bfb12cc44f1d8495106136e4ddacb817135f8f9)]: - - @tanstack/query-core@5.97.0 - -## 5.96.2 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.96.2 - -## 5.96.1 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.96.1 - -## 5.96.0 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.96.0 - -## 5.95.2 - -### Patch Changes - -- Updated dependencies [[`cd5a35b`](https://github.com/TanStack/query/commit/cd5a35b328837781aa4f9305bb2bd7877ca934e9)]: - - @tanstack/query-core@5.95.2 - -## 5.95.1 - -### Patch Changes - -- Updated dependencies [[`1f1775c`](https://github.com/TanStack/query/commit/1f1775ca92f2b6c035682947ff3b3424804ff31a)]: - - @tanstack/query-core@5.95.1 - -## 5.95.0 - -### Patch Changes - -- Updated dependencies []: - - @tanstack/query-core@5.95.0 - -## 5.94.5 - -### Patch Changes - -- fix(\*): resolve issue about excluded build directory ([#10312](https://github.com/TanStack/query/pull/10312)) - -- Updated dependencies [[`4b6536d`](https://github.com/TanStack/query/commit/4b6536dfce99036f4e37f52943c6fed3ad0e0a18)]: - - @tanstack/query-core@5.94.5 - -## 5.94.4 - -### Patch Changes - -- chore: fixed version ([#10064](https://github.com/TanStack/query/pull/10064)) - -- Updated dependencies [[`4c75210`](https://github.com/TanStack/query/commit/4c75210ce8235fe3d39b67e1029eff11278927cc)]: - - @tanstack/query-core@5.94.4 - -## 5.90.28 - -### Patch Changes - -- fix(streamedQuery): maintain error state on reset refetch with initialData defined ([#10287](https://github.com/TanStack/query/pull/10287)) - -- Updated dependencies [[`248975e`](https://github.com/TanStack/query/commit/248975e896f585f6eaa505c796e73fcf7bfd1eec)]: - - @tanstack/query-core@5.91.2 - -## 5.90.27 - -### Patch Changes - -- Updated dependencies [[`a89aab9`](https://github.com/TanStack/query/commit/a89aab975581c25c113a26c8af486b4cafad272a)]: - - @tanstack/query-core@5.91.1 - -## 5.90.26 - -### Patch Changes - -- Updated dependencies [[`6fa901b`](https://github.com/TanStack/query/commit/6fa901b97a22a80d0fca3f6ed86237ff0cbdd13b)]: - - @tanstack/query-core@5.91.0 - -## 5.90.25 - -## 5.90.24 - -### Patch Changes - -- Updated dependencies [[`e7258c5`](https://github.com/TanStack/query/commit/e7258c5cb30cafa456cdb4e6bc75b43bf619954d)]: - - @tanstack/query-core@5.90.20 - -## 5.90.23 - -### Patch Changes - -- Updated dependencies [[`53fc74e`](https://github.com/TanStack/query/commit/53fc74ebb16730bd3317f039a69c6821386bae93)]: - - @tanstack/query-core@5.90.19 - -## 5.90.22 - -### Patch Changes - -- Updated dependencies [[`dea1614`](https://github.com/TanStack/query/commit/dea1614aaad5c572cf43cea54b64ac09dc4d5b41)]: - - @tanstack/query-core@5.90.18 - -## 5.90.21 - -### Patch Changes - -- Updated dependencies [[`269351b`](https://github.com/TanStack/query/commit/269351b8ce4b4846da3d320ac5b850ee6aada0d6)]: - - @tanstack/query-core@5.90.17 - -## 5.90.20 - -### Patch Changes - -- Updated dependencies [[`7f47906`](https://github.com/TanStack/query/commit/7f47906eaccc3f3aa5ce24b77a83bd7a620a237b)]: - - @tanstack/query-core@5.90.16 - -## 5.90.19 - -### Patch Changes - -- Updated dependencies [[`fccef79`](https://github.com/TanStack/query/commit/fccef797d57d4a9566517bba87c8377f363920f2)]: - - @tanstack/query-core@5.90.15 - -## 5.90.18 - -### Patch Changes - -- Updated dependencies [[`d576092`](https://github.com/TanStack/query/commit/d576092e2ece4ca3936add3eb0da5234c1d82ed4)]: - - @tanstack/query-core@5.90.14 - -## 5.90.17 - -### Patch Changes - -- Updated dependencies [[`4a0a78a`](https://github.com/TanStack/query/commit/4a0a78afbc2432f8cb6828035965853fa98c86a0)]: - - @tanstack/query-core@5.90.13 - -## 5.90.16 - -### Patch Changes - -- Updated dependencies [[`72d8ac5`](https://github.com/TanStack/query/commit/72d8ac5c592004b8f9c3ee086fcb9c3cd615ca05)]: - - @tanstack/query-core@5.90.12 - -## 5.90.15 - -### Patch Changes - -- Updated dependencies [[`c01b150`](https://github.com/TanStack/query/commit/c01b150e3673e11d6533768529a5e6fe3ebee68c)]: - - @tanstack/query-core@5.90.11 - -## 5.90.14 - -## 5.90.13 - -## 5.90.12 - -### Patch Changes - -- Updated dependencies [[`8e2e174`](https://github.com/TanStack/query/commit/8e2e174e9fd2e7b94cd232041e49c9d014d74e26), [`eb559a6`](https://github.com/TanStack/query/commit/eb559a66dc0d77dd46435f624fa64fc068bef9ae)]: - - @tanstack/query-core@5.90.10 - -## 5.90.11 - -### Patch Changes - -- Updated dependencies [[`08b211f`](https://github.com/TanStack/query/commit/08b211f8aa475e05d2f13a36517fc556861ef962)]: - - @tanstack/query-core@5.90.9 - -## 5.90.10 - -### Patch Changes - -- Updated dependencies [[`c0ec9fe`](https://github.com/TanStack/query/commit/c0ec9fe0d1426fe3f233adda3ebf23989ffaa110)]: - - @tanstack/query-core@5.90.8 - -## 5.90.9 - -### Patch Changes - -- Updated dependencies [[`b4cd121`](https://github.com/TanStack/query/commit/b4cd121a39d07cefaa3a3411136d342cc54ce8fb)]: - - @tanstack/query-core@5.90.7 - -## 5.90.8 - -### Patch Changes - -- Updated dependencies [[`1638c02`](https://github.com/TanStack/query/commit/1638c028df55648995d04431179904371a189772)]: - - @tanstack/query-core@5.90.6 - -## 5.90.7 - -### Patch Changes - -- Make `injectIsMutating` signal read-only to prevent external modifications to the internal state ([#9801](https://github.com/TanStack/query/pull/9801)) - -## 5.90.6 - -### Patch Changes - -- Rename `DeveloperToolsFeature` to `DevtoolsFeature` for consistency with internal naming conventions and ecosystem ([#9794](https://github.com/TanStack/query/pull/9794)) - -## 5.90.5 - -### Patch Changes - -- Updated dependencies [[`e42ddfe`](https://github.com/TanStack/query/commit/e42ddfe919f34f847ca101aeef162c69845f9a1e)]: - - @tanstack/query-core@5.90.5 - -## 5.90.4 - -### Patch Changes - -- Updated dependencies [[`20ef922`](https://github.com/TanStack/query/commit/20ef922a0a7c3aee00150bf69123c338b0922922)]: - - @tanstack/query-core@5.90.4 - -## 5.90.3 - -### Patch Changes - -- Updated dependencies [[`4e1c433`](https://github.com/TanStack/query/commit/4e1c4338a72f7384600bbda99e44bc1891695df4)]: - - @tanstack/query-core@5.90.3 diff --git a/packages/angular-query-experimental/package.json b/packages/angular-query-experimental/package.json deleted file mode 100644 index 86e5b2dedb5..00000000000 --- a/packages/angular-query-experimental/package.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "name": "@tanstack/angular-query-experimental", - "version": "5.103.1", - "description": "Signals for managing, caching and syncing asynchronous and remote data in Angular", - "author": "Arnoud de Vries", - "license": "MIT", - "repository": { - "type": "git", - "url": "git+https://github.com/TanStack/query.git", - "directory": "packages/angular-query-experimental" - }, - "homepage": "https://tanstack.com/query", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "keywords": [ - "angular query", - "angular", - "cache", - "performance", - "reactive", - "rxjs", - "signals", - "state management", - "state", - "tanstack" - ], - "scripts": { - "clean": "premove ./dist ./coverage ./dist-ts ./.cache/test-types", - "compile": "tsc --project tsconfig.json", - "test:eslint": "eslint --concurrency=auto ./src", - "test:types": "npm-run-all --serial test:types:*", - "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts56 --tsBuildInfoFile .cache/test-types/ts56.tsbuildinfo", - "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts57 --tsBuildInfoFile .cache/test-types/ts57.tsbuildinfo", - "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts58 --tsBuildInfoFile .cache/test-types/ts58.tsbuildinfo", - "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts59 --tsBuildInfoFile .cache/test-types/ts59.tsbuildinfo", - "test:types:tscurrent": "tsc --project tsconfig.json --outDir .cache/test-types/tscurrent --tsBuildInfoFile .cache/test-types/tscurrent.tsbuildinfo", - "test:types:ts70": "node ../../node_modules/typescript70/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts70 --tsBuildInfoFile .cache/test-types/ts70.tsbuildinfo", - "test:lib": "vitest", - "test:lib:dev": "pnpm run test:lib --watch", - "test:build": "pnpm pack && publint ./dist/*.tgz --strict && attw ./dist/*.tgz; premove ./dist/*.tgz", - "build": "vite build", - "prepack": "node scripts/prepack.js" - }, - "type": "module", - "types": "dist/index.d.ts", - "module": "dist/index.mjs", - "exports": { - ".": { - "@tanstack/custom-condition": "./src/index.ts", - "types": "./dist/index.d.ts", - "default": "./dist/index.mjs" - }, - "./package.json": "./package.json", - "./inject-queries-experimental": { - "types": "./dist/inject-queries-experimental/index.d.ts", - "default": "./dist/inject-queries-experimental/index.mjs" - }, - "./devtools": { - "types": "./dist/devtools/index.d.ts", - "development": "./dist/devtools/index.mjs", - "default": "./dist/devtools/stub.mjs" - }, - "./devtools/production": { - "types": "./dist/devtools/production/index.d.ts", - "default": "./dist/devtools/index.mjs" - }, - "./devtools-panel": { - "types": "./dist/devtools-panel/index.d.ts", - "development": "./dist/devtools-panel/index.mjs", - "default": "./dist/devtools-panel/stub.mjs" - }, - "./devtools-panel/production": { - "types": "./dist/devtools-panel/production/index.d.ts", - "default": "./dist/devtools-panel/index.mjs" - } - }, - "sideEffects": false, - "files": [ - "**/*.d.ts", - "**/*.mjs", - "**/*.mjs.map" - ], - "dependencies": { - "@tanstack/query-core": "workspace:*" - }, - "devDependencies": { - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/query-test-utils": "workspace:*", - "@testing-library/angular": "^18.0.0", - "npm-run-all2": "^5.0.0", - "rxjs": "^7.8.2", - "vite-plugin-dts": "4.2.3", - "vite-plugin-externalize-deps": "^0.9.0", - "vite-tsconfig-paths": "^5.1.4" - }, - "optionalDependencies": { - "@tanstack/query-devtools": "workspace:*" - }, - "peerDependencies": { - "@angular/common": ">=16.0.0", - "@angular/core": ">=16.0.0" - }, - "publishConfig": { - "directory": "dist", - "linkDirectory": false - } -} diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts deleted file mode 100644 index 6e880ea494c..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test-d.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { afterEach, beforeEach, describe, expectTypeOf, it, vi } from 'vitest' -import { provideZonelessChangeDetection } from '@angular/core' -import { queryKey, sleep } from '@tanstack/query-test-utils' -import { - QueryClient, - injectInfiniteQuery, - provideTanStackQuery, - skipToken, -} from '..' -import type { InfiniteData } from '@tanstack/query-core' - -describe('injectInfiniteQuery', () => { - let queryClient: QueryClient - - beforeEach(() => { - queryClient = new QueryClient() - vi.useFakeTimers() - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - }) - - afterEach(() => { - vi.useRealTimers() - }) - - it('should narrow type after isSuccess', () => { - const key = queryKey() - const query = TestBed.runInInjectionContext(() => { - return injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: ({ pageParam }) => - sleep(0).then(() => 'data on page ' + pageParam), - initialPageParam: 0, - getNextPageParam: () => 12, - })) - }) - - if (query.isSuccess()) { - const data = query.data() - expectTypeOf(data).toEqualTypeOf>() - } - }) - - it('should work when queryFn is skipToken', () => { - const key = queryKey() - const query = TestBed.runInInjectionContext(() => { - return injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: skipToken, - initialPageParam: 0, - getNextPageParam: () => 1, - })) - }) - - // TPageParam falls back to `unknown` here (unlike `infiniteQueryOptions`, which infers - // `number` for the same options) because there's no skipToken-specific overload to carry it. - expectTypeOf(query.data()).toEqualTypeOf< - InfiniteData | undefined - >() - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts deleted file mode 100644 index 7f14ec24459..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts +++ /dev/null @@ -1,227 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - Component, - Injector, - provideZonelessChangeDetection, - signal, -} from '@angular/core' -import { render } from '@testing-library/angular' -import { queryKey, sleep } from '@tanstack/query-test-utils' -import { - QueryClient, - injectInfiniteQuery, - provideTanStackQuery, - skipToken, -} from '..' - -describe('injectInfiniteQuery', () => { - let queryClient: QueryClient - - beforeEach(() => { - queryClient = new QueryClient() - vi.useFakeTimers() - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - }) - - afterEach(() => { - vi.useRealTimers() - }) - - it('should properly execute infinite query', async () => { - const key = queryKey() - - @Component({ - template: ` -
status: {{ query.status() }}
-
pages: {{ query.data()?.pages?.join(', ') ?? 'none' }}
- `, - }) - class Page { - readonly query = injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: ({ pageParam }) => - sleep(10).then(() => 'data on page ' + pageParam), - initialPageParam: 0, - getNextPageParam: () => 12, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('status: pending')).toBeInTheDocument() - expect(rendered.getByText('pages: none')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('status: success')).toBeInTheDocument() - expect(rendered.getByText('pages: data on page 0')).toBeInTheDocument() - - rendered.fixture.componentInstance.query.fetchNextPage() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('status: success')).toBeInTheDocument() - expect( - rendered.getByText('pages: data on page 0, data on page 12'), - ).toBeInTheDocument() - }) - - it('should reject and update signal', async () => { - const key = queryKey() - - @Component({ - template: ` -
status: {{ query.status() }}
-
pages: {{ query.data()?.pages?.join(', ') ?? 'none' }}
-
error: {{ query.error()?.message ?? 'none' }}
-
isError: {{ query.isError() }}
-
failureCount: {{ query.failureCount() }}
- `, - }) - class Page { - readonly query = injectInfiniteQuery(() => ({ - retry: false, - queryKey: key, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - initialPageParam: 0, - getNextPageParam: () => 12, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('status: pending')).toBeInTheDocument() - expect(rendered.getByText('pages: none')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('status: error')).toBeInTheDocument() - expect(rendered.getByText('pages: none')).toBeInTheDocument() - expect(rendered.getByText('error: Some error')).toBeInTheDocument() - expect(rendered.getByText('isError: true')).toBeInTheDocument() - expect(rendered.getByText('failureCount: 1')).toBeInTheDocument() - }) - - it('should keep initialData visible alongside the error when a refetch fails', async () => { - const key = queryKey() - - @Component({ - template: ` -
pages: {{ query.data().pages.join(', ') }}
-
isError: {{ query.isError() }}
- `, - }) - class Page { - readonly query = injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - initialData: { pages: [1], pageParams: [1] }, - getNextPageParam: (lastPage: number) => lastPage + 1, - initialPageParam: 0, - retry: false, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('pages: 1')).toBeInTheDocument() - expect(rendered.getByText('isError: false')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('pages: 1')).toBeInTheDocument() - expect(rendered.getByText('isError: true')).toBeInTheDocument() - }) - - describe('skipToken', () => { - it('should not fetch when queryFn is skipToken, and fetch once it is replaced', async () => { - const key = queryKey() - const queryFn = vi.fn(({ pageParam }: { pageParam: number }) => - sleep(10).then(() => `comments for 1 page ${pageParam}`), - ) - - @Component({ - template: ` -
status: {{ query.status() }}
-
isFetching: {{ query.isFetching() }}
-
pages: {{ query.data()?.pages?.join(', ') ?? 'none' }}
- `, - }) - class Page { - postId = signal(undefined) - - readonly query = injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: this.postId() != null ? queryFn : skipToken, - initialPageParam: 0, - getNextPageParam: () => 12, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('status: pending')).toBeInTheDocument() - expect(rendered.getByText('isFetching: false')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(queryFn).not.toHaveBeenCalled() - expect(rendered.getByText('status: pending')).toBeInTheDocument() - expect(rendered.getByText('isFetching: false')).toBeInTheDocument() - - rendered.fixture.componentInstance.postId.set('1') - rendered.fixture.detectChanges() - expect(rendered.getByText('isFetching: true')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(queryFn).toHaveBeenCalledTimes(1) - expect(rendered.getByText('status: success')).toBeInTheDocument() - expect( - rendered.getByText('pages: comments for 1 page 0'), - ).toBeInTheDocument() - }) - }) - - describe('injection context', () => { - it('should throw NG0203 with descriptive error outside injection context', () => { - const key = queryKey() - expect(() => { - injectInfiniteQuery(() => ({ - queryKey: key, - queryFn: ({ pageParam }) => - sleep(0).then(() => 'data on page ' + pageParam), - initialPageParam: 0, - getNextPageParam: () => 12, - })) - }).toThrow(/NG0203(.*?)injectInfiniteQuery/) - }) - - it('should be usable outside injection context when passing an injector', () => { - const key = queryKey() - const query = injectInfiniteQuery( - () => ({ - queryKey: key, - queryFn: ({ pageParam }) => - sleep(0).then(() => 'data on page ' + pageParam), - initialPageParam: 0, - getNextPageParam: () => 12, - }), - { - injector: TestBed.inject(Injector), - }, - ) - - expect(query.status()).toBe('pending') - }) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-is-restoring.test.ts b/packages/angular-query-experimental/src/__tests__/inject-is-restoring.test.ts deleted file mode 100644 index 72ca01f9d69..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-is-restoring.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { beforeEach, describe, expect, it } from 'vitest' -import { Injector, provideZonelessChangeDetection, signal } from '@angular/core' -import { - QueryClient, - injectIsRestoring, - provideIsRestoring, - provideTanStackQuery, -} from '..' - -describe('injectIsRestoring', () => { - let queryClient: QueryClient - - beforeEach(() => { - queryClient = new QueryClient() - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - }) - - it('should return false by default when provideIsRestoring is not used', () => { - const isRestoring = TestBed.runInInjectionContext(() => { - return injectIsRestoring() - }) - - expect(isRestoring()).toBe(false) - }) - - it('should return the provided signal value when provideIsRestoring is used', () => { - const restoringSignal = signal(true) - - TestBed.configureTestingModule({ - providers: [provideIsRestoring(restoringSignal.asReadonly())], - }) - - const isRestoring = TestBed.runInInjectionContext(() => { - return injectIsRestoring() - }) - - expect(isRestoring()).toBe(true) - }) - - it('should reactively reflect changes to the provided signal', () => { - const restoringSignal = signal(true) - - TestBed.configureTestingModule({ - providers: [provideIsRestoring(restoringSignal.asReadonly())], - }) - - const isRestoring = TestBed.runInInjectionContext(() => { - return injectIsRestoring() - }) - - expect(isRestoring()).toBe(true) - - restoringSignal.set(false) - expect(isRestoring()).toBe(false) - - restoringSignal.set(true) - expect(isRestoring()).toBe(true) - }) - - it('should be usable outside injection context when passing an injector', () => { - const isRestoring = injectIsRestoring({ - injector: TestBed.inject(Injector), - }) - - expect(isRestoring()).toBe(false) - }) - - it('should return the provided signal value when using injector option', () => { - const restoringSignal = signal(true) - - TestBed.configureTestingModule({ - providers: [provideIsRestoring(restoringSignal.asReadonly())], - }) - - const isRestoring = injectIsRestoring({ - injector: TestBed.inject(Injector), - }) - - expect(isRestoring()).toBe(true) - }) - - it('should throw NG0203 with descriptive error outside injection context', () => { - expect(() => { - injectIsRestoring() - }).toThrow(/NG0203(.*?)injectIsRestoring/) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts deleted file mode 100644 index b133575746c..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-queries.test-d.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { describe, expectTypeOf, it } from 'vitest' -import { queryKey } from '@tanstack/query-test-utils' -import { skipToken } from '..' -import { injectQueries } from '../inject-queries' -import { queryOptions } from '../query-options' -import type { CreateQueryOptions, CreateQueryResult, OmitKeyof } from '..' -import type { Signal } from '@angular/core' - -describe('injectQueries', () => { - describe('config object overload', () => { - it('TData should always be defined when initialData is provided as an object', () => { - const key1 = queryKey() - const key2 = queryKey() - const key3 = queryKey() - - const query1 = { - queryKey: key1, - queryFn: () => { - return { - wow: true, - } - }, - initialData: { - wow: false, - }, - } - - const query2 = { - queryKey: key2, - queryFn: () => 'Query Data', - initialData: 'initial data', - } - - const query3 = { - queryKey: key3, - queryFn: () => 'Query Data', - } - - const queryResults = injectQueries(() => ({ - queries: [query1, query2, query3], - })) - - const query1Data = queryResults()[0].data() - const query2Data = queryResults()[1].data() - const query3Data = queryResults()[2].data() - - expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>() - expectTypeOf(query2Data).toEqualTypeOf() - expectTypeOf(query3Data).toEqualTypeOf() - }) - - it('TData should be defined when passed through queryOptions', () => { - const key = queryKey() - const options = queryOptions({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: { - wow: true, - }, - }) - const queryResults = injectQueries(() => ({ queries: [options] })) - - const data = queryResults()[0].data() - - expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() - }) - - it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into injectQuery', () => { - const key1 = queryKey() - const key2 = queryKey() - - const query1 = queryOptions({ - queryKey: key1, - queryFn: () => Promise.resolve(1), - select: (data) => data > 1, - }) - - const query2 = { - queryKey: key2, - queryFn: () => Promise.resolve(1), - select: (data: number) => data > 1, - } - - const queryResults = injectQueries(() => ({ queries: [query1, query2] })) - const query1Data = queryResults()[0].data() - const query2Data = queryResults()[1].data() - - expectTypeOf(query1Data).toEqualTypeOf() - expectTypeOf(query2Data).toEqualTypeOf() - }) - - it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { - const key = queryKey() - const queryResults = injectQueries(() => ({ - queries: [ - { - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: () => undefined as { wow: boolean } | undefined, - }, - ], - })) - - const data = queryResults()[0].data() - - expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() - }) - - describe('custom injectable', () => { - it('should allow custom hooks using UseQueryOptions', () => { - type Data = string - const key = queryKey() - - const injectCustomQueries = ( - options?: OmitKeyof, 'queryKey' | 'queryFn'>, - ) => { - return injectQueries(() => ({ - queries: [ - { - ...options, - queryKey: key, - queryFn: () => Promise.resolve('data'), - }, - ], - })) - } - - const queryResults = injectCustomQueries() - const data = queryResults()[0].data() - - expectTypeOf(data).toEqualTypeOf() - }) - }) - - it('TData should have correct type when conditional skipToken is passed', () => { - const key = queryKey() - const queryResults = injectQueries(() => ({ - queries: [ - { - queryKey: key, - queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), - }, - ], - })) - - const firstResult = queryResults()[0] - - expectTypeOf(firstResult).toEqualTypeOf< - CreateQueryResult - >() - expectTypeOf(firstResult.data()).toEqualTypeOf() - }) - - it('should return correct data for dynamic queries with mixed result types', () => { - const key1 = queryKey() - const key2 = queryKey() - - const Queries1 = { - get: () => - queryOptions({ - queryKey: key1, - queryFn: () => Promise.resolve(1), - }), - } - const Queries2 = { - get: () => - queryOptions({ - queryKey: key2, - queryFn: () => Promise.resolve(true), - }), - } - - const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() })) - const result = injectQueries(() => ({ - queries: [...queries1List, { ...Queries2.get() }], - })) - - expectTypeOf(result).branded.toEqualTypeOf< - Signal< - [ - ...Array>, - CreateQueryResult, - ] - > - >() - }) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts b/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts deleted file mode 100644 index 523bda69f06..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-queries.test.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - Component, - effect, - provideZonelessChangeDetection, - signal, -} from '@angular/core' -import { TestBed } from '@angular/core/testing' -import { render } from '@testing-library/angular' -import { queryKey, sleep } from '@tanstack/query-test-utils' -import { QueryClient, provideIsRestoring, provideTanStackQuery } from '..' -import { injectQueries } from '../inject-queries' - -describe('injectQueries', () => { - let queryClient: QueryClient - - beforeEach(() => { - vi.useFakeTimers() - queryClient = new QueryClient() - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - }) - - afterEach(() => { - vi.useRealTimers() - }) - - it('should return the correct states', async () => { - const key1 = queryKey() - const key2 = queryKey() - const results: Array>> = [] - - @Component({ - template: ` -
-
- data1: {{ result()[0].data() ?? 'null' }}, data2: - {{ result()[1].data() ?? 'null' }} -
-
- `, - }) - class Page { - readonly result = injectQueries(() => ({ - queries: [ - { - queryKey: key1, - queryFn: () => sleep(10).then(() => 1), - }, - { - queryKey: key2, - queryFn: () => sleep(100).then(() => 2), - }, - ], - })) - - constructor() { - effect(() => { - const snapshot = this.result().map((q) => ({ data: q.data() })) - results.push(snapshot) - }) - } - } - - const rendered = await render(Page) - - await vi.advanceTimersByTimeAsync(101) - rendered.fixture.detectChanges() - - expect(rendered.getByText('data1: 1, data2: 2')).toBeInTheDocument() - - expect(results.length).toBe(3) - expect(results[0]).toMatchObject([{ data: undefined }, { data: undefined }]) - expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }]) - expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) - }) - - it('should return the combined result when combine is provided', async () => { - const key1 = queryKey() - const key2 = queryKey() - const results: Array> = [] - - @Component({ - template: ` -
data: {{ combined().data.join(',') }}
-
isPending: {{ combined().isPending }}
- `, - }) - class Page { - readonly combined = injectQueries(() => ({ - queries: [ - { - queryKey: key1, - queryFn: () => sleep(10).then(() => 1), - }, - { - queryKey: key2, - queryFn: () => sleep(10).then(() => 2), - }, - ], - combine: (queries) => ({ - data: queries.map((q) => q.data), - isPending: queries.some((q) => q.isPending), - }), - })) - - constructor() { - effect(() => { - results.push({ ...this.combined() }) - }) - } - } - - const rendered = await render(Page) - - expect(rendered.getByText('data: ,')).toBeInTheDocument() - expect(rendered.getByText('isPending: true')).toBeInTheDocument() - expect(results[0]).toMatchObject({ - data: [undefined, undefined], - isPending: true, - }) - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('data: 1,2')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(results[results.length - 1]).toMatchObject({ - data: [1, 2], - isPending: false, - }) - expect(results.length).toBeGreaterThanOrEqual(2) - }) - - it('should reflect error state when one of the queries rejects', async () => { - const key1 = queryKey() - const key2 = queryKey() - - @Component({ - template: ` -
- status1: {{ result()[0].status() }}, error1: - {{ result()[0].error()?.message ?? 'none' }} -
-
- status2: {{ result()[1].status() }}, data2: - {{ result()[1].data() ?? 'none' }} -
- `, - }) - class Page { - readonly result = injectQueries(() => ({ - queries: [ - { - queryKey: key1, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - retry: false, - }, - { - queryKey: key2, - queryFn: () => sleep(10).then(() => 2), - }, - ], - })) - } - - const rendered = await render(Page) - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect( - rendered.getByText('status1: error, error1: Some error'), - ).toBeInTheDocument() - expect(rendered.getByText('status2: success, data2: 2')).toBeInTheDocument() - }) - - describe('isRestoring', () => { - it('should not fetch for the duration of the restoring period when isRestoring is true', async () => { - const key1 = queryKey() - const key2 = queryKey() - const queryFn1 = vi.fn().mockImplementation(() => sleep(10).then(() => 1)) - const queryFn2 = vi.fn().mockImplementation(() => sleep(10).then(() => 2)) - - TestBed.configureTestingModule({ - providers: [provideIsRestoring(signal(true).asReadonly())], - }) - - @Component({ - template: ` -
- status1: {{ queries()[0].status() }}, fetchStatus1: - {{ queries()[0].fetchStatus() }} -
-
- status2: {{ queries()[1].status() }}, fetchStatus2: - {{ queries()[1].fetchStatus() }} -
- `, - }) - class Page { - readonly queries = injectQueries(() => ({ - queries: [ - { queryKey: key1, queryFn: queryFn1 }, - { queryKey: key2, queryFn: queryFn2 }, - ], - })) - } - - const rendered = await render(Page) - - await vi.advanceTimersByTimeAsync(0) - rendered.fixture.detectChanges() - expect( - rendered.getByText('status1: pending, fetchStatus1: idle'), - ).toBeInTheDocument() - expect( - rendered.getByText('status2: pending, fetchStatus2: idle'), - ).toBeInTheDocument() - expect(queryFn1).toHaveBeenCalledTimes(0) - expect(queryFn2).toHaveBeenCalledTimes(0) - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect( - rendered.getByText('status1: pending, fetchStatus1: idle'), - ).toBeInTheDocument() - expect( - rendered.getByText('status2: pending, fetchStatus2: idle'), - ).toBeInTheDocument() - expect(queryFn1).toHaveBeenCalledTimes(0) - expect(queryFn2).toHaveBeenCalledTimes(0) - }) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts deleted file mode 100644 index 022e86ae87d..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test-d.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { describe, expectTypeOf, it } from 'vitest' -import { queryKey, sleep } from '@tanstack/query-test-utils' -import { injectQuery, queryOptions } from '..' -import type { Signal } from '@angular/core' -import type { CreateQueryOptions, OmitKeyof, QueryFunction } from '..' - -describe('injectQuery', () => { - it('should return the correct types', () => { - const key = queryKey() - // unspecified query function should default to unknown - const noQueryFn = injectQuery(() => ({ - queryKey: key, - })) - expectTypeOf(noQueryFn.data()).toEqualTypeOf() - expectTypeOf(noQueryFn.error()).toEqualTypeOf() - - // it should infer the result type from the query function - const fromQueryFn = injectQuery(() => ({ - queryKey: key, - queryFn: () => 'test', - })) - expectTypeOf(fromQueryFn.data()).toEqualTypeOf() - expectTypeOf(fromQueryFn.error()).toEqualTypeOf() - - // it should be possible to specify the result type - const withResult = injectQuery(() => ({ - queryKey: key, - queryFn: () => 'test', - })) - expectTypeOf(withResult.data()).toEqualTypeOf() - expectTypeOf(withResult.error()).toEqualTypeOf() - - // it should be possible to specify the error type - type CustomErrorType = { message: string } - const withError = injectQuery(() => ({ - queryKey: key, - queryFn: () => 'test', - })) - expectTypeOf(withError.data()).toEqualTypeOf() - expectTypeOf(withError.error()).toEqualTypeOf() - - // it should infer the result type from the configuration - const withResultInfer = injectQuery(() => ({ - queryKey: key, - queryFn: () => true, - })) - expectTypeOf(withResultInfer.data()).toEqualTypeOf() - expectTypeOf(withResultInfer.error()).toEqualTypeOf() - - // it should be possible to specify a union type as result type - const unionTypeSync = injectQuery(() => ({ - queryKey: key, - queryFn: () => (Math.random() > 0.5 ? ('a' as const) : ('b' as const)), - })) - expectTypeOf(unionTypeSync.data()).toEqualTypeOf<'a' | 'b' | undefined>() - const unionTypeAsync = injectQuery<'a' | 'b'>(() => ({ - queryKey: key, - queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), - })) - expectTypeOf(unionTypeAsync.data()).toEqualTypeOf<'a' | 'b' | undefined>() - - // it should error when the query function result does not match with the specified type - // @ts-expect-error - injectQuery(() => ({ queryKey: key, queryFn: () => 'test' })) - - // it should infer the result type from a generic query function - /** - * - */ - function queryFn(): Promise { - return Promise.resolve({} as T) - } - - const fromGenericQueryFn = injectQuery(() => ({ - queryKey: key, - queryFn: () => queryFn(), - })) - expectTypeOf(fromGenericQueryFn.data()).toEqualTypeOf() - expectTypeOf(fromGenericQueryFn.error()).toEqualTypeOf() - - type MyData = number - type MyQueryKey = readonly ['my-data', number] - - const getMyDataArrayKey: QueryFunction = ({ - queryKey: [, n], - }) => { - return n + 42 - } - - const fromMyDataArrayKeyQueryFn = injectQuery(() => ({ - queryKey: ['my-data', 100] as const, - queryFn: getMyDataArrayKey, - })) - expectTypeOf(fromMyDataArrayKeyQueryFn.data()).toEqualTypeOf< - number | undefined - >() - - // it should handle query-functions that return Promise - const fromPromiseAnyQueryFn = injectQuery(() => ({ - queryKey: key, - queryFn: () => fetch('return Promise').then((resp) => resp.json()), - })) - expectTypeOf(fromPromiseAnyQueryFn.data()).toEqualTypeOf() - - const getMyDataStringKey: QueryFunction = (context) => { - expectTypeOf(context.queryKey).toEqualTypeOf<['1']>() - return Number(context.queryKey[0]) + 42 - } - - const fromGetMyDataStringKeyQueryFn = injectQuery(() => ({ - queryKey: ['1'] as ['1'], - queryFn: getMyDataStringKey, - })) - expectTypeOf(fromGetMyDataStringKeyQueryFn.data()).toEqualTypeOf< - number | undefined - >() - - // handles wrapped queries with custom fetcher passed as inline queryFn - const createWrappedQuery = < - TQueryKey extends [string, Record?], - TQueryFnData, - TError, - TData = TQueryFnData, - >( - qk: TQueryKey, - fetcher: ( - obj: TQueryKey[1], - token: string, - // return type must be wrapped with TQueryFnReturn - ) => Promise, - options?: OmitKeyof< - CreateQueryOptions, - 'queryKey' | 'queryFn' | 'initialData', - 'safely' - >, - ) => - injectQuery(() => ({ - queryKey: qk, - queryFn: () => fetcher(qk[1], 'token'), - ...options, - })) - const fromWrappedQuery = createWrappedQuery([''], () => - Promise.resolve('1'), - ) - expectTypeOf(fromWrappedQuery.data()).toEqualTypeOf() - - // handles wrapped queries with custom fetcher passed directly to createQuery - const createWrappedFuncStyleQuery = < - TQueryKey extends [string, Record?], - TQueryFnData, - TError, - TData = TQueryFnData, - >( - qk: TQueryKey, - fetcher: () => Promise, - options?: OmitKeyof< - CreateQueryOptions, - 'queryKey' | 'queryFn' | 'initialData', - 'safely' - >, - ) => injectQuery(() => ({ queryKey: qk, queryFn: fetcher, ...options })) - const fromWrappedFuncStyleQuery = createWrappedFuncStyleQuery([''], () => - Promise.resolve(true), - ) - expectTypeOf(fromWrappedFuncStyleQuery.data()).toEqualTypeOf< - boolean | undefined - >() - }) - - describe('initialData', () => { - describe('Config object overload', () => { - it('TData should always be defined when initialData is provided as an object', () => { - const key = queryKey() - const { data } = injectQuery(() => ({ - queryKey: key, - queryFn: () => ({ wow: true }), - initialData: { wow: true }, - })) - - expectTypeOf(data).toEqualTypeOf>() - }) - - it('TData should be defined when passed through queryOptions', () => { - const key = queryKey() - const options = () => - queryOptions({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: { - wow: true, - }, - }) - const { data } = injectQuery(options) - - expectTypeOf(data).toEqualTypeOf>() - }) - - it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => { - const key = queryKey() - const options = queryOptions({ - queryKey: key, - queryFn: () => Promise.resolve(1), - }) - - const query = injectQuery(() => ({ - ...options, - select: (data) => data > 1, - })) - - expectTypeOf(query.data).toEqualTypeOf>() - }) - - it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { - const key = queryKey() - const { data } = injectQuery(() => ({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: () => ({ - wow: true, - }), - })) - - expectTypeOf(data).toEqualTypeOf>() - }) - - it('TData should have undefined in the union when initialData is NOT provided', () => { - const key = queryKey() - const { data } = injectQuery(() => ({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - })) - - expectTypeOf(data).toEqualTypeOf>() - }) - - it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { - const key = queryKey() - const { data } = injectQuery(() => ({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: () => undefined as { wow: boolean } | undefined, - })) - - expectTypeOf(data).toEqualTypeOf>() - }) - - it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => { - return { - wow: true, - } - }, - initialData: () => undefined as { wow: boolean } | undefined, - })) - - if (query.isSuccess()) { - expectTypeOf(query.data).toEqualTypeOf>() - } - }) - }) - - describe('structuralSharing', () => { - it('should be able to use structuralSharing with unknown types', () => { - const key = queryKey() - // https://github.com/TanStack/query/issues/6525#issuecomment-1938411343 - injectQuery(() => ({ - queryKey: key, - queryFn: () => 5, - structuralSharing: (oldData, newData) => { - expectTypeOf(oldData).toBeUnknown() - expectTypeOf(newData).toBeUnknown() - return newData - }, - })) - }) - }) - }) - - describe('Discriminated union return type', () => { - it('data should be possibly undefined by default', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - - expectTypeOf(query.data).toEqualTypeOf>() - }) - - it('data should be defined when query is success', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - - if (query.isSuccess()) { - expectTypeOf(query.data).toEqualTypeOf>() - } - }) - - it('error should be null when query is success', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - - if (query.isSuccess()) { - expectTypeOf(query.error).toEqualTypeOf>() - } - }) - - it('data should be undefined when query is pending', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - - if (query.isPending()) { - expectTypeOf(query.data).toEqualTypeOf>() - } - }) - - it('error should be defined when query is error', () => { - const key = queryKey() - const query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - - if (query.isError()) { - expectTypeOf(query.error).toEqualTypeOf>() - } - }) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts b/packages/angular-query-experimental/src/__tests__/inject-query.test.ts deleted file mode 100644 index c36aa0d1ff7..00000000000 --- a/packages/angular-query-experimental/src/__tests__/inject-query.test.ts +++ /dev/null @@ -1,700 +0,0 @@ -import { - ApplicationRef, - Component, - Injector, - computed, - input, - provideZonelessChangeDetection, - signal, -} from '@angular/core' -import { TestBed } from '@angular/core/testing' -import { HttpClient, provideHttpClient } from '@angular/common/http' -import { - HttpTestingController, - provideHttpClientTesting, -} from '@angular/common/http/testing' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { render } from '@testing-library/angular' -import { queryKey, sleep } from '@tanstack/query-test-utils' -import { lastValueFrom } from 'rxjs' -import { - QueryCache, - QueryClient, - injectQuery, - provideIsRestoring, - provideTanStackQuery, -} from '..' -import { setSignalInputs } from './test-utils' - -describe('injectQuery', () => { - let queryCache: QueryCache - let queryClient: QueryClient - - beforeEach(() => { - vi.useFakeTimers() - queryCache = new QueryCache() - queryClient = new QueryClient({ queryCache }) - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - }) - - afterEach(() => { - vi.useRealTimers() - }) - - it('should return pending status initially', () => { - const key = queryKey() - const query = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(10).then(() => 'Some data'), - })) - }) - - expect(query.status()).toBe('pending') - expect(query.isPending()).toBe(true) - expect(query.isFetching()).toBe(true) - expect(query.isStale()).toBe(true) - expect(query.isFetched()).toBe(false) - }) - - it('should resolve to success and update signal: injectQuery()', async () => { - const key = queryKey() - - @Component({ - template: ` -
status: {{ query.status() }}
-
data: {{ query.data() ?? 'none' }}
-
isPending: {{ query.isPending() }}
-
isFetching: {{ query.isFetching() }}
-
isFetched: {{ query.isFetched() }}
-
isSuccess: {{ query.isSuccess() }}
- `, - }) - class Page { - readonly query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(10).then(() => 'result2'), - })) - } - - const rendered = await render(Page) - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('status: success')).toBeInTheDocument() - expect(rendered.getByText('data: result2')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(rendered.getByText('isFetching: false')).toBeInTheDocument() - expect(rendered.getByText('isFetched: true')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: true')).toBeInTheDocument() - }) - - it('should reject and update signal', async () => { - const key = queryKey() - - @Component({ - template: ` -
status: {{ query.status() }}
-
data: {{ query.data() ?? 'none' }}
-
error: {{ query.error()?.message ?? 'none' }}
-
isPending: {{ query.isPending() }}
-
isFetching: {{ query.isFetching() }}
-
isError: {{ query.isError() }}
-
failureCount: {{ query.failureCount() }}
-
failureReason: {{ query.failureReason()?.message ?? 'none' }}
- `, - }) - class Page { - readonly query = injectQuery(() => ({ - retry: false, - queryKey: key, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - })) - } - - const rendered = await render(Page) - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('status: error')).toBeInTheDocument() - expect(rendered.getByText('data: none')).toBeInTheDocument() - expect(rendered.getByText('error: Some error')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(rendered.getByText('isFetching: false')).toBeInTheDocument() - expect(rendered.getByText('isError: true')).toBeInTheDocument() - expect(rendered.getByText('failureCount: 1')).toBeInTheDocument() - expect(rendered.getByText('failureReason: Some error')).toBeInTheDocument() - }) - - it('should be able to select a part of the data with select', async () => { - const key = queryKey() - - @Component({ - template: `
data: {{ query.data() ?? 'none' }}
`, - }) - class Page { - readonly query = injectQuery<{ name: string }, Error, string>(() => ({ - queryKey: key, - queryFn: () => sleep(10).then(() => ({ name: 'test' })), - select: (data) => data.name, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('data: none')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('data: test')).toBeInTheDocument() - }) - - it('should show placeholderData until queryFn resolves and then expose real data', async () => { - const key = queryKey() - - @Component({ - template: ` -
data: {{ query.data() }}
-
isPlaceholderData: {{ query.isPlaceholderData() }}
-
isSuccess: {{ query.isSuccess() }}
- `, - }) - class Page { - readonly query = injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(10).then(() => 'real-data'), - placeholderData: 'placeholder', - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('data: placeholder')).toBeInTheDocument() - expect(rendered.getByText('isPlaceholderData: true')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: true')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - - expect(rendered.getByText('data: real-data')).toBeInTheDocument() - expect(rendered.getByText('isPlaceholderData: false')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: true')).toBeInTheDocument() - }) - - it('should update query on options contained signal change', async () => { - const key1 = queryKey() - const key2 = queryKey() - const key = signal(key1) - const spy = vi.fn(() => sleep(10).then(() => 'Some data')) - - const query = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key(), - queryFn: spy, - })) - }) - - await vi.advanceTimersByTimeAsync(0) - expect(spy).toHaveBeenCalledTimes(1) - - await vi.advanceTimersByTimeAsync(10) - expect(query.status()).toBe('success') - - key.set(key2) - TestBed.tick() - - expect(spy).toHaveBeenCalledTimes(2) - // should call queryFn with context containing the new queryKey - expect(spy).toHaveBeenNthCalledWith(2, { - client: queryClient, - meta: undefined, - queryKey: key2, - signal: expect.anything(), - }) - }) - - it('should only run query once enabled signal is set to true', async () => { - const key = queryKey() - const spy = vi.fn(() => sleep(10).then(() => 'Some data')) - const enabled = signal(false) - - const query = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key, - queryFn: spy, - enabled: enabled(), - })) - }) - - expect(spy).not.toHaveBeenCalled() - expect(query.status()).toBe('pending') - - enabled.set(true) - - await vi.advanceTimersByTimeAsync(10) - expect(spy).toHaveBeenCalledTimes(1) - expect(query.status()).toBe('success') - }) - - it('should not fetch while the enabled signal is false, and fetch again once it is truthy', async () => { - const key = queryKey() - const spy = vi.fn(() => sleep(10).then(() => 'Some data')) - const filter = signal('a') - - const query = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: [...key, filter()], - queryFn: spy, - enabled: !!filter(), - })) - }) - - await vi.advanceTimersByTimeAsync(10) - expect(spy).toHaveBeenCalledTimes(1) - expect(query.status()).toBe('success') - - filter.set('') - - await vi.advanceTimersByTimeAsync(10) - expect(spy).toHaveBeenCalledTimes(1) - expect(query.isFetching()).toBe(false) - - filter.set('b') - - await vi.advanceTimersByTimeAsync(10) - expect(spy).toHaveBeenCalledTimes(2) - }) - - it('should properly execute dependent queries', async () => { - const key1 = queryKey() - const key2 = queryKey() - - const query1 = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key1, - queryFn: () => sleep(10).then(() => 'Some data'), - })) - }) - - const dependentQueryFn = vi - .fn() - .mockImplementation(() => sleep(1000).then(() => 'Some data')) - - const query2 = TestBed.runInInjectionContext(() => { - return injectQuery( - computed(() => ({ - queryKey: key2, - queryFn: dependentQueryFn, - enabled: !!query1.data(), - })), - ) - }) - - expect(query1.data()).toStrictEqual(undefined) - expect(query2.fetchStatus()).toStrictEqual('idle') - expect(dependentQueryFn).not.toHaveBeenCalled() - - await vi.advanceTimersByTimeAsync(11) - - expect(query1.data()).toStrictEqual('Some data') - expect(query2.fetchStatus()).toStrictEqual('fetching') - - await vi.advanceTimersByTimeAsync(1002) - - expect(query2.fetchStatus()).toStrictEqual('idle') - expect(query2.status()).toStrictEqual('success') - expect(dependentQueryFn).toHaveBeenCalledTimes(1) - expect(dependentQueryFn).toHaveBeenCalledWith( - expect.objectContaining({ queryKey: key2 }), - ) - }) - - it('should use the current value for the queryKey when refetch is called', async () => { - const key = queryKey() - const fetchFn = vi.fn(() => sleep(10).then(() => 'Some data')) - const keySignal = signal('key11') - - const query = TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: [...key, keySignal()], - queryFn: fetchFn, - enabled: false, - })) - }) - - expect(fetchFn).not.toHaveBeenCalled() - - void query.refetch() - await vi.advanceTimersByTimeAsync(10) - - expect(fetchFn).toHaveBeenCalledTimes(1) - expect(fetchFn).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - queryKey: [...key, 'key11'], - }), - ) - - keySignal.set('key12') - - void query.refetch() - await vi.advanceTimersByTimeAsync(10) - - expect(fetchFn).toHaveBeenCalledTimes(2) - expect(fetchFn).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - queryKey: [...key, 'key12'], - }), - ) - expect(query.data()).toBe('Some data') - }) - - it('should keep initialData visible alongside the error when a refetch fails', async () => { - const key = queryKey() - - @Component({ - template: ` -
data: {{ query.data() }}
-
isError: {{ query.isError() }}
- `, - }) - class Page { - readonly query = injectQuery(() => ({ - queryKey: key, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - initialData: 'initial', - retry: false, - })) - } - - const rendered = await render(Page) - - expect(rendered.getByText('data: initial')).toBeInTheDocument() - expect(rendered.getByText('isError: false')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('data: initial')).toBeInTheDocument() - expect(rendered.getByText('isError: true')).toBeInTheDocument() - }) - - describe('throwOnError', () => { - it('should evaluate throwOnError when query is expected to throw', async () => { - const key = queryKey() - const boundaryFn = vi.fn() - TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key, - queryFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - retry: false, - throwOnError: boundaryFn, - })) - }) - - await vi.advanceTimersByTimeAsync(11) - expect(boundaryFn).toHaveBeenCalledTimes(1) - expect(boundaryFn).toHaveBeenCalledWith( - Error('Some error'), - expect.objectContaining({ - state: expect.objectContaining({ status: 'error' }), - }), - ) - }) - - it('should throw when throwOnError is true', async () => { - const key = queryKey() - TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key, - queryFn: () => - sleep(0).then(() => Promise.reject(new Error('Some error'))), - throwOnError: true, - })) - }) - - await expect(vi.runAllTimersAsync()).rejects.toThrow('Some error') - }) - - it('should throw when throwOnError function returns true', async () => { - const key = queryKey() - TestBed.runInInjectionContext(() => { - return injectQuery(() => ({ - queryKey: key, - queryFn: () => - sleep(0).then(() => Promise.reject(new Error('Some error'))), - throwOnError: () => true, - })) - }) - - await expect(vi.runAllTimersAsync()).rejects.toThrow('Some error') - }) - }) - - it('should render with required signal inputs', async () => { - @Component({ - selector: 'app-fake', - template: `{{ query.data() }}`, - }) - class FakeComponent { - name = input.required() - - query = injectQuery(() => ({ - queryKey: ['fake', this.name()], - queryFn: () => this.name(), - })) - } - - const fixture = TestBed.createComponent(FakeComponent) - setSignalInputs(fixture.componentInstance, { - name: 'signal-input-required-test', - }) - - fixture.detectChanges() - await vi.advanceTimersByTimeAsync(0) - - expect(fixture.componentInstance.query.data()).toEqual( - 'signal-input-required-test', - ) - }) - - describe('isRestoring', () => { - it('should not fetch for the duration of the restoring period when isRestoring is true', async () => { - const key = queryKey() - const queryFn = vi - .fn() - .mockImplementation(() => sleep(10).then(() => 'data')) - - TestBed.configureTestingModule({ - providers: [provideIsRestoring(signal(true).asReadonly())], - }) - - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - queryFn, - })), - ) - - await vi.advanceTimersByTimeAsync(0) - expect(query.status()).toBe('pending') - expect(query.fetchStatus()).toBe('idle') - expect(query.data()).toBeUndefined() - expect(queryFn).toHaveBeenCalledTimes(0) - - await vi.advanceTimersByTimeAsync(10) - expect(query.status()).toBe('pending') - expect(query.fetchStatus()).toBe('idle') - expect(query.data()).toBeUndefined() - expect(queryFn).toHaveBeenCalledTimes(0) - }) - }) - - describe('injection context', () => { - it('should throw NG0203 with descriptive error outside injection context', () => { - const key = queryKey() - expect(() => { - injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - })) - }).toThrow(/NG0203(.*?)injectQuery/) - }) - - it('should be usable outside injection context when passing an injector', () => { - const key = queryKey() - const query = injectQuery( - () => ({ - queryKey: key, - queryFn: () => sleep(0).then(() => 'Some data'), - }), - { - injector: TestBed.inject(Injector), - }, - ) - - expect(query.status()).toBe('pending') - }) - - it('should complete queries before whenStable() resolves', async () => { - const key = queryKey() - const app = TestBed.inject(ApplicationRef) - - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(50).then(() => 'test data'), - })), - ) - - expect(query.status()).toBe('pending') - expect(query.data()).toBeUndefined() - - const stablePromise = app.whenStable() - await vi.advanceTimersByTimeAsync(60) - await stablePromise - - expect(query.status()).toBe('success') - expect(query.data()).toBe('test data') - }) - - it('should complete HttpClient-based queries before whenStable() resolves', async () => { - TestBed.configureTestingModule({ - providers: [provideHttpClient(), provideHttpClientTesting()], - }) - - const app = TestBed.inject(ApplicationRef) - const httpClient = TestBed.inject(HttpClient) - const httpTestingController = TestBed.inject(HttpTestingController) - - // Create a query using HttpClient - const key = queryKey() - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - queryFn: () => - lastValueFrom(httpClient.get<{ message: string }>('/api/test')), - })), - ) - - // Schedule the HTTP response - setTimeout(() => { - const req = httpTestingController.expectOne('/api/test') - req.flush({ message: 'http test data' }) - }, 10) - - // Initial state - expect(query.status()).toBe('pending') - - // Advance timers and wait for Angular to be "stable" - const stablePromise = app.whenStable() - await vi.advanceTimersByTimeAsync(20) - await stablePromise - - // Query should be complete after whenStable() thanks to PendingTasks integration - expect(query.status()).toBe('success') - expect(query.data()).toEqual({ message: 'http test data' }) - - httpTestingController.verify() - }) - - it('should handle synchronous queryFn with staleTime', async () => { - const app = TestBed.inject(ApplicationRef) - let callCount = 0 - - const key = queryKey() - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - staleTime: 1000, - queryFn: () => { - callCount++ - return `sync-data-${callCount}` - }, - })), - ) - - // Synchronize pending effects - TestBed.tick() - - const stablePromise = app.whenStable() - await stablePromise - - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') - expect(callCount).toBe(1) - - await query.refetch() - await Promise.resolve() - await vi.runAllTimersAsync() - await app.whenStable() - - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-2') - expect(callCount).toBe(2) - }) - - it('should handle enabled/disabled transitions with synchronous queryFn', async () => { - const app = TestBed.inject(ApplicationRef) - const enabledSignal = signal(false) - let callCount = 0 - - const key = queryKey() - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - enabled: enabledSignal(), - queryFn: () => { - callCount++ - return `sync-data-${callCount}` - }, - })), - ) - - // Initially disabled - TestBed.tick() - await app.whenStable() - expect(query.status()).toBe('pending') - expect(query.data()).toBeUndefined() - expect(callCount).toBe(0) - - // Enable the query - enabledSignal.set(true) - TestBed.tick() - - await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') - expect(callCount).toBe(1) - }) - - it('should handle query invalidation with synchronous data', async () => { - const app = TestBed.inject(ApplicationRef) - const testKey = queryKey() - let callCount = 0 - - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: testKey, - queryFn: () => { - callCount++ - return `sync-data-${callCount}` - }, - })), - ) - - // Synchronize pending effects - TestBed.tick() - - await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-1') - expect(callCount).toBe(1) - - // Invalidate the query - queryClient.invalidateQueries({ queryKey: testKey }) - TestBed.tick() - - // Wait for the invalidation to trigger a refetch - await Promise.resolve() - await vi.advanceTimersByTimeAsync(10) - TestBed.tick() - - await app.whenStable() - expect(query.status()).toBe('success') - expect(query.data()).toBe('sync-data-2') - expect(callCount).toBe(2) - }) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/provide-query-client.test.ts b/packages/angular-query-experimental/src/__tests__/provide-query-client.test.ts deleted file mode 100644 index 5253274de34..00000000000 --- a/packages/angular-query-experimental/src/__tests__/provide-query-client.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { describe, expect, it } from 'vitest' -import { InjectionToken, provideZonelessChangeDetection } from '@angular/core' -import { QueryClient } from '@tanstack/query-core' -import { provideQueryClient } from '../providers' - -describe('provideQueryClient', () => { - it('should provide a QueryClient instance directly', () => { - const queryClient = new QueryClient() - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideQueryClient(queryClient), - ], - }) - - const providedQueryClient = TestBed.inject(QueryClient) - expect(providedQueryClient).toBe(queryClient) - }) - - it('should provide a QueryClient instance using an InjectionToken', () => { - const queryClient = new QueryClient() - const CUSTOM_QUERY_CLIENT = new InjectionToken('', { - factory: () => queryClient, - }) - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideQueryClient(CUSTOM_QUERY_CLIENT), - ], - }) - - const providedQueryClient = TestBed.inject(QueryClient) - expect(providedQueryClient).toBe(queryClient) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/provide-tanstack-query.test.ts b/packages/angular-query-experimental/src/__tests__/provide-tanstack-query.test.ts deleted file mode 100644 index ff1cf4324fc..00000000000 --- a/packages/angular-query-experimental/src/__tests__/provide-tanstack-query.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { TestBed } from '@angular/core/testing' -import { describe, expect, it } from 'vitest' -import { InjectionToken, provideZonelessChangeDetection } from '@angular/core' -import { QueryClient } from '@tanstack/query-core' -import { provideTanStackQuery } from '../providers' - -describe('provideTanStackQuery', () => { - it('should provide a QueryClient instance directly', () => { - const queryClient = new QueryClient() - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) - - const providedQueryClient = TestBed.inject(QueryClient) - expect(providedQueryClient).toBe(queryClient) - }) - - it('should provide a QueryClient instance using an InjectionToken', () => { - const queryClient = new QueryClient() - const CUSTOM_QUERY_CLIENT = new InjectionToken('', { - factory: () => queryClient, - }) - - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(CUSTOM_QUERY_CLIENT), - ], - }) - - const providedQueryClient = TestBed.inject(QueryClient) - expect(providedQueryClient).toBe(queryClient) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts b/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts deleted file mode 100644 index cebcd7b82bb..00000000000 --- a/packages/angular-query-experimental/src/__tests__/signal-proxy.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { isSignal, signal } from '@angular/core' -import { describe, expect, it } from 'vitest' -import { signalProxy } from '../signal-proxy' - -describe('signalProxy', () => { - const inputSignal = signal({ fn: () => 'bar', baz: 'qux' }) - const proxy = signalProxy(inputSignal) - - it('should have computed fields', () => { - expect(proxy.baz()).toEqual('qux') - expect(isSignal(proxy.baz)).toBe(true) - }) - - it('should pass through functions as-is', () => { - expect(proxy.fn()).toEqual('bar') - expect(isSignal(proxy.fn)).toBe(false) - }) - - it('should support "in" operator', () => { - expect('baz' in proxy).toBe(true) - expect('foo' in proxy).toBe(false) - }) - - it('should support "Object.keys"', () => { - expect(Object.keys(proxy)).toEqual(['fn', 'baz']) - }) -}) diff --git a/packages/angular-query-experimental/src/__tests__/test-utils.ts b/packages/angular-query-experimental/src/__tests__/test-utils.ts deleted file mode 100644 index 218cdea5f6c..00000000000 --- a/packages/angular-query-experimental/src/__tests__/test-utils.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { isSignal, untracked } from '@angular/core' -import { SIGNAL, signalSetFn } from '@angular/core/primitives/signals' -import { expect } from 'vitest' -import type { InputSignal, Signal } from '@angular/core' -import type { ComponentFixture } from '@angular/core/testing' - -// Evaluate all signals on an object and return the result -function evaluateSignals>( - obj: T, -): { [K in keyof T]: ReturnType } { - const result: Partial<{ [K in keyof T]: ReturnType }> = {} - - untracked(() => { - for (const key in obj) { - if ( - Object.prototype.hasOwnProperty.call(obj, key) && - // Only evaluate signals, not normal functions - isSignal(obj[key]) - ) { - const func = obj[key] - result[key] = func() - } - } - }) - - return result as { [K in keyof T]: ReturnType } -} - -export const expectSignals = >( - obj: T, - expected: Partial<{ - [K in keyof T]: T[K] extends Signal ? ReturnType : never - }>, -): void => { - expect(evaluateSignals(obj)).toMatchObject(expected) -} - -type ToSignalInputUpdatableMap = { - [K in keyof T as T[K] extends InputSignal - ? K - : never]: T[K] extends InputSignal ? Value : never -} - -function componentHasSignalInputProperty( - component: object, - property: TProperty, -): component is { [key in TProperty]: InputSignal } { - return ( - component.hasOwnProperty(property) && (component as any)[property][SIGNAL] - ) -} - -/** - * Set required signal input value to component fixture - * @see https://github.com/angular/angular/issues/54013 - */ -export function setSignalInputs>( - component: T, - inputs: ToSignalInputUpdatableMap, -) { - for (const inputKey in inputs) { - if (componentHasSignalInputProperty(component, inputKey)) { - signalSetFn(component[inputKey][SIGNAL], inputs[inputKey]) - } - } -} - -export function setFixtureSignalInputs>( - componentFixture: ComponentFixture, - inputs: ToSignalInputUpdatableMap, - options: { detectChanges: boolean } = { detectChanges: true }, -) { - setSignalInputs(componentFixture.componentInstance, inputs) - if (options.detectChanges) { - componentFixture.detectChanges() - } -} diff --git a/packages/angular-query-experimental/src/create-base-query.ts b/packages/angular-query-experimental/src/create-base-query.ts deleted file mode 100644 index 4daede76844..00000000000 --- a/packages/angular-query-experimental/src/create-base-query.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { - NgZone, - VERSION, - computed, - effect, - inject, - signal, - untracked, -} from '@angular/core' -import { - QueryClient, - notifyManager, - shouldThrowError, -} from '@tanstack/query-core' -import { signalProxy } from './signal-proxy' -import { injectIsRestoring } from './inject-is-restoring' -import { PENDING_TASKS } from './pending-tasks-compat' -import type { PendingTaskRef } from './pending-tasks-compat' -import type { - QueryKey, - QueryObserver, - QueryObserverResult, -} from '@tanstack/query-core' -import type { CreateBaseQueryOptions } from './types' - -/** - * Base implementation for `injectQuery` and `injectInfiniteQuery`. - * @param optionsFn - * @param Observer - */ -export function createBaseQuery< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey extends QueryKey, ->( - optionsFn: () => CreateBaseQueryOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - >, - Observer: typeof QueryObserver, -) { - const ngZone = inject(NgZone) - const pendingTasks = inject(PENDING_TASKS) - const queryClient = inject(QueryClient) - const isRestoring = injectIsRestoring() - - /** - * Signal that has the default options from query client applied - * computed() is used so signals can be inserted into the options - * making it reactive. Wrapping options in a function ensures embedded expressions - * are preserved and can keep being applied after signal changes - */ - const defaultedOptionsSignal = computed(() => { - const defaultedOptions = queryClient.defaultQueryOptions(optionsFn()) - defaultedOptions._optimisticResults = isRestoring() - ? 'isRestoring' - : 'optimistic' - return defaultedOptions - }) - - const observerSignal = (() => { - let instance: QueryObserver< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey - > | null = null - - return computed(() => { - return (instance ||= new Observer(queryClient, defaultedOptionsSignal())) - }) - })() - - const optimisticResultSignal = computed(() => - observerSignal().getOptimisticResult(defaultedOptionsSignal()), - ) - - const resultFromSubscriberSignal = signal | null>(null) - - effect( - (onCleanup) => { - const observer = observerSignal() - const defaultedOptions = defaultedOptionsSignal() - - untracked(() => { - 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' || undefined, - }, - ) - - effect((onCleanup) => { - // observer.trackResult is not used as this optimization is not needed for Angular - const observer = observerSignal() - let pendingTaskRef: PendingTaskRef | null = null - - const unsubscribe = isRestoring() - ? () => undefined - : untracked(() => - ngZone.runOutsideAngular(() => { - return observer.subscribe( - notifyManager.batchCalls((state) => { - ngZone.run(() => { - if (state.fetchStatus === 'fetching' && !pendingTaskRef) { - pendingTaskRef = pendingTasks.add() - } - - if (state.fetchStatus === 'idle' && pendingTaskRef) { - pendingTaskRef() - pendingTaskRef = null - } - - if ( - state.isError && - !state.isFetching && - shouldThrowError(observer.options.throwOnError, [ - state.error, - observer.getCurrentQuery(), - ]) - ) { - ngZone.onError.emit(state.error) - throw state.error - } - resultFromSubscriberSignal.set(state) - }) - }), - ) - }), - ) - - onCleanup(() => { - if (pendingTaskRef) { - pendingTaskRef() - pendingTaskRef = null - } - unsubscribe() - }) - }) - - return signalProxy( - computed(() => { - const subscriberResult = resultFromSubscriberSignal() - const optimisticResult = optimisticResultSignal() - const result = subscriberResult ?? optimisticResult - - // Wrap methods to ensure observer has latest options before execution - const observer = observerSignal() - - const originalRefetch = result.refetch - return { - ...result, - refetch: ((...args: Parameters) => { - observer.setOptions(defaultedOptionsSignal()) - return originalRefetch(...args) - }) as typeof originalRefetch, - } - }), - ) -} diff --git a/packages/angular-query-experimental/src/devtools-panel/inject-devtools-panel.ts b/packages/angular-query-experimental/src/devtools-panel/inject-devtools-panel.ts deleted file mode 100644 index 94aaa185ca7..00000000000 --- a/packages/angular-query-experimental/src/devtools-panel/inject-devtools-panel.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { - DestroyRef, - Injector, - PLATFORM_ID, - assertInInjectionContext, - computed, - effect, - inject, - runInInjectionContext, - untracked, -} from '@angular/core' -import { QueryClient, onlineManager } from '@tanstack/query-core' -import { isPlatformBrowser } from '@angular/common' -import type { TanstackQueryDevtoolsPanel } from '@tanstack/query-devtools' -import type { - DevtoolsPanelOptions, - InjectDevtoolsPanel, - InjectDevtoolsPanelOptions, -} from './types' - -/** - * Inject a TanStack Query devtools panel and render it in the DOM. - * - * Devtools panel allows programmatic control over the devtools, for example if you want to render - * the devtools as part of your own devtools. - * - * Consider `withDevtools` instead if you don't need this. - * @param injectDevtoolsPanelFn - A function that returns devtools panel options. - * @param options - Additional configuration - * @returns DevtoolsPanelRef - * @see https://tanstack.com/query/v5/docs/framework/angular/devtools - */ -export const injectDevtoolsPanel: InjectDevtoolsPanel = ( - injectDevtoolsPanelFn: () => DevtoolsPanelOptions, - options?: InjectDevtoolsPanelOptions, -) => { - !options?.injector && assertInInjectionContext(injectDevtoolsPanel) - const currentInjector = options?.injector ?? inject(Injector) - - return runInInjectionContext(currentInjector, () => { - const destroyRef = inject(DestroyRef) - const isBrowser = isPlatformBrowser(inject(PLATFORM_ID)) - const injectedClient = inject(QueryClient, { optional: true }) - - const queryOptions = computed(injectDevtoolsPanelFn) - let devtools: TanstackQueryDevtoolsPanel | null = null - - const destroy = () => { - devtools?.unmount() - devtools = null - } - - if (!isBrowser) - return { - destroy, - } - - effect(() => { - const { - client = injectedClient, - errorTypes = [], - styleNonce, - shadowDOMTarget, - onClose, - hostElement, - } = queryOptions() - - untracked(() => { - if (!client) throw new Error('No QueryClient found') - if (!devtools && hostElement) { - import('@tanstack/query-devtools') - .then((queryDevtools) => { - devtools = new queryDevtools.TanstackQueryDevtoolsPanel({ - client, - queryFlavor: 'Angular Query', - version: '5', - buttonPosition: 'bottom-left', - position: 'bottom', - initialIsOpen: true, - errorTypes, - styleNonce, - shadowDOMTarget, - onClose, - onlineManager, - }) - devtools.mount(hostElement.nativeElement) - }) - .catch((error) => { - console.error( - 'Install @tanstack/query-devtools or reinstall without --omit=optional.', - error, - ) - }) - } else if (devtools && hostElement) { - devtools.setClient(client) - devtools.setErrorTypes(errorTypes) - onClose && devtools.setOnClose(onClose) - } else if (devtools && !hostElement) { - destroy() - } - }) - }) - - destroyRef.onDestroy(destroy) - - return { - destroy, - } - }) -} diff --git a/packages/angular-query-experimental/src/devtools-panel/production/index.ts b/packages/angular-query-experimental/src/devtools-panel/production/index.ts deleted file mode 100644 index 546becea7f8..00000000000 --- a/packages/angular-query-experimental/src/devtools-panel/production/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '..' diff --git a/packages/angular-query-experimental/src/devtools/index.ts b/packages/angular-query-experimental/src/devtools/index.ts deleted file mode 100644 index d4f7e9238f7..00000000000 --- a/packages/angular-query-experimental/src/devtools/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type { - DevtoolsOptions, - WithDevtools, - WithDevtoolsFn, - WithDevtoolsOptions, -} from './types' - -export { withDevtools } from './with-devtools' diff --git a/packages/angular-query-experimental/src/devtools/production/index.ts b/packages/angular-query-experimental/src/devtools/production/index.ts deleted file mode 100644 index 546becea7f8..00000000000 --- a/packages/angular-query-experimental/src/devtools/production/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '..' diff --git a/packages/angular-query-experimental/src/devtools/stub.ts b/packages/angular-query-experimental/src/devtools/stub.ts deleted file mode 100644 index d8090a4b28b..00000000000 --- a/packages/angular-query-experimental/src/devtools/stub.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { WithDevtools } from './types' - -// Stub which replaces `withDevtools` in production builds -export const withDevtools: WithDevtools = () => ({ - ɵkind: 'Devtools', - ɵproviders: [], -}) diff --git a/packages/angular-query-experimental/src/devtools/types.ts b/packages/angular-query-experimental/src/devtools/types.ts deleted file mode 100644 index 2dd870b3738..00000000000 --- a/packages/angular-query-experimental/src/devtools/types.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { QueryClient } from '@tanstack/query-core' -import type { - DevtoolsButtonPosition, - DevtoolsErrorType, - DevtoolsPosition, - Theme, -} from '@tanstack/query-devtools' -import type { DevtoolsFeature } from '../providers' - -/** - * Options for configuring withDevtools. - */ -export interface WithDevtoolsOptions { - /** - * An array of dependencies to be injected and passed to the `withDevtoolsFn` function. - * - * **Example** - * ```ts - * export const appConfig: ApplicationConfig = { - * providers: [ - * provideTanStackQuery( - * new QueryClient(), - * withDevtools( - * (devToolsOptionsManager: DevtoolsOptionsManager) => ({ - * loadDevtools: devToolsOptionsManager.loadDevtools(), - * }), - * { - * deps: [DevtoolsOptionsManager], - * }, - * ), - * ), - * ], - * } - * ``` - */ - deps?: Array -} - -/** - * Options for configuring the TanStack Query devtools. - */ -export interface DevtoolsOptions { - /** - * Set this true if you want the devtools to default to being open - */ - initialIsOpen?: boolean - /** - * The position of the TanStack logo to open and close the devtools panel. - * `top-left` | `top-right` | `bottom-left` | `bottom-right` | `relative` - * Defaults to `bottom-right`. - */ - buttonPosition?: DevtoolsButtonPosition - /** - * The position of the Angular Query devtools panel. - * `top` | `bottom` | `left` | `right` - * Defaults to `bottom`. - */ - position?: DevtoolsPosition - /** - * Custom instance of QueryClient - */ - client?: QueryClient - /** - * Use this so you can define custom errors that can be shown in the devtools. - */ - errorTypes?: Array - /** - * Use this to pass a nonce to the style tag that is added to the document head. This is useful if you are using a Content Security Policy (CSP) nonce to allow inline styles. - */ - styleNonce?: string - /** - * Use this so you can attach the devtool's styles to a specific element in the DOM. - */ - shadowDOMTarget?: ShadowRoot - /** - * Set this to true to hide disabled queries from the devtools panel. - */ - hideDisabledQueries?: boolean - /** - * Set this to 'light', 'dark', or 'system' to change the theme of the devtools panel. - * Defaults to 'system'. - */ - theme?: Theme - - /** - * Whether the developer tools should load. - * - `auto`- (Default) Lazily loads devtools when in development mode. Skips loading in production mode. - * - `true`- Always load the devtools, regardless of the environment. - * - `false`- Never load the devtools, regardless of the environment. - * - * You can use `true` and `false` to override loading developer tools from an environment file. - * For example, a test environment might run in production mode but you may want to load developer tools. - * - * Additionally, you can use a signal in the callback to dynamically load the devtools based on a condition. For example, - * a signal created from a RxJS observable that listens for a keyboard shortcut. - * - * **Example** - * ```ts - * withDevtools(() => ({ - * initialIsOpen: true, - * loadDevtools: inject(ExampleService).loadDevtools() - * })) - * ``` - */ - loadDevtools?: 'auto' | boolean -} - -export type WithDevtoolsFn = (...deps: Array) => DevtoolsOptions - -export type WithDevtools = ( - withDevtoolsFn?: WithDevtoolsFn, - options?: WithDevtoolsOptions, -) => DevtoolsFeature diff --git a/packages/angular-query-experimental/src/devtools/with-devtools.ts b/packages/angular-query-experimental/src/devtools/with-devtools.ts deleted file mode 100644 index 4d4b861c7f2..00000000000 --- a/packages/angular-query-experimental/src/devtools/with-devtools.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { isPlatformBrowser } from '@angular/common' -import { - DestroyRef, - ENVIRONMENT_INITIALIZER, - InjectionToken, - Injector, - PLATFORM_ID, - computed, - effect, - inject, - isDevMode, -} from '@angular/core' -import { QueryClient, noop, onlineManager } from '@tanstack/query-core' -import { queryFeature } from '../providers' -import type { Signal } from '@angular/core' -import type { - DevtoolsOptions, - WithDevtools, - WithDevtoolsFn, - WithDevtoolsOptions, -} from './types' -import type { TanstackQueryDevtools } from '@tanstack/query-devtools' - -/** - * Internal token used to prevent double providing of devtools in child injectors - */ -const DEVTOOLS_PROVIDED = new InjectionToken('', { - factory: () => ({ - isProvided: false, - }), -}) - -/** - * Internal token for providing devtools options - */ -const DEVTOOLS_OPTIONS_SIGNAL = new InjectionToken>('') - -/** - * Enables developer tools in Angular development builds. - * - * **Example** - * - * ```ts - * export const appConfig: ApplicationConfig = { - * providers: [ - * provideTanStackQuery(new QueryClient(), withDevtools()) - * ] - * } - * ``` - * The devtools will be rendered in ``. - * - * If you need more control over when devtools are loaded, you can use the `loadDevtools` option. - * - * If you need more control over where devtools are rendered, consider `injectDevtoolsPanel`. This allows rendering devtools inside your own devtools for example. - * @param withDevtoolsFn - A function that returns `DevtoolsOptions`. - * @param options - Additional options for configuring `withDevtools`. - * @returns A set of providers for use with `provideTanStackQuery`. - * @see {@link provideTanStackQuery} - * @see {@link DevtoolsOptions} - */ -export const withDevtools: WithDevtools = ( - withDevtoolsFn?: WithDevtoolsFn, - options: WithDevtoolsOptions = {}, -) => - queryFeature('Devtools', [ - { - provide: DEVTOOLS_OPTIONS_SIGNAL, - useFactory: (...deps: Array) => - computed(() => withDevtoolsFn?.(...deps) ?? {}), - deps: options.deps || [], - }, - { - // Do not use provideEnvironmentInitializer while Angular < v19 is supported - provide: ENVIRONMENT_INITIALIZER, - multi: true, - useFactory: () => { - const devtoolsProvided = inject(DEVTOOLS_PROVIDED) - if ( - !isPlatformBrowser(inject(PLATFORM_ID)) || - devtoolsProvided.isProvided - ) - return noop - - devtoolsProvided.isProvided = true - let injectorIsDestroyed = false - inject(DestroyRef).onDestroy(() => (injectorIsDestroyed = true)) - - return () => { - const injectedClient = inject(QueryClient, { - optional: true, - }) - const destroyRef = inject(DestroyRef) - const devtoolsOptions = inject(DEVTOOLS_OPTIONS_SIGNAL) - const injector = inject(Injector) - - let devtools: TanstackQueryDevtools | null = null - let el: HTMLElement | null = null - - const shouldLoadToolsSignal = computed(() => { - const { loadDevtools } = devtoolsOptions() - return typeof loadDevtools === 'boolean' - ? loadDevtools - : isDevMode() - }) - - const getResolvedQueryClient = () => { - const client = devtoolsOptions().client ?? injectedClient - if (!client) { - throw new Error('No QueryClient found') - } - return client - } - - const destroyDevtools = () => { - devtools?.unmount() - el?.remove() - devtools = null - } - - effect( - () => { - const shouldLoadTools = shouldLoadToolsSignal() - const { - client, - position, - errorTypes, - buttonPosition, - initialIsOpen, - theme, - } = devtoolsOptions() - - if (!shouldLoadTools) { - // Destroy or do nothing - devtools && destroyDevtools() - return - } - - if (devtools) { - // Update existing devtools config - client && devtools.setClient(client) - position && devtools.setPosition(position) - errorTypes && devtools.setErrorTypes(errorTypes) - buttonPosition && devtools.setButtonPosition(buttonPosition) - typeof initialIsOpen === 'boolean' && - devtools.setInitialIsOpen(initialIsOpen) - theme && devtools.setTheme(theme) - return - } - - // Create devtools - import('@tanstack/query-devtools') - .then((queryDevtools) => { - // As this code runs async, the injector could have been destroyed - if (injectorIsDestroyed) return - - devtools = new queryDevtools.TanstackQueryDevtools({ - ...devtoolsOptions(), - client: getResolvedQueryClient(), - queryFlavor: 'Angular Query', - version: '5', - onlineManager, - }) - - el = document.body.appendChild(document.createElement('div')) - el.classList.add('tsqd-parent-container') - devtools.mount(el) - - destroyRef.onDestroy(destroyDevtools) - }) - .catch((error) => { - console.error( - 'Install @tanstack/query-devtools or reinstall without --omit=optional.', - error, - ) - }) - }, - { injector }, - ) - } - }, - }, - ]) diff --git a/packages/angular-query-experimental/src/inject-infinite-query.ts b/packages/angular-query-experimental/src/inject-infinite-query.ts deleted file mode 100644 index a0413075162..00000000000 --- a/packages/angular-query-experimental/src/inject-infinite-query.ts +++ /dev/null @@ -1,296 +0,0 @@ -import { InfiniteQueryObserver } from '@tanstack/query-core' -import { - Injector, - assertInInjectionContext, - inject, - runInInjectionContext, -} from '@angular/core' -import { createBaseQuery } from './create-base-query' -import type { - DefaultError, - InfiniteData, - QueryKey, - QueryObserver, -} from '@tanstack/query-core' -import type { - CreateInfiniteQueryOptions, - CreateInfiniteQueryResult, - DefinedCreateInfiniteQueryResult, -} from './types' -import type { - DefinedInitialDataInfiniteOptions, - UndefinedInitialDataInfiniteOptions, -} from './infinite-query-options' - -export interface InjectInfiniteQueryOptions { - /** - * The `Injector` in which to create the infinite query. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * The options for `injectInfiniteQuery` are identical to `injectQuery`, with the addition of - * `initialPageParam`, `getNextPageParam`, `getPreviousPageParam`, and `maxPages`. Infinite queries can - * additively "load more" data onto an existing set of data, or "infinite scroll". - * - * This overload is selected when `initialData` is set on the options returned by `injectInfiniteQueryFn`, - * so the resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). - * - * @remarks Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default - * refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user - * actions, or add conditions like `hasNextPage() && !isFetching()`. - * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and imperative APIs - * like `queryClient.fetchInfiniteQuery`. - * @param injectInfiniteQueryFn - A function returning the {@link DefinedInitialDataInfiniteOptions} to use — - * everything you can pass to `injectInfiniteQuery`, with `initialData` set. Similar to `computed` from - * Angular, this function runs in the reactive context, so signals read inside it drive the query. - * @param options - Additional configuration. - * @returns The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, - * `hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and - * `data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default - * `InfiniteData` shape. - * - * @example - * ```angular-ts - * @Component({ - * selector: 'projects', - * template: ` - * - *
    - * @for (page of projectsQuery.data().pages; track $index) { - * @for (project of page.projects; track project.id) { - *
  • {{ project.name }}
  • - * } - * } - *
- * `, - * }) - * export class Projects { - * readonly projectsQuery = injectInfiniteQuery(() => ({ - * queryKey: ['projects'], - * queryFn: ({ pageParam }) => fetchProjects(pageParam), - * initialPageParam: 0, - * getNextPageParam: (lastPage) => lastPage.nextId, - * initialData: { pages: [], pageParams: [] }, - * })) - * } - * ``` - */ -export function injectInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, ->( - injectInfiniteQueryFn: () => DefinedInitialDataInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >, - options?: InjectInfiniteQueryOptions, -): DefinedCreateInfiniteQueryResult - -/** - * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a - * unique key. Infinite queries can additively "load more" data onto an existing set of data, or - * "infinite scroll". - * - * @remarks Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default - * refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user - * actions, or add conditions like `hasNextPage() && !isFetching()`. This is the only overload that accepts - * `queryFn: skipToken`, shown below. - * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and imperative APIs - * like `queryClient.fetchInfiniteQuery`. - * @param injectInfiniteQueryFn - A function returning the {@link UndefinedInitialDataInfiniteOptions} to use - * — everything you can pass to `injectInfiniteQuery`. Similar to `computed` from Angular, this function runs - * in the reactive context, so signals read inside it drive the query. - * @param options - Additional configuration. - * @returns The same signals as `injectQuery`, with the addition of `fetchNextPage`, `fetchPreviousPage`, - * `hasNextPage`, `hasPreviousPage`, `isFetchingNextPage`, and `isFetchingPreviousPage`. `data().pages` and - * `data().pageParams` are also added, as long as a `select` doesn't change `TData` away from its default - * `InfiniteData` shape. - * - * @example - * Fetching the next page from a button click: - * ```angular-ts - * @Component({ - * selector: 'projects-list', - * template: ` - *
    - * @for (page of projectsQuery.data()?.pages; track $index) { - * @for (project of page.projects; track project.id) { - *
  • {{ project.name }}
  • - * } - * } - *
- * - * `, - * }) - * export class ProjectsList { - * readonly projectsQuery = injectInfiniteQuery(() => ({ - * queryKey: ['projects'], - * queryFn: ({ pageParam }) => fetchProjects(pageParam), - * initialPageParam: 0, - * getNextPageParam: (lastPage) => lastPage.nextId, - * })) - * } - * ``` - * - * @example - * Fetching the next page automatically as the user scrolls, using an `IntersectionObserver` on a sentinel - * element after the list: - * ```angular-ts - * @Component({ - * selector: 'projects-list', - * template: ` - *
    - * @for (page of projectsQuery.data()?.pages; track $index) { - * @for (project of page.projects; track project.id) { - *
  • {{ project.name }}
  • - * } - * } - *
- *
{{ projectsQuery.isFetchingNextPage() ? 'Loading more...' : '' }}
- * `, - * }) - * export class ProjectsList { - * readonly sentinel = viewChild>('sentinel') - * - * readonly projectsQuery = injectInfiniteQuery(() => ({ - * queryKey: ['projects'], - * queryFn: ({ pageParam }) => fetchProjects(pageParam), - * initialPageParam: 0, - * getNextPageParam: (lastPage) => lastPage.nextId, - * })) - * - * constructor() { - * effect((onCleanup) => { - * const sentinel = this.sentinel()?.nativeElement - * if ( - * sentinel == null || - * !this.projectsQuery.hasNextPage() || - * this.projectsQuery.isFetching() - * ) { - * return - * } - * - * const observer = new IntersectionObserver(([entry]) => { - * if (entry?.isIntersecting) this.projectsQuery.fetchNextPage() - * }) - * observer.observe(sentinel) - * - * onCleanup(() => observer.disconnect()) - * }) - * } - * } - * ``` - * - * @example - * A query that's disabled, type safe, until `postId` is set — pass `skipToken` as `queryFn` instead of - * setting `enabled: false`: - * ```angular-ts - * @Component({ - * selector: 'comments', - * template: ` - * @if (postId() == null) { - * Select a post - * } @else if (commentsQuery.isPending()) { - * Loading... - * } @else if (commentsQuery.isError()) { - * Error: {{ commentsQuery.error()?.message }} - * } @else { - *
    - * @for (page of commentsQuery.data().pages; track $index) { - * @for (comment of page.comments; track comment.id) { - *
  • {{ comment.text }}
  • - * } - * } - *
- * } - * `, - * }) - * export class Comments { - * readonly postId = signal(undefined) - * - * readonly commentsQuery = injectInfiniteQuery(() => ({ - * queryKey: ['post', this.postId(), 'comments'], - * queryFn: - * this.postId() != null - * ? ({ pageParam }) => fetchComments(this.postId()!, pageParam) - * : skipToken, - * initialPageParam: 0, - * getNextPageParam: (lastPage) => lastPage.nextId, - * })) - * } - * ``` - */ -export function injectInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, ->( - injectInfiniteQueryFn: () => UndefinedInitialDataInfiniteOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >, - options?: InjectInfiniteQueryOptions, -): CreateInfiniteQueryResult - -/** - * This overload accepts the general {@link CreateInfiniteQueryOptions} shape rather than the - * `initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call site — - * useful when wrapping `injectInfiniteQuery` in your own helper function that forwards caller-provided - * options. - * - * @param injectInfiniteQueryFn - A function that returns infinite query options. Similar to `computed` from - * Angular, this function runs in the reactive context, so signals read inside it drive the query. - * @param options - Additional configuration. - * @returns The infinite query result. - */ -export function injectInfiniteQuery< - TQueryFnData, - TError = DefaultError, - TData = InfiniteData, - TQueryKey extends QueryKey = QueryKey, - TPageParam = unknown, ->( - injectInfiniteQueryFn: () => CreateInfiniteQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey, - TPageParam - >, - options?: InjectInfiniteQueryOptions, -): CreateInfiniteQueryResult - -export function injectInfiniteQuery( - injectInfiniteQueryFn: () => CreateInfiniteQueryOptions, - options?: InjectInfiniteQueryOptions, -) { - !options?.injector && assertInInjectionContext(injectInfiniteQuery) - const injector = options?.injector ?? inject(Injector) - return runInInjectionContext(injector, () => - createBaseQuery( - injectInfiniteQueryFn, - InfiniteQueryObserver as typeof QueryObserver, - ), - ) -} diff --git a/packages/angular-query-experimental/src/inject-is-fetching.ts b/packages/angular-query-experimental/src/inject-is-fetching.ts deleted file mode 100644 index 5517fc29849..00000000000 --- a/packages/angular-query-experimental/src/inject-is-fetching.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - DestroyRef, - Injector, - NgZone, - assertInInjectionContext, - inject, - signal, -} from '@angular/core' -import { QueryClient, notifyManager } from '@tanstack/query-core' -import type { QueryFilters } from '@tanstack/query-core' -import type { Signal } from '@angular/core' - -export interface InjectIsFetchingOptions { - /** - * The `Injector` in which to create the isFetching signal. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * Injects a signal that tracks the number of queries that your application is loading or fetching in the - * background (useful for app-wide loading indicators). - * - * @param filters - The {@link QueryFilters} to narrow down the matched queries. - * @param options - Additional configuration - * @returns A `Signal` with the number of queries that your application is currently loading or fetching in - * the background. - * - * @example - * ```angular-ts - * @Component({ - * selector: 'posts-fetching-indicator', - * template: ` - * @if (isFetchingPosts()) { - * Refreshing posts... - * } - * `, - * }) - * export class PostsFetchingIndicator { - * // How many queries matching the posts prefix are fetching? - * readonly isFetchingPosts = injectIsFetching({ queryKey: ['posts'] }) - * } - * ``` - * - * @example - * A global loading indicator for any query fetching in the background, not just the ones on screen: - * ```angular-ts - * @Component({ - * selector: 'global-loading-indicator', - * template: ` - * @if (isFetching()) { - *
Queries are fetching in the background...
- * } - * `, - * }) - * export class GlobalLoadingIndicator { - * readonly isFetching = injectIsFetching() - * } - * ``` - */ -export function injectIsFetching( - filters?: QueryFilters, - options?: InjectIsFetchingOptions, -): Signal { - !options?.injector && assertInInjectionContext(injectIsFetching) - const injector = options?.injector ?? inject(Injector) - const destroyRef = injector.get(DestroyRef) - const ngZone = injector.get(NgZone) - const queryClient = injector.get(QueryClient) - - const cache = queryClient.getQueryCache() - // isFetching is the prev value initialized on mount * - let isFetching = queryClient.isFetching(filters) - - const result = signal(isFetching) - - const unsubscribe = ngZone.runOutsideAngular(() => - cache.subscribe( - notifyManager.batchCalls(() => { - const newIsFetching = queryClient.isFetching(filters) - if (isFetching !== newIsFetching) { - // * and update with each change - isFetching = newIsFetching - ngZone.run(() => { - result.set(isFetching) - }) - } - }), - ), - ) - - destroyRef.onDestroy(unsubscribe) - - return result -} diff --git a/packages/angular-query-experimental/src/inject-is-mutating.ts b/packages/angular-query-experimental/src/inject-is-mutating.ts deleted file mode 100644 index 42a41ad0e7a..00000000000 --- a/packages/angular-query-experimental/src/inject-is-mutating.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - DestroyRef, - Injector, - NgZone, - assertInInjectionContext, - inject, - signal, -} from '@angular/core' -import { QueryClient, notifyManager } from '@tanstack/query-core' -import type { MutationFilters } from '@tanstack/query-core' -import type { Signal } from '@angular/core' - -export interface InjectIsMutatingOptions { - /** - * The `Injector` in which to create the isMutating signal. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * Injects a signal that tracks the number of mutations that your application currently has `pending` - * (useful for app-wide loading indicators). - * - * @param filters - The {@link MutationFilters} to narrow down the matched mutations. - * @param options - Additional configuration - * @returns A `Signal` with the number of mutations that your application currently has `pending`. - * - * @example - * ```angular-ts - * @Component({ - * selector: 'posts-mutating-indicator', - * template: ` - * @if (isMutatingPosts()) { - * Saving posts... - * } - * `, - * }) - * export class PostsMutatingIndicator { - * // How many mutations matching the posts prefix are in progress? - * readonly isMutatingPosts = injectIsMutating({ mutationKey: ['posts'] }) - * } - * ``` - */ -export function injectIsMutating( - filters?: MutationFilters, - options?: InjectIsMutatingOptions, -): Signal { - !options?.injector && assertInInjectionContext(injectIsMutating) - const injector = options?.injector ?? inject(Injector) - const destroyRef = injector.get(DestroyRef) - const ngZone = injector.get(NgZone) - const queryClient = injector.get(QueryClient) - - const cache = queryClient.getMutationCache() - // isMutating is the prev value initialized on mount * - let isMutating = queryClient.isMutating(filters) - - const result = signal(isMutating) - - const unsubscribe = ngZone.runOutsideAngular(() => - cache.subscribe( - notifyManager.batchCalls(() => { - const newIsMutating = queryClient.isMutating(filters) - if (isMutating !== newIsMutating) { - // * and update with each change - isMutating = newIsMutating - ngZone.run(() => { - result.set(isMutating) - }) - } - }), - ), - ) - - destroyRef.onDestroy(unsubscribe) - - return result.asReadonly() -} diff --git a/packages/angular-query-experimental/src/inject-is-restoring.ts b/packages/angular-query-experimental/src/inject-is-restoring.ts deleted file mode 100644 index 901f5e55852..00000000000 --- a/packages/angular-query-experimental/src/inject-is-restoring.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - InjectionToken, - Injector, - assertInInjectionContext, - inject, - signal, -} from '@angular/core' -import type { Provider, Signal } from '@angular/core' - -/** - * Internal token used to track isRestoring state, accessible in public API through `injectIsRestoring` and set via `provideIsRestoring` - */ -const IS_RESTORING = new InjectionToken('', { - // Default value when not provided - factory: () => signal(false).asReadonly(), -}) - -interface InjectIsRestoringOptions { - /** - * The `Injector` to use to get the isRestoring signal. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * Injects a signal that tracks whether a restore (e.g. from a persisted client, wired up via - * `provideIsRestoring`) is currently in progress. `injectQuery` and friends also check this internally to - * avoid race conditions between the restore and initializing queries. - * @param options - Additional configuration - * @returns A readonly `Signal` — `true` while a restore is in progress, `false` otherwise (the - * default when no `provideIsRestoring` provider is registered). - */ -export function injectIsRestoring(options?: InjectIsRestoringOptions) { - !options?.injector && assertInInjectionContext(injectIsRestoring) - const injector = options?.injector ?? inject(Injector) - return injector.get(IS_RESTORING) -} - -/** - * Registers a provider for the restore state read by `injectIsRestoring`. Wire this up wherever you drive a - * restore yourself — e.g. a persist-client integration — so `injectQuery` and friends can defer subscribing - * to their observer (avoiding a race with the restore) until the restore signal flips back to `false`. - * @param isRestoring - A readonly `Signal` that tracks the restore state. - * @returns A provider for the `isRestoring` signal. - */ -export function provideIsRestoring(isRestoring: Signal): Provider { - return { - provide: IS_RESTORING, - useValue: isRestoring, - } -} diff --git a/packages/angular-query-experimental/src/inject-mutation-state.ts b/packages/angular-query-experimental/src/inject-mutation-state.ts deleted file mode 100644 index 3a1dcf7beee..00000000000 --- a/packages/angular-query-experimental/src/inject-mutation-state.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { - DestroyRef, - Injector, - NgZone, - assertInInjectionContext, - computed, - inject, - signal, -} from '@angular/core' -import { - QueryClient, - notifyManager, - replaceEqualDeep, -} from '@tanstack/query-core' -import type { Signal } from '@angular/core' -import type { - Mutation, - MutationCache, - MutationFilters, - MutationState, -} from '@tanstack/query-core' - -type MutationStateOptions = { - filters?: MutationFilters - select?: (mutation: Mutation) => TResult -} - -function getResult( - mutationCache: MutationCache, - options: MutationStateOptions, -): Array { - return mutationCache - .findAll(options.filters) - .map( - (mutation): TResult => - (options.select ? options.select(mutation) : mutation.state) as TResult, - ) -} - -export interface InjectMutationStateOptions { - /** - * The `Injector` in which to create the mutation state signal. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * Injects a signal that gives you access to all mutations in the `MutationCache`. You can pass `filters` - * ({@link MutationFilters}) to narrow down your mutations, and `select` to transform the mutation state. - * - * @param injectMutationStateFn - A function returning the `filters` to narrow down matched mutations, and an - * optional `select` to transform the mutation state. Similar to `computed` from Angular, this function runs - * in the reactive context, so signals read inside it re-narrow the matched mutations. - * @param options - Additional configuration - * @returns A `Signal` with an Array of whatever `select` returns for each matching mutation. - * - * @example - * Get all variables of all running mutations: - * ```angular-ts - * @Component({ - * selector: 'pending-posts', - * template: `{{ pendingVariables().length }} posts saving...`, - * }) - * export class PendingPosts { - * readonly pendingVariables = injectMutationState(() => ({ - * filters: { status: 'pending' }, - * select: (mutation) => mutation.state.variables, - * })) - * } - * ``` - * - * @example - * Get all data for specific mutations via the `mutationKey`: - * ```angular-ts - * const mutationKey = ['posts'] - * - * @Component({ - * selector: 'posts', - * template: ` - * - * `, - * }) - * export class Posts { - * // Some mutation that we want to get the state for - * readonly createPostMutation = injectMutation(() => ({ - * mutationKey, - * mutationFn: createPosts, - * })) - * - * readonly savedPosts = injectMutationState(() => ({ - * // this mutation key needs to match the mutation key of the given mutation (see above) - * filters: { mutationKey, status: 'success' }, - * select: (mutation) => mutation.state.data, - * })) - * - * createPost() { - * this.createPostMutation.mutate(['New Post']) - * } - * } - * ``` - */ -export function injectMutationState( - injectMutationStateFn: () => MutationStateOptions = () => ({}), - options?: InjectMutationStateOptions, -): Signal> { - !options?.injector && assertInInjectionContext(injectMutationState) - const injector = options?.injector ?? inject(Injector) - const destroyRef = injector.get(DestroyRef) - const ngZone = injector.get(NgZone) - const queryClient = injector.get(QueryClient) - const mutationCache = queryClient.getMutationCache() - - /** - * Computed signal that gets result from mutation cache based on passed options - * First element is the result, second element is the time when the result was set - */ - const resultFromOptionsSignal = computed(() => { - return [ - getResult(mutationCache, injectMutationStateFn()), - performance.now(), - ] as const - }) - - /** - * Signal that contains result set by subscriber - * First element is the result, second element is the time when the result was set - */ - const resultFromSubscriberSignal = signal<[Array, number] | null>( - null, - ) - - /** - * Returns the last result by either subscriber or options - */ - const effectiveResultSignal = computed(() => { - const optionsResult = resultFromOptionsSignal() - const subscriberResult = resultFromSubscriberSignal() - return subscriberResult && subscriberResult[1] > optionsResult[1] - ? subscriberResult[0] - : optionsResult[0] - }) - - const unsubscribe = ngZone.runOutsideAngular(() => - mutationCache.subscribe( - notifyManager.batchCalls(() => { - const [lastResult] = effectiveResultSignal() - const nextResult = replaceEqualDeep( - lastResult, - getResult(mutationCache, injectMutationStateFn()), - ) - if (lastResult !== nextResult) { - ngZone.run(() => { - resultFromSubscriberSignal.set([nextResult, performance.now()]) - }) - } - }), - ), - ) - - destroyRef.onDestroy(unsubscribe) - - return effectiveResultSignal -} diff --git a/packages/angular-query-experimental/src/inject-mutation.ts b/packages/angular-query-experimental/src/inject-mutation.ts deleted file mode 100644 index 6fac7e5e73e..00000000000 --- a/packages/angular-query-experimental/src/inject-mutation.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { - Injector, - NgZone, - assertInInjectionContext, - computed, - effect, - inject, - signal, - untracked, -} from '@angular/core' -import { - MutationObserver, - QueryClient, - noop, - notifyManager, - shouldThrowError, -} from '@tanstack/query-core' -import { signalProxy } from './signal-proxy' -import { PENDING_TASKS } from './pending-tasks-compat' -import type { PendingTaskRef } from './pending-tasks-compat' -import type { DefaultError, MutationObserverResult } from '@tanstack/query-core' -import type { - CreateMutateFunction, - CreateMutationOptions, - CreateMutationResult, -} from './types' - -export interface InjectMutationOptions { - /** - * The `Injector` in which to create the mutation. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - -/** - * Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects. - * `injectMutation` is the function for that. Unlike queries, mutations are not run automatically. - * - * @remarks `mutate`/`mutateAsync` also accept per-call `onSuccess`/`onError`/`onSettled` callbacks as a - * second argument, useful for triggering call-site side effects (e.g. navigation) without coupling them to - * the shared mutation definition. Callbacks defined in `injectMutationFn` fire for every mutation; per-call - * callbacks fire only for the latest call you've made — `mutateAsync` gives you a promise per call instead, - * so you can await `Promise.all`/`Promise.allSettled` over several calls and see each one's outcome. - * @see {@link mutationOptions} to share these options across multiple `injectMutation` call sites, or to look - * the mutation up elsewhere via its `mutationKey` (e.g. with `injectMutationState`). - * @param injectMutationFn - A function that returns mutation options. Similar to `computed` from Angular, - * this function runs in the reactive context, so signals read inside it drive the mutation's options. - * @param options - Additional configuration - * @returns The mutation result. Value fields are exposed as a `Signal` — read `data`/`error` by calling them - * (e.g. `mutation.data()`) — while function fields (`mutate`, `mutateAsync`, `reset`) are called directly, - * unchanged. `isSuccess`/`isError`/`isPending`/`isIdle` are type-guard methods you can call to narrow whether - * `data` is defined. - * - * @example - * ```angular-ts - * @Component({ - * selector: 'todos', - * template: ` - * @if (addMutation.isPending()) { - * Adding todo... - * } @else if (addMutation.isError()) { - *
An error occurred: {{ addMutation.error()?.message }}
- * } - * - * `, - * }) - * export class Todos { - * readonly #queryClient = inject(QueryClient) - * - * readonly addMutation = injectMutation(() => ({ - * mutationFn: addTodo, - * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), - * })) - * } - * ``` - * - * @example - * Optimistic update via `onMutate`, rolling back on `onError`: - * ```angular-ts - * @Component({ - * selector: 'todos', - * template: ``, - * }) - * export class Todos { - * readonly #queryClient = inject(QueryClient) - * - * readonly addMutation = injectMutation(() => ({ - * mutationFn: addTodo, - * onMutate: async (newTodo) => { - * await this.#queryClient.cancelQueries({ queryKey: ['todos'] }) - * const previousTodos = this.#queryClient.getQueryData>(['todos']) - * - * this.#queryClient.setQueryData>(['todos'], (old) => [ - * ...(old ?? []), - * newTodo, - * ]) - * - * // Passed to `onError` as `onMutateResult` if the mutation fails. - * return { previousTodos } - * }, - * onError: (_err, _newTodo, onMutateResult) => { - * this.#queryClient.setQueryData(['todos'], onMutateResult?.previousTodos) - * }, - * onSettled: () => { - * this.#queryClient.invalidateQueries({ queryKey: ['todos'] }) - * }, - * })) - * } - * ``` - * - * @example - * Callbacks passed per call to `mutate` only fire for the last call — `mutateAsync` gives you a promise per - * call instead, so you can wait for all of them when they succeed: - * ```angular-ts - * @Component({ - * selector: 'todos', - * template: ` - * - * `, - * }) - * export class Todos { - * readonly #queryClient = inject(QueryClient) - * - * readonly addMutation = injectMutation(() => ({ - * mutationFn: addTodo, - * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), - * })) - * - * async handleAddAll(todos: Array) { - * try { - * await Promise.all(todos.map((todo) => this.addMutation.mutateAsync(todo))) - * } catch (error) { - * console.error('Failed to add todos:', error) - * } - * } - * } - * ``` - * - * @example - * If some of the mutations above can fail independently of the others, and you want to know which ones did — - * rather than losing that information the moment the first one rejects — swap `Promise.all` for - * `Promise.allSettled`: - * ```angular-ts - * @Component({ - * selector: 'todos', - * template: ` - * - * `, - * }) - * export class Todos { - * readonly #queryClient = inject(QueryClient) - * - * readonly addMutation = injectMutation(() => ({ - * mutationFn: addTodo, - * onSuccess: () => this.#queryClient.invalidateQueries({ queryKey: ['todos'] }), - * })) - * - * async handleAddAll(todos: Array) { - * const addResults = await Promise.allSettled( - * todos.map((todo) => this.addMutation.mutateAsync(todo)), - * ) - * - * addResults.forEach((addResult, index) => { - * if (addResult.status === 'rejected') { - * console.error(`Failed to add "${todos[index]}":`, addResult.reason) - * } - * }) - * } - * } - * ``` - */ -export function injectMutation< - TData = unknown, - TError = DefaultError, - TVariables = void, - TOnMutateResult = unknown, ->( - injectMutationFn: () => CreateMutationOptions< - TData, - TError, - TVariables, - TOnMutateResult - >, - options?: InjectMutationOptions, -): CreateMutationResult { - !options?.injector && assertInInjectionContext(injectMutation) - const injector = options?.injector ?? inject(Injector) - const ngZone = injector.get(NgZone) - const pendingTasks = injector.get(PENDING_TASKS) - const queryClient = injector.get(QueryClient) - - /** - * computed() is used so signals can be inserted into the options - * making it reactive. Wrapping options in a function ensures embedded expressions - * are preserved and can keep being applied after signal changes - */ - const optionsSignal = computed(injectMutationFn) - - const observerSignal = (() => { - let instance: MutationObserver< - TData, - TError, - TVariables, - TOnMutateResult - > | null = null - - return computed(() => { - return (instance ||= new MutationObserver(queryClient, optionsSignal())) - }) - })() - - const mutateFnSignal = computed< - CreateMutateFunction - >(() => { - const observer = observerSignal() - return ( - ...args: Parameters< - CreateMutateFunction - > - ) => { - observer.mutate(args[0] as TVariables, args[1]).catch(noop) - } - }) - - /** - * Computed signal that gets result from mutation cache based on passed options - */ - const resultFromInitialOptionsSignal = computed(() => { - const observer = observerSignal() - return observer.getCurrentResult() - }) - - /** - * Signal that contains result set by subscriber - */ - const resultFromSubscriberSignal = signal | null>(null) - - effect( - () => { - const observer = observerSignal() - const observerOptions = optionsSignal() - - untracked(() => { - observer.setOptions(observerOptions) - }) - }, - { - injector, - }, - ) - - effect( - (onCleanup) => { - // observer.trackResult is not used as this optimization is not needed for Angular - const observer = observerSignal() - let pendingTaskRef: PendingTaskRef | null = null - - untracked(() => { - const unsubscribe = ngZone.runOutsideAngular(() => - observer.subscribe( - notifyManager.batchCalls((state) => { - ngZone.run(() => { - // Track pending task when mutation is pending - if (state.isPending && !pendingTaskRef) { - pendingTaskRef = pendingTasks.add() - } - - // Clear pending task when mutation is no longer pending - if (!state.isPending && pendingTaskRef) { - pendingTaskRef() - pendingTaskRef = null - } - - if ( - state.isError && - shouldThrowError(observer.options.throwOnError, [state.error]) - ) { - ngZone.onError.emit(state.error) - throw state.error - } - - resultFromSubscriberSignal.set(state) - }) - }), - ), - ) - onCleanup(() => { - // Clean up any pending task on destroy - if (pendingTaskRef) { - pendingTaskRef() - pendingTaskRef = null - } - unsubscribe() - }) - }) - }, - { - injector, - }, - ) - - const resultSignal = computed(() => { - const resultFromSubscriber = resultFromSubscriberSignal() - const resultFromInitialOptions = resultFromInitialOptionsSignal() - - const result = resultFromSubscriber ?? resultFromInitialOptions - - return { - ...result, - mutate: mutateFnSignal(), - mutateAsync: result.mutate, - } - }) - - return signalProxy(resultSignal) as CreateMutationResult< - TData, - TError, - TVariables, - TOnMutateResult - > -} diff --git a/packages/angular-query-experimental/src/inject-queries-experimental/index.ts b/packages/angular-query-experimental/src/inject-queries-experimental/index.ts deleted file mode 100644 index d300d7d2a55..00000000000 --- a/packages/angular-query-experimental/src/inject-queries-experimental/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '../inject-queries' diff --git a/packages/angular-query-experimental/src/inject-queries.ts b/packages/angular-query-experimental/src/inject-queries.ts deleted file mode 100644 index 47d692716d8..00000000000 --- a/packages/angular-query-experimental/src/inject-queries.ts +++ /dev/null @@ -1,479 +0,0 @@ -import { - QueriesObserver, - QueryClient, - notifyManager, -} from '@tanstack/query-core' -import { - DestroyRef, - Injector, - NgZone, - assertInInjectionContext, - computed, - effect, - inject, - runInInjectionContext, - signal, - untracked, -} from '@angular/core' -import { signalProxy } from './signal-proxy' -import { injectIsRestoring } from './inject-is-restoring' -import type { - DefaultError, - OmitKeyof, - QueriesObserverOptions, - QueriesPlaceholderDataFunction, - QueryFunction, - QueryKey, - QueryObserverOptions, - ThrowOnError, -} from '@tanstack/query-core' -import type { - CreateQueryOptions, - CreateQueryResult, - DefinedCreateQueryResult, -} from './types' -import type { Signal } from '@angular/core' - -// This defines the `CreateQueryOptions` that are accepted in `QueriesOptions` & `GetOptions`. -// `placeholderData` function always gets undefined passed -type QueryObserverOptionsForCreateQueries< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> = OmitKeyof< - CreateQueryOptions, - 'placeholderData' -> & { - placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction -} - -// Avoid TS depth-limit error in case of large array literal -type MAXIMUM_DEPTH = 20 - -// Widen the type of the symbol to enable type inference even if skipToken is not immutable. -type SkipTokenForCreateQueries = symbol - -type GetCreateQueryOptionsForCreateQueries = - // Part 1: responsible for applying explicit type parameter to function arguments, if object { queryFnData: TQueryFnData, error: TError, data: TData } - T extends { - queryFnData: infer TQueryFnData - error?: infer TError - data: infer TData - } - ? QueryObserverOptionsForCreateQueries - : T extends { queryFnData: infer TQueryFnData; error?: infer TError } - ? QueryObserverOptionsForCreateQueries - : T extends { data: infer TData; error?: infer TError } - ? QueryObserverOptionsForCreateQueries - : // Part 2: responsible for applying explicit type parameter to function arguments, if tuple [TQueryFnData, TError, TData] - T extends [infer TQueryFnData, infer TError, infer TData] - ? QueryObserverOptionsForCreateQueries - : T extends [infer TQueryFnData, infer TError] - ? QueryObserverOptionsForCreateQueries - : T extends [infer TQueryFnData] - ? QueryObserverOptionsForCreateQueries - : // Part 3: responsible for inferring and enforcing type if no explicit parameter was provided - T extends { - queryFn?: - | QueryFunction - | SkipTokenForCreateQueries - select?: (data: any) => infer TData - throwOnError?: ThrowOnError - } - ? QueryObserverOptionsForCreateQueries< - TQueryFnData, - unknown extends TError ? DefaultError : TError, - unknown extends TData ? TQueryFnData : TData, - TQueryKey - > - : // Fallback - QueryObserverOptionsForCreateQueries - -// A defined initialData setting should return a DefinedCreateQueryResult rather than CreateQueryResult -type GetDefinedOrUndefinedQueryResult = T extends { - initialData?: infer TInitialData -} - ? unknown extends TInitialData - ? CreateQueryResult - : TInitialData extends TData - ? DefinedCreateQueryResult - : TInitialData extends () => infer TInitialDataResult - ? unknown extends TInitialDataResult - ? CreateQueryResult - : TInitialDataResult extends TData - ? DefinedCreateQueryResult - : CreateQueryResult - : CreateQueryResult - : CreateQueryResult - -type GetCreateQueryResult = - // Part 1: responsible for mapping explicit type parameter to function result, if object - T extends { queryFnData: any; error?: infer TError; data: infer TData } - ? GetDefinedOrUndefinedQueryResult - : T extends { queryFnData: infer TQueryFnData; error?: infer TError } - ? GetDefinedOrUndefinedQueryResult - : T extends { data: infer TData; error?: infer TError } - ? GetDefinedOrUndefinedQueryResult - : // Part 2: responsible for mapping explicit type parameter to function result, if tuple - T extends [any, infer TError, infer TData] - ? GetDefinedOrUndefinedQueryResult - : T extends [infer TQueryFnData, infer TError] - ? GetDefinedOrUndefinedQueryResult - : T extends [infer TQueryFnData] - ? GetDefinedOrUndefinedQueryResult - : // Part 3: responsible for mapping inferred type to results, if no explicit parameter was provided - T extends { - queryFn?: - | QueryFunction - | SkipTokenForCreateQueries - select?: (data: any) => infer TData - throwOnError?: ThrowOnError - } - ? GetDefinedOrUndefinedQueryResult< - T, - unknown extends TData ? TQueryFnData : TData, - unknown extends TError ? DefaultError : TError - > - : // Fallback - CreateQueryResult - -/** - * The `queries` array accepted by `injectQueries`. Recursively unwraps each tuple element so every entry's - * `queryFn`/`select`/`throwOnError` are inferred individually, up to 20 elements — past that, tuple - * recursion falls back to a single homogeneous options type. An opaque array (e.g. `unknown[]`) is returned - * as-is; a non-tuple array of a known element type is mapped to that element type instead, with no such - * limit. - * - * @template T - The type of the `queries` array as written at the call site. - * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to - * be set explicitly. - * @template TDepth - The internal recursion-depth counter, checked against the 20-element limit. It is not - * meant to be set explicitly. - */ -export type QueriesOptions< - T extends Array, - TResults extends Array = [], - TDepth extends ReadonlyArray = [], -> = TDepth['length'] extends MAXIMUM_DEPTH - ? Array - : T extends [] - ? [] - : T extends [infer Head] - ? [...TResults, GetCreateQueryOptionsForCreateQueries] - : T extends [infer Head, ...infer Tails] - ? QueriesOptions< - [...Tails], - [...TResults, GetCreateQueryOptionsForCreateQueries], - [...TDepth, 1] - > - : ReadonlyArray extends T - ? T - : // If T is *some* array but we couldn't assign unknown[] to it, then it must hold some known/homogeneous type! - // use this to infer the param types in the case of Array.map() argument - T extends Array< - QueryObserverOptionsForCreateQueries< - infer TQueryFnData, - infer TError, - infer TData, - infer TQueryKey - > - > - ? Array< - QueryObserverOptionsForCreateQueries< - TQueryFnData, - TError, - TData, - TQueryKey - > - > - : // Fallback - Array - -/** - * The result type returned by `injectQueries`, when no `combine` is provided. Mirrors {@link QueriesOptions}: - * each tuple element's result type is inferred individually, up to 20 elements — past that, tuple recursion - * falls back to a single homogeneous {@link CreateQueryResult} type. A non-tuple array is mapped per-element - * instead, with no such limit — every entry keeps its individually inferred type regardless of array length. - * - * @template T - The type of the `queries` array, as inferred by {@link QueriesOptions}. - * @template TResults - The internal accumulator that this type builds during recursion. It is not meant to - * be set explicitly. - * @template TDepth - The internal recursion-depth counter, checked against the 20-element limit. It is not - * meant to be set explicitly. - */ -export type QueriesResults< - T extends Array, - TResults extends Array = [], - TDepth extends ReadonlyArray = [], -> = TDepth['length'] extends MAXIMUM_DEPTH - ? Array - : T extends [] - ? [] - : T extends [infer Head] - ? [...TResults, GetCreateQueryResult] - : T extends [infer Head, ...infer Tails] - ? QueriesResults< - [...Tails], - [...TResults, GetCreateQueryResult], - [...TDepth, 1] - > - : { [K in keyof T]: GetCreateQueryResult } - -export interface InjectQueriesOptions< - T extends Array, - TCombinedResult = QueriesResults, -> { - queries: - | readonly [...QueriesOptions] - | readonly [ - ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueries }, - ] - combine?: (result: QueriesResults) => TCombinedResult -} - -/** - * Injects a signal to fetch a variable number of queries. - * - * The `queries` key accepts an array with query option objects mostly identical to `injectQuery`'s. Having - * the same query key more than once in the array of query objects may cause some data to be shared between - * queries. To avoid this, consider de-duplicating the queries and map the results back to the desired - * structure. - * - * The `combine` option can be used to combine the results of the queries into a single value. The result - * will be structurally shared to be as referentially stable as possible. - * - * @remarks Unlike `injectQuery`, `injectQueries` cannot infer the `data` argument of an _inline_ `select` - * from its sibling `queryFn`. Because `injectQueries` infers the type of the whole `queries` array at once, - * the `select` parameter of a query object written inline cannot be contextually typed from that same - * object's `queryFn`, so it falls back to `unknown` — a - * [known TypeScript limitation](https://github.com/TanStack/query/issues/6556). Annotate the `select` - * parameter explicitly, or define the query with {@link queryOptions}, which resolves its types in a single - * object _before_ it reaches `injectQueries`, to work around this — see the example below. - * @param optionsFn - A function returning the queries' options — an array of query option objects under - * `queries`, and an optional `combine`. Similar to `computed` from Angular, this function runs in the - * reactive context, so signals read inside it (e.g. to build the `queries` array) drive the queries. - * @param injector - The `Injector` in which to create the queries. If this is not provided, the current - * injection context will be used instead (via `inject`). - * @returns A `Signal` with the combined result. Without `combine`, this is an array with all the query - * results, in the same order as the input. When `combine` is provided, this is the value returned by - * `combine` instead. - * - * @example - * ```angular-ts - * @Component({ - * selector: 'posts', - * template: ` - *
    - * @for (query of postQueries(); track $index) { - * @if (query.isPending()) { - *
  • Loading...
  • - * } @else if (query.isError()) { - *
  • Error: {{ query.error()?.message }}
  • - * } @else { - *
  • {{ query.data().title }}
  • - * } - * } - *
- * `, - * }) - * export class Posts { - * readonly ids = signal([1, 2, 3]) - * - * readonly postQueries = injectQueries(() => ({ - * queries: this.ids().map((id) => ({ - * queryKey: ['post', id], - * queryFn: () => fetchPost(id), - * staleTime: Infinity, - * })), - * })) - * } - * ``` - * - * @example - * Combining results into a single value: - * ```angular-ts - * @Component({ - * selector: 'posts', - * template: ` - * @if (combined().isPending) { - * Loading... - * } @else if (combined().isError) { - * Error loading posts - * } @else { - *
    - * @for (post of combined().data; track post?.id) { - *
  • {{ post?.title }}
  • - * } - *
- * } - * `, - * }) - * export class Posts { - * readonly ids = signal([1, 2, 3]) - * - * readonly combined = injectQueries(() => ({ - * queries: this.ids().map((id) => ({ - * queryKey: ['post', id], - * queryFn: () => fetchPost(id), - * })), - * combine: (postQueries) => ({ - * data: postQueries.map((query) => query.data), - * isPending: postQueries.some((query) => query.isPending), - * isError: postQueries.some((query) => query.isError), - * }), - * })) - * } - * ``` - * - * @example - * Typing `select` via {@link queryOptions}. Note that spreading a `queryOptions` result and overriding - * `select` inline still falls back to `unknown` — wrap the spread in `queryOptions` again so the override is - * resolved before it reaches `injectQueries`: - * ```angular-ts - * const postOptions = (id: number) => - * queryOptions({ - * queryKey: ['post', id], - * queryFn: () => fetchPost(id), - * }) - * - * @Component({ - * selector: 'post-title', - * template: `

{{ fixed()[0].data() }}

`, - * }) - * export class PostTitle { - * readonly id = signal(1) - * - * readonly broken = injectQueries(() => ({ - * queries: [ - * { - * ...postOptions(this.id()), - * // ❌ `data` is `unknown` here - * select: (data) => data.title, - * }, - * ], - * })) - * - * readonly fixed = injectQueries(() => ({ - * queries: [ - * queryOptions({ - * ...postOptions(this.id()), - * // ✅ `data` is `Post` - * select: (data) => data.title, - * }), - * ], - * })) - * } - * ``` - */ -export function injectQueries< - T extends Array, - TCombinedResult = QueriesResults, ->( - optionsFn: () => InjectQueriesOptions, - injector?: Injector, -): Signal { - !injector && assertInInjectionContext(injectQueries) - return runInInjectionContext(injector ?? inject(Injector), () => { - const destroyRef = inject(DestroyRef) - const ngZone = inject(NgZone) - const queryClient = inject(QueryClient) - const isRestoring = injectIsRestoring() - - /** - * Signal that has the default options from query client applied - * computed() is used so signals can be inserted into the options - * making it reactive. Wrapping options in a function ensures embedded expressions - * are preserved and can keep being applied after signal changes - */ - const optionsSignal = computed(() => { - return optionsFn() - }) - - const defaultedQueries = computed(() => { - return optionsSignal().queries.map((opts) => { - const defaultedOptions = queryClient.defaultQueryOptions( - opts as QueryObserverOptions, - ) - // Make sure the results are already in fetching state before subscribing or updating options - defaultedOptions._optimisticResults = isRestoring() - ? 'isRestoring' - : 'optimistic' - - return defaultedOptions as QueryObserverOptions - }) - }) - - const observerSignal = (() => { - let instance: QueriesObserver | null = null - - return computed(() => { - return (instance ||= new QueriesObserver( - queryClient, - defaultedQueries(), - optionsSignal() as QueriesObserverOptions, - )) - }) - })() - - const optimisticResultSignal = computed(() => - observerSignal().getOptimisticResult( - defaultedQueries(), - (optionsSignal() as QueriesObserverOptions).combine, - ), - ) - - // Do not notify on updates because of changes in the options because - // these changes should already be reflected in the optimistic result. - effect(() => { - observerSignal().setQueries( - defaultedQueries(), - optionsSignal() as QueriesObserverOptions, - ) - }) - - const optimisticCombinedResultSignal = computed(() => { - const [_optimisticResult, getCombinedResult, trackResult] = - optimisticResultSignal() - return getCombinedResult(trackResult()) - }) - - const resultFromSubscriberSignal = signal(null) - - effect(() => { - const observer = observerSignal() - const [_optimisticResult, getCombinedResult] = optimisticResultSignal() - - untracked(() => { - const unsubscribe = isRestoring() - ? () => undefined - : ngZone.runOutsideAngular(() => - observer.subscribe( - notifyManager.batchCalls((state) => { - resultFromSubscriberSignal.set(getCombinedResult(state)) - }), - ), - ) - - destroyRef.onDestroy(unsubscribe) - }) - }) - - const resultSignal = computed(() => { - const subscriberResult = resultFromSubscriberSignal() - const optimisticResult = optimisticCombinedResultSignal() - return subscriberResult ?? optimisticResult - }) - - return computed(() => { - const result = resultSignal() - const { combine } = optionsSignal() - - return combine - ? result - : (result as QueriesResults).map((query) => - signalProxy(signal(query)), - ) - }) - }) as unknown as Signal -} diff --git a/packages/angular-query-experimental/src/inject-query-client.ts b/packages/angular-query-experimental/src/inject-query-client.ts deleted file mode 100644 index 7cd29e38508..00000000000 --- a/packages/angular-query-experimental/src/inject-query-client.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Injector, inject } from '@angular/core' -import { QueryClient } from '@tanstack/query-core' -import type { InjectOptions } from '@angular/core' - -/** - * Injects a `QueryClient` instance and allows passing a custom injector. - * @param injectOptions - Type of the options argument to inject and optionally a custom injector. - * @returns The `QueryClient` instance. - * @deprecated Use `inject(QueryClient)` instead. - * If you need to get a `QueryClient` from a custom injector, use `injector.get(QueryClient)`. - * - * - * **Example** - * ```ts - * const queryClient = injectQueryClient(); - * ``` - */ -export function injectQueryClient( - injectOptions: InjectOptions & { injector?: Injector } = {}, -) { - return (injectOptions.injector ?? inject(Injector)).get(QueryClient) -} diff --git a/packages/angular-query-experimental/src/pending-tasks-compat.ts b/packages/angular-query-experimental/src/pending-tasks-compat.ts deleted file mode 100644 index e156996993e..00000000000 --- a/packages/angular-query-experimental/src/pending-tasks-compat.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { InjectionToken, inject } from '@angular/core' -import * as ng from '@angular/core' -import { noop } from '@tanstack/query-core' - -type PendingTasksCompat = { add: () => PendingTaskRef } - -export type PendingTaskRef = () => void - -export const PENDING_TASKS = new InjectionToken( - 'PENDING_TASKS', - { - factory: (): PendingTasksCompat => { - // Access via Reflect so bundlers stay quiet when the token is absent (Angular < 19). - const token = Reflect.get(ng, 'PendingTasks') as unknown as - | Parameters[0] - | undefined - - const svc: PendingTasksCompat | null = token - ? (inject(token, { optional: true }) as PendingTasksCompat | null) - : null - - // Without PendingTasks we fall back to a stable no-op shim. - return { - add: svc ? () => svc.add() : () => noop, - } - }, - }, -) diff --git a/packages/angular-query-experimental/src/providers.ts b/packages/angular-query-experimental/src/providers.ts deleted file mode 100644 index 9f52c11690f..00000000000 --- a/packages/angular-query-experimental/src/providers.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { DestroyRef, InjectionToken, inject } from '@angular/core' -import { QueryClient } from '@tanstack/query-core' -import type { Provider } from '@angular/core' - -/** - * Usually {@link provideTanStackQuery} is used once to set up TanStack Query and the - * [`QueryClient`](https://tanstack.com/query/latest/docs/reference/QueryClient) for the entire application — - * it calls `provideQueryClient` internally. Use `provideQueryClient` directly to provide a different - * `QueryClient` instance for part of the application, or for unit testing. - * @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - * @returns A provider object that can be used to provide the `QueryClient` instance. - * - * @example - * Providing a test-only `QueryClient` in a component test, without wiring up `provideTanStackQuery`'s other - * defaults: - * ```ts - * TestBed.configureTestingModule({ - * providers: [provideQueryClient(new QueryClient())], - * }) - * ``` - */ -export function provideQueryClient( - queryClient: QueryClient | InjectionToken, -): Provider { - return { - provide: QueryClient, - useFactory: () => { - const client = - queryClient instanceof InjectionToken - ? inject(queryClient) - : queryClient - // Unmount the query client on injector destroy - inject(DestroyRef).onDestroy(() => client.unmount()) - client.mount() - return client - }, - } -} - -/** - * Sets up providers necessary to enable TanStack Query functionality for Angular applications. Allows - * configuring a `QueryClient` and optional features such as developer tools. - * - * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start - * @see {@link withDevtools} - * @param queryClient - A `QueryClient` instance, or an `InjectionToken` which provides a `QueryClient`. - * @param features - Optional features to configure additional Query functionality. - * @returns A set of providers to set up TanStack Query. - * - * @example - * ```ts - * import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' - * - * bootstrapApplication(AppComponent, { - * providers: [provideTanStackQuery(new QueryClient())], - * }) - * ``` - * - * @example - * The same, in an `NgModule`-based application: - * ```ts - * import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query-experimental' - * - * @NgModule({ - * declarations: [AppComponent], - * imports: [BrowserModule], - * providers: [provideTanStackQuery(new QueryClient())], - * bootstrap: [AppComponent], - * }) - * export class AppModule {} - * ``` - * - * @example - * Enabling optional developer tools by adding `withDevtools` — by default, the tools are then loaded when - * your app is in development mode: - * ```ts - * import { - * provideTanStackQuery, - * withDevtools, - * QueryClient, - * } from '@tanstack/angular-query-experimental' - * - * bootstrapApplication(AppComponent, { - * providers: [provideTanStackQuery(new QueryClient(), withDevtools())], - * }) - * ``` - * - * @example - * Using an `InjectionToken` for the `QueryClient` — an advanced optimization that lets TanStack Query be - * absent from the main application bundle, useful for including it on lazy-loaded routes only while still - * sharing a `QueryClient`. This is a small optimization; for most applications it's preferable to provide - * the `QueryClient` in the main application config, as in the examples above: - * ```ts - * export const MY_QUERY_CLIENT = new InjectionToken('', { - * factory: () => new QueryClient(), - * }) - * - * // In a lazy loaded route or lazy loaded component's providers array: - * providers: [provideTanStackQuery(MY_QUERY_CLIENT)] - * ``` - */ -export function provideTanStackQuery( - queryClient: QueryClient | InjectionToken, - ...features: Array -): Array { - return [ - provideQueryClient(queryClient), - features.map((feature) => feature.ɵproviders), - ] -} - -/** - * Sets up providers necessary to enable TanStack Query functionality for Angular applications. - * - * Allows configuring a `QueryClient`. - * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start - * @param queryClient - A `QueryClient` instance. - * @returns A set of providers to set up TanStack Query. - * @deprecated Use `provideTanStackQuery` instead. - */ -export function provideAngularQuery(queryClient: QueryClient): Array { - return provideTanStackQuery(queryClient) -} - -const queryFeatures = ['Devtools', 'PersistQueryClient'] as const - -type QueryFeatureKind = (typeof queryFeatures)[number] - -/** - * Helper type to represent a Query feature. - */ -export interface QueryFeature { - ɵkind: TFeatureKind - ɵproviders: Array -} - -/** - * Helper function to create an object that represents a Query feature. - * @param kind - The kind of feature, e.g. `'Devtools'`. - * @param providers - The Angular providers this feature contributes to `provideTanStackQuery`. - * @returns A Query feature. - */ -export function queryFeature( - kind: TFeatureKind, - providers: Array, -): QueryFeature { - return { ɵkind: kind, ɵproviders: providers } -} - -/** - * A type alias that represents a feature which enables developer tools. - * The type is used to describe the return value of the `withDevtools` function. - * @see {@link withDevtools} - */ -export type DevtoolsFeature = QueryFeature<'Devtools'> - -/** - * A type alias that represents a feature which enables persistence. - * The type is used to describe the return value of the `withPersistQueryClient` function. - */ -export type PersistQueryClientFeature = QueryFeature<'PersistQueryClient'> - -/** - * A type alias that represents all Query features available for use with `provideTanStackQuery`. - * Features can be enabled by adding special functions to the `provideTanStackQuery` call. - * See documentation for each symbol to find corresponding function name. See also `provideTanStackQuery` - * documentation on how to use those functions. - * @see {@link provideTanStackQuery} - */ -export type QueryFeatures = DevtoolsFeature | PersistQueryClientFeature diff --git a/packages/angular-query-experimental/src/signal-proxy.ts b/packages/angular-query-experimental/src/signal-proxy.ts deleted file mode 100644 index e2a9de345f6..00000000000 --- a/packages/angular-query-experimental/src/signal-proxy.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { computed, untracked } from '@angular/core' -import type { Signal } from '@angular/core' - -export type MapToSignals = { - [K in keyof T]: T[K] extends Function ? T[K] : Signal -} - -/** - * Exposes fields of an object passed via an Angular `Signal` as `Computed` signals. - * Functions on the object are passed through as-is. - * @param inputSignal - `Signal` that must return an object. - * @returns A proxy object with the same fields as the input object, but with each field wrapped in a `Computed` signal. - */ -export function signalProxy>( - inputSignal: Signal, -) { - const internalState = {} as MapToSignals - - return new Proxy>(internalState, { - get(target, prop) { - // first check if we have it in our internal state and return it - const computedField = target[prop] - if (computedField) return computedField - - // then, check if it's a function on the resultState and return it - const targetField = untracked(inputSignal)[prop] - if (typeof targetField === 'function') return targetField - - // finally, create a computed field, store it and return it - // @ts-expect-error - return (target[prop] = computed(() => inputSignal()[prop])) - }, - has(_, prop) { - return !!untracked(inputSignal)[prop] - }, - ownKeys() { - return Reflect.ownKeys(untracked(inputSignal)) - }, - getOwnPropertyDescriptor() { - return { - enumerable: true, - configurable: true, - } - }, - }) -} diff --git a/packages/angular-query-persist-client/README.md b/packages/angular-query-persist-client/README.md new file mode 100644 index 00000000000..08526b55a67 --- /dev/null +++ b/packages/angular-query-persist-client/README.md @@ -0,0 +1,33 @@ +# @tanstack/angular-query-persist-client + +Persistence integration for `@tanstack/angular-query`. + +## Installation + +```sh +pnpm add @tanstack/angular-query-persist-client @tanstack/query-persist-client-core +``` + +## Usage + +```ts +import { QueryClient, provideTanStackQuery } from '@tanstack/angular-query' +import { withPersistQueryClient } from '@tanstack/angular-query-persist-client' + +export const appConfig: ApplicationConfig = { + providers: [ + provideTanStackQuery( + () => new QueryClient(), + withPersistQueryClient(() => ({ + persistOptions: { + persister: createAsyncStoragePersister({ storage: localStorage }), + }, + })), + ), + ], +} +``` + +The factory runs only in the browser and inside an Angular injection context. +This makes it safe to access browser-only storage and inject application +services without sharing persistence state between SSR requests. diff --git a/packages/angular-query-persist-client/package.json b/packages/angular-query-persist-client/package.json index b1bca08ecec..4acb5fa11fc 100644 --- a/packages/angular-query-persist-client/package.json +++ b/packages/angular-query-persist-client/package.json @@ -1,7 +1,6 @@ { "name": "@tanstack/angular-query-persist-client", - "private": true, - "version": "5.103.1", + "version": "4.0.0", "description": "Angular bindings to work with persisters in TanStack/angular-query", "author": "Omer Gronich", "license": "MIT", @@ -20,12 +19,13 @@ "compile": "tsc --project tsconfig.json", "test:eslint": "eslint --concurrency=auto ./src", "test:types": "npm-run-all --serial test:types:*", - "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts56 --tsBuildInfoFile .cache/test-types/ts56.tsbuildinfo", - "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts57 --tsBuildInfoFile .cache/test-types/ts57.tsbuildinfo", - "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts58 --tsBuildInfoFile .cache/test-types/ts58.tsbuildinfo", - "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts59 --tsBuildInfoFile .cache/test-types/ts59.tsbuildinfo", - "test:types:tscurrent": "tsc --project tsconfig.json --outDir .cache/test-types/tscurrent --tsBuildInfoFile .cache/test-types/tscurrent.tsbuildinfo", - "test:types:ts70": "node ../../node_modules/typescript70/lib/tsc.js --project tsconfig.json --outDir .cache/test-types/ts70 --tsBuildInfoFile .cache/test-types/ts70.tsbuildinfo", + "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build", + "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build", + "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build", + "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js --build", + "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js --build", + "test:types:tscurrent": "tsc --build", + "test:types:ts60": "node ../../node_modules/typescript60/lib/tsc.js --build", "test:lib": "vitest", "test:lib:dev": "pnpm run test:lib --watch", "test:build": "publint --strict && attw --pack", @@ -54,21 +54,23 @@ "@tanstack/query-persist-client-core": "workspace:*" }, "devDependencies": { - "@angular/animations": "^20.0.0", - "@angular/common": "^20.0.0", - "@angular/compiler": "^20.0.0", - "@angular/core": "^20.0.0", - "@angular/platform-browser": "^20.0.0", - "@tanstack/angular-query-experimental": "workspace:*", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/router": "^22.0.0", + "@tanstack/angular-query": "workspace:*", "@tanstack/query-test-utils": "workspace:*", - "@testing-library/angular": "^18.0.0", + "@testing-library/angular": "^18.1.1", "@testing-library/dom": "^10.4.0", "eslint-plugin-jsdoc": "^50.5.0", - "npm-run-all2": "^5.0.0" + "npm-run-all2": "^5.0.0", + "typescript": "5.9.3", + "zone.js": "^0.16.0" }, "peerDependencies": { - "@angular/common": ">=16.0.0", - "@angular/core": ">=16.0.0", - "@tanstack/angular-query-experimental": "workspace:^" + "@angular/common": ">=22.0.0", + "@angular/core": ">=22.0.0", + "@tanstack/angular-query": "workspace:^" } } diff --git a/packages/angular-query-persist-client/src/__tests__/with-persist-query-client.test.ts b/packages/angular-query-persist-client/src/__tests__/with-persist-query-client.test.ts index 7a5883120a6..d46bfc1ee1e 100644 --- a/packages/angular-query-persist-client/src/__tests__/with-persist-query-client.test.ts +++ b/packages/angular-query-persist-client/src/__tests__/with-persist-query-client.test.ts @@ -1,24 +1,41 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { QueryClient, + injectIsRestoring, + injectQueries, injectQuery, noop, provideTanStackQuery, -} from '@tanstack/angular-query-experimental' +} from '@tanstack/angular-query' import { persistQueryClientSave } from '@tanstack/query-persist-client-core' import { Component, + EnvironmentInjector, + InjectionToken, + PLATFORM_ID, + createEnvironmentInjector, effect, + inject, provideZonelessChangeDetection, } from '@angular/core' +import { TestBed } from '@angular/core/testing' import { render } from '@testing-library/angular' import { queryKey, sleep } from '@tanstack/query-test-utils' +import * as persistClientCore from '@tanstack/query-persist-client-core' import { withPersistQueryClient } from '../with-persist-query-client' import type { PersistedClient, Persister, } from '@tanstack/query-persist-client-core' +beforeEach(() => { + vi.useFakeTimers() +}) + +afterEach(() => { + vi.useRealTimers() +}) + const createMockPersister = (): Persister => { let storedState: PersistedClient | undefined @@ -55,15 +72,7 @@ const createMockErrorPersister = ( } describe('withPersistQueryClient', () => { - beforeEach(() => { - vi.useFakeTimers() - }) - - afterEach(() => { - vi.useRealTimers() - }) - - it('should restore cache from persister', async () => { + it('restores cache from persister', async () => { const key = queryKey() const states: Array<{ status: string @@ -100,24 +109,21 @@ describe('withPersistQueryClient', () => { queryKey: key, queryFn: () => sleep(10).then(() => 'fetched'), })) - - constructor() { - effect(() => { - states.push({ - status: this.state.status(), - fetchStatus: this.state.fetchStatus(), - data: this.state.data(), - }) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), }) - } + }) } const rendered = await render(Page, { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - queryClient, - withPersistQueryClient({ persistOptions: { persister } }), + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister } })), ), ], }) @@ -152,9 +158,115 @@ describe('withPersistQueryClient', () => { }) }) - it.todo( - '(Once injectQueries is functional) verify that injectQueries transitions to an idle state', - ) + it('restores cache for injectQueries and keeps it idle while restoring', async () => { + const key1 = queryKey() + const key2 = queryKey() + const states: Array< + Array<{ + status: string + fetchStatus: string + data: string | undefined + }> + > = [] + + const queryClient = new QueryClient() + void queryClient.query({ + queryKey: key1, + queryFn: () => sleep(10).then(() => 'hydrated-1'), + }).catch(noop) + void queryClient.query({ + queryKey: key2, + queryFn: () => sleep(10).then(() => 'hydrated-2'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(10) + + const persister = createMockPersister() + persistQueryClientSave({ queryClient, persister }) + await vi.advanceTimersByTimeAsync(0) + queryClient.clear() + + @Component({ + template: ` +
+

{{ formattedData() }}

+

fetchStatus: {{ formattedFetchStatus() }}

+
+ `, + }) + class Page { + state = injectQueries(() => ({ + queries: [ + { + queryKey: key1, + queryFn: () => sleep(10).then(() => 'fetched-1'), + }, + { + queryKey: key2, + queryFn: () => sleep(10).then(() => 'fetched-2'), + }, + ], + })) + + _ = effect(() => { + states.push( + this.state().map((query) => ({ + status: query.status(), + fetchStatus: query.fetchStatus(), + data: query.data(), + })), + ) + }) + + formattedData() { + return this.state() + .map((query) => query.data() ?? 'null') + .join(',') + } + + formattedFetchStatus() { + return this.state() + .map((query) => query.fetchStatus()) + .join(',') + } + } + + const rendered = await render(Page, { + providers: [ + provideZonelessChangeDetection(), + provideTanStackQuery( + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister } })), + ), + ], + }) + + expect(rendered.getByText('fetchStatus: idle,idle')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + expect(rendered.getByText('hydrated-1,hydrated-2')).toBeInTheDocument() + expect( + rendered.getByText('fetchStatus: fetching,fetching'), + ).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(11) + rendered.fixture.detectChanges() + expect(rendered.getByText('fetched-1,fetched-2')).toBeInTheDocument() + expect(rendered.getByText('fetchStatus: idle,idle')).toBeInTheDocument() + + expect(states[0]).toMatchObject([ + { status: 'pending', fetchStatus: 'idle', data: undefined }, + { status: 'pending', fetchStatus: 'idle', data: undefined }, + ]) + expect(states).toContainEqual([ + { status: 'success', fetchStatus: 'fetching', data: 'hydrated-1' }, + { status: 'success', fetchStatus: 'fetching', data: 'hydrated-2' }, + ]) + expect(states[states.length - 1]).toMatchObject([ + { status: 'success', fetchStatus: 'idle', data: 'fetched-1' }, + { status: 'success', fetchStatus: 'idle', data: 'fetched-2' }, + ]) + }) it('should show initialData while restoring', async () => { const key = queryKey() @@ -197,24 +309,21 @@ describe('withPersistQueryClient', () => { // otherwise initialData would be newer and takes precedence initialDataUpdatedAt: 1, })) - - constructor() { - effect(() => { - states.push({ - status: this.state.status(), - fetchStatus: this.state.fetchStatus(), - data: this.state.data(), - }) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), }) - } + }) } const rendered = await render(Page, { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - queryClient, - withPersistQueryClient({ persistOptions: { persister } }), + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister } })), ), ], }) @@ -293,24 +402,21 @@ describe('withPersistQueryClient', () => { }), staleTime: Infinity, })) - - constructor() { - effect(() => { - states.push({ - status: this.state.status(), - fetchStatus: this.state.fetchStatus(), - data: this.state.data(), - }) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), }) - } + }) } const rendered = await render(Page, { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - queryClient, - withPersistQueryClient({ persistOptions: { persister } }), + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister } })), ), ], }) @@ -375,11 +481,11 @@ describe('withPersistQueryClient', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - queryClient, - withPersistQueryClient({ + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister }, onSuccess, - }), + })), ), ], }) @@ -395,6 +501,75 @@ describe('withPersistQueryClient', () => { expect(rendered.getByText('fetched')).toBeInTheDocument() }) + it('should await onSuccess before refetching or subscribing', async () => { + const key = queryKey() + const queryClient = new QueryClient() + void queryClient.query({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'hydrated'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(10) + + const persister = createMockPersister() + persistQueryClientSave({ queryClient, persister }) + await vi.advanceTimersByTimeAsync(0) + + queryClient.clear() + + const fetchSpy = vi.fn(() => sleep(10).then(() => 'fetched')) + const onSuccess = vi.fn(async () => { + await sleep(20) + }) + + @Component({ + template: ` +
+

{{ state.data() ?? 'null' }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: fetchSpy, + })) + } + + const rendered = await render(Page, { + providers: [ + provideZonelessChangeDetection(), + provideTanStackQuery( + () => queryClient, + withPersistQueryClient(() => ({ + persistOptions: { persister }, + onSuccess, + })), + ), + ], + }) + + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + expect(onSuccess).toHaveBeenCalledTimes(1) + expect(fetchSpy).toHaveBeenCalledTimes(0) + expect(rendered.getByText('fetchStatus: idle')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(19) + rendered.fixture.detectChanges() + expect(fetchSpy).toHaveBeenCalledTimes(0) + + await vi.advanceTimersByTimeAsync(1) + rendered.fixture.detectChanges() + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(rendered.getByText('hydrated')).toBeInTheDocument() + expect(rendered.getByText('fetchStatus: fetching')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(11) + rendered.fixture.detectChanges() + expect(rendered.getByText('fetched')).toBeInTheDocument() + }) + it('should remove cache after non-successful restoring', async () => { const key = queryKey() const onErrorMock = vi @@ -425,12 +600,12 @@ describe('withPersistQueryClient', () => { providers: [ provideZonelessChangeDetection(), provideTanStackQuery( - queryClient, - withPersistQueryClient({ + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister }, onSuccess, onError, - }), + })), ), ], }) @@ -449,4 +624,252 @@ describe('withPersistQueryClient', () => { expect(onErrorMock).toHaveBeenNthCalledWith(1, error) onErrorMock.mockRestore() }) + + it('should await onError before starting queries after restore failure', async () => { + const key = queryKey() + const onErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const queryClient = new QueryClient() + const removeClient = vi.fn() + const [, persister] = createMockErrorPersister(removeClient) + const fetchSpy = vi.fn(() => sleep(10).then(() => 'fetched')) + const onError = vi.fn(async () => { + await sleep(20) + }) + + @Component({ + template: ` +
+

{{ state.data() ?? 'null' }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: fetchSpy, + })) + } + + const rendered = await render(Page, { + providers: [ + provideZonelessChangeDetection(), + provideTanStackQuery( + () => queryClient, + withPersistQueryClient(() => ({ + persistOptions: { persister }, + onError, + })), + ), + ], + }) + + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + expect(onError).toHaveBeenCalledTimes(1) + expect(fetchSpy).toHaveBeenCalledTimes(0) + expect(rendered.getByText('fetchStatus: idle')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(19) + rendered.fixture.detectChanges() + expect(fetchSpy).toHaveBeenCalledTimes(0) + + await vi.advanceTimersByTimeAsync(1) + rendered.fixture.detectChanges() + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(rendered.getByText('fetchStatus: fetching')).toBeInTheDocument() + + await vi.advanceTimersByTimeAsync(11) + rendered.fixture.detectChanges() + expect(rendered.getByText('fetched')).toBeInTheDocument() + + onErrorMock.mockRestore() + }) + + it('factory form can inject a token and restores cache', async () => { + const key = queryKey() + const holder = { persister: createMockPersister() } + const HOLDER = new InjectionToken<{ persister: Persister }>( + 'persist-test-holder', + ) + + const queryClient = new QueryClient() + void queryClient.query({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'hydrated'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(10) + + persistQueryClientSave({ queryClient, persister: holder.persister }) + await vi.advanceTimersByTimeAsync(0) + + queryClient.clear() + + @Component({ + template: ` +
+

{{ state.data() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'fetched'), + })) + } + + const rendered = await render(Page, { + providers: [ + provideZonelessChangeDetection(), + { provide: HOLDER, useValue: holder }, + provideTanStackQuery( + () => queryClient, + withPersistQueryClient(() => ({ + persistOptions: { persister: inject(HOLDER).persister }, + })), + ), + ], + }) + + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + expect(rendered.getByText('hydrated')).toBeInTheDocument() + }) + + it('factory callback runs only in browser mode', async () => { + const factory = vi.fn(() => ({ + persistOptions: { + persister: createMockPersister(), + }, + })) + + @Component({ + template: `{{ isRestoring() }}`, + }) + class Page { + isRestoring = injectIsRestoring() + } + + const rendered = await render(Page, { + providers: [ + provideZonelessChangeDetection(), + { provide: PLATFORM_ID, useValue: 'server' }, + provideTanStackQuery( + () => new QueryClient(), + withPersistQueryClient(factory), + ), + ], + }) + + rendered.fixture.detectChanges() + + expect(factory).not.toHaveBeenCalled() + expect(rendered.fixture.nativeElement.textContent.trim()).toBe('false') + }) + + it('cleanup subscription runs on injector destroy', async () => { + const key = queryKey() + const queryClient = new QueryClient() + const persister = createMockPersister() + const cleanup = vi.fn() + const subscribeSpy = vi + .spyOn(persistClientCore, 'persistQueryClientSubscribe') + .mockReturnValue(cleanup) + + void queryClient.query({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'hydrated'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(10) + persistQueryClientSave({ queryClient, persister }) + await vi.advanceTimersByTimeAsync(0) + queryClient.clear() + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + }) + + const injector = createEnvironmentInjector( + [ + provideTanStackQuery( + () => queryClient, + withPersistQueryClient(() => ({ persistOptions: { persister } })), + ), + ], + TestBed.inject(EnvironmentInjector), + ) + + await vi.advanceTimersByTimeAsync(10) + injector.destroy() + + expect(subscribeSpy).toHaveBeenCalledTimes(1) + expect(cleanup).toHaveBeenCalledTimes(1) + }) + + it('does not subscribe when the injector is destroyed during restore', async () => { + const subscribeSpy = vi.spyOn( + persistClientCore, + 'persistQueryClientSubscribe', + ) + + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + }) + + const injector = createEnvironmentInjector( + [ + provideTanStackQuery( + () => new QueryClient(), + withPersistQueryClient(() => ({ + persistOptions: { persister: createMockPersister() }, + })), + ), + ], + TestBed.inject(EnvironmentInjector), + ) + + injector.destroy() + await vi.advanceTimersByTimeAsync(10) + + expect(subscribeSpy).not.toHaveBeenCalled() + }) + it('keeps restoration state independent when a provider configuration is reused', async () => { + TestBed.configureTestingModule({ + providers: [provideZonelessChangeDetection()], + }) + const resolutions: Array<(value: undefined) => void> = [] + const providers = provideTanStackQuery( + () => new QueryClient(), + withPersistQueryClient(() => ({ + persistOptions: { + persister: { + restoreClient: () => + new Promise((resolve) => resolutions.push(resolve)), + persistClient: () => {}, + removeClient: () => {}, + }, + }, + })), + ) + const parent = TestBed.inject(EnvironmentInjector) + const first = createEnvironmentInjector([providers], parent) + const second = createEnvironmentInjector([providers], parent) + const firstState = first.runInContext(() => injectIsRestoring()) + const secondState = second.runInContext(() => injectIsRestoring()) + expect(firstState).not.toBe(secondState) + expect(firstState()).toBe(true) + expect(secondState()).toBe(true) + resolutions[0]!(undefined) + await vi.advanceTimersByTimeAsync(0) + expect(firstState()).toBe(false) + expect(secondState()).toBe(true) + first.destroy() + expect(secondState()).toBe(true) + resolutions[1]!(undefined) + await vi.advanceTimersByTimeAsync(0) + expect(secondState()).toBe(false) + second.destroy() + }) }) diff --git a/packages/angular-query-persist-client/src/with-persist-query-client.ts b/packages/angular-query-persist-client/src/with-persist-query-client.ts index 43f562450a1..f2169e48ed4 100644 --- a/packages/angular-query-persist-client/src/with-persist-query-client.ts +++ b/packages/angular-query-persist-client/src/with-persist-query-client.ts @@ -1,88 +1,84 @@ -import { - QueryClient, - provideIsRestoring, - queryFeature, -} from '@tanstack/angular-query-experimental' +import { QueryClient } from '@tanstack/angular-query' import { DestroyRef, - ENVIRONMENT_INITIALIZER, + InjectionToken, PLATFORM_ID, inject, + makeEnvironmentProviders, + provideEnvironmentInitializer, signal, } from '@angular/core' +import { + provideIsRestoring, + queryFeature, +} from '@tanstack/angular-query/internal' import { isPlatformBrowser } from '@angular/common' import { persistQueryClientRestore, persistQueryClientSubscribe, } from '@tanstack/query-persist-client-core' -import type { PersistQueryClientOptions as PersistQueryClientOptionsCore } from '@tanstack/query-persist-client-core' -import type { PersistQueryClientFeature } from '@tanstack/angular-query-experimental' +import type { WritableSignal } from '@angular/core' +import type { QueryFeature } from '@tanstack/angular-query' +import type { WithPersistQueryClientFn } from './with-persist-query-client.types' -type PersistQueryClientOptions = { - persistOptions: Omit - onSuccess?: () => Promise | unknown - onError?: () => Promise | unknown -} +const RESTORING_STATE = new InjectionToken>( + 'Query restoration state', +) + +export type { + PersistQueryClientUserOptions, + WithPersistQueryClientFn, +} from './with-persist-query-client.types' /** - * Enables persistence. - * - * **Example** + * Enables persistence. The options factory runs once per injector, only in the + * browser, in an Angular injection context. It can call `inject()` and use + * browser APIs such as `localStorage`. * * ```ts - * const localStoragePersister = createAsyncStoragePersister({ - * storage: window.localStorage, - * }) - * - * export const appConfig: ApplicationConfig = { - * providers: [ - * provideTanStackQuery( - * new QueryClient(), - * withPersistQueryClient({ - * persistOptions: { - * persister: localStoragePersister, - * }, - * onSuccess: () => console.log('Restoration completed successfully.'), - * }) - * ), - * ], - * }; + * withPersistQueryClient(() => ({ + * persistOptions: { + * persister: createAsyncStoragePersister({ storage: localStorage }), + * }, + * })) * ``` - * @param persistQueryClientOptions - persistence options and optional onSuccess and onError callbacks which get called when the restoration process is complete. + * + * @param optionsFactory - Creates the persistence options in the browser. * @returns A set of providers for use with `provideTanStackQuery`. * @public */ export function withPersistQueryClient( - persistQueryClientOptions: PersistQueryClientOptions, -): PersistQueryClientFeature { - const isRestoring = signal(true) - const providers = [ - provideIsRestoring(isRestoring.asReadonly()), - { - // Do not use provideEnvironmentInitializer while Angular < v19 is supported - provide: ENVIRONMENT_INITIALIZER, - multi: true, - useValue: () => { - if (!isPlatformBrowser(inject(PLATFORM_ID))) return + optionsFactory: WithPersistQueryClientFn, +): QueryFeature { + return queryFeature( + makeEnvironmentProviders([ + { provide: RESTORING_STATE, useFactory: () => signal(true) }, + provideIsRestoring(() => inject(RESTORING_STATE).asReadonly()), + provideEnvironmentInitializer(() => { + const isRestoring = inject(RESTORING_STATE) + if (!isPlatformBrowser(inject(PLATFORM_ID))) { + isRestoring.set(false) + return + } const destroyRef = inject(DestroyRef) const queryClient = inject(QueryClient) - const { onSuccess, onError, persistOptions } = persistQueryClientOptions + const { onSuccess, onError, persistOptions } = optionsFactory() const options = { queryClient, ...persistOptions } - persistQueryClientRestore(options) + void persistQueryClientRestore(options) .then(() => { - onSuccess?.() + return onSuccess?.() }) .catch(() => { - onError?.() + return onError?.() }) .finally(() => { + if (destroyRef.destroyed) return isRestoring.set(false) const cleanup = persistQueryClientSubscribe(options) destroyRef.onDestroy(cleanup) }) - }, - }, - ] - return queryFeature('PersistQueryClient', providers) + }), + ]), + ) } diff --git a/packages/angular-query-persist-client/src/with-persist-query-client.types.ts b/packages/angular-query-persist-client/src/with-persist-query-client.types.ts new file mode 100644 index 00000000000..56c1b06e12c --- /dev/null +++ b/packages/angular-query-persist-client/src/with-persist-query-client.types.ts @@ -0,0 +1,15 @@ +import type { PersistQueryClientOptions as PersistQueryClientOptionsCore } from '@tanstack/query-persist-client-core' + +export type PersistQueryClientUserOptions = { + persistOptions: Omit + /** Called after restoration succeeds. A returned promise is awaited. */ + onSuccess?: () => unknown + /** Called if restoration fails. A returned promise is awaited. */ + onError?: () => unknown +} + +/** + * Returns persistence options. The function runs once in an Angular injection + * context in the browser, so it can call `inject()` and touch browser APIs. + */ +export type WithPersistQueryClientFn = () => PersistQueryClientUserOptions diff --git a/packages/angular-query-persist-client/tsconfig.json b/packages/angular-query-persist-client/tsconfig.json index b1366ab7700..f7c0a2343a8 100644 --- a/packages/angular-query-persist-client/tsconfig.json +++ b/packages/angular-query-persist-client/tsconfig.json @@ -15,7 +15,7 @@ "package.json" ], "references": [ - { "path": "../angular-query-experimental" }, + { "path": "../angular-query" }, { "path": "../query-persist-client-core" } ] } diff --git a/packages/angular-query-persist-client/vite.config.ts b/packages/angular-query-persist-client/vite.config.ts index 3c608b8a481..f362edc2bb4 100644 --- a/packages/angular-query-persist-client/vite.config.ts +++ b/packages/angular-query-persist-client/vite.config.ts @@ -1,11 +1,24 @@ +import path from 'node:path' +import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import packageJson from './package.json' +const packageDir = path.dirname(fileURLToPath(import.meta.url)) +const angularQueryEntry = path.join(packageDir, '../angular-query/src/index.ts') +const angularQueryInternalEntry = path.join( + packageDir, + '../angular-query/src/internal.ts', +) + export default defineConfig({ // fix from https://github.com/vitest-dev/vitest/issues/6992#issuecomment-2509408660 resolve: { conditions: ['@tanstack/custom-condition'], + alias: { + '@tanstack/angular-query/internal': angularQueryInternalEntry, + '@tanstack/angular-query': angularQueryEntry, + }, }, environments: { ssr: { diff --git a/packages/angular-query-experimental/.attw.json b/packages/angular-query/.attw.json similarity index 100% rename from packages/angular-query-experimental/.attw.json rename to packages/angular-query/.attw.json diff --git a/packages/angular-query-experimental/README.md b/packages/angular-query/README.md similarity index 62% rename from packages/angular-query-experimental/README.md rename to packages/angular-query/README.md index 53fea66deba..073022cc684 100644 --- a/packages/angular-query-experimental/README.md +++ b/packages/angular-query/README.md @@ -14,15 +14,13 @@ /> -[![npm version](https://img.shields.io/npm/v/@tanstack/angular-query-experimental)](https://www.npmjs.com/package/@tanstack/angular-query-experimental) -[![npm license](https://img.shields.io/npm/l/@tanstack/angular-query-experimental)](https://github.com/TanStack/query/blob/main/LICENSE) -[![bundle size](https://img.shields.io/bundlephobia/minzip/@tanstack/angular-query-experimental)](https://bundlephobia.com/package/@tanstack/angular-query-experimental) -[![npm](https://img.shields.io/npm/dm/@tanstack/angular-query-experimental)](https://www.npmjs.com/package/@tanstack/angular-query-experimental) +[![npm version](https://img.shields.io/npm/v/@tanstack/angular-query)](https://www.npmjs.com/package/@tanstack/angular-query) +[![npm license](https://img.shields.io/npm/l/@tanstack/angular-query)](https://github.com/TanStack/query/blob/main/LICENSE) +[![bundle size](https://img.shields.io/bundlephobia/minzip/@tanstack/angular-query)](https://bundlephobia.com/package/@tanstack/angular-query) +[![npm](https://img.shields.io/npm/dm/@tanstack/angular-query)](https://www.npmjs.com/package/@tanstack/angular-query) # Angular Query -> IMPORTANT: This library is currently in an experimental stage. This means that breaking changes may happen in minor AND patch releases. Upgrade carefully. If you use this in production while in experimental stage, please lock your version to a patch-level version to avoid unexpected breaking changes. - Functions for fetching, caching and updating asynchronous data in Angular # Documentation @@ -43,64 +41,48 @@ Visit https://tanstack.com/query/latest/docs/framework/angular/overview # Quick Start -> The Angular adapter for TanStack Query requires Angular 16 or higher. +> The Angular adapter for TanStack Query requires Angular 20.1 or higher. 1. Install `angular-query` ```bash -$ npm i @tanstack/angular-query-experimental +$ npm i @tanstack/angular-query ``` or ```bash -$ pnpm add @tanstack/angular-query-experimental +$ pnpm add @tanstack/angular-query ``` or ```bash -$ yarn add @tanstack/angular-query-experimental +$ yarn add @tanstack/angular-query ``` or ```bash -$ bun add @tanstack/angular-query-experimental +$ bun add @tanstack/angular-query ``` 2. Initialize **TanStack Query** by adding **provideTanStackQuery** to your application +Pass a factory; Angular runs it once per injector and in an injection context. + ```ts -import { provideTanStackQuery } from '@tanstack/angular-query-experimental' -import { QueryClient } from '@tanstack/angular-query-experimental' +import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query' bootstrapApplication(AppComponent, { - providers: [provideTanStackQuery(new QueryClient())], -}) -``` - -or in a NgModule-based app - -```ts -import { provideHttpClient } from '@angular/common/http' -import { - provideTanStackQuery, - QueryClient, -} from '@tanstack/angular-query-experimental' - -@NgModule({ - declarations: [AppComponent], - imports: [BrowserModule], - providers: [provideTanStackQuery(new QueryClient())], - bootstrap: [AppComponent], + providers: [provideTanStackQuery(() => new QueryClient())], }) ``` 3. Inject query ```ts -import { injectQuery } from '@tanstack/angular-query-experimental' +import { injectQuery } from '@tanstack/angular-query' import { Component } from '@angular/core' @Component({...}) @@ -145,5 +127,3 @@ export interface Post { body: string } ``` - - diff --git a/packages/angular-query-experimental/eslint.config.js b/packages/angular-query/eslint.config.js similarity index 100% rename from packages/angular-query-experimental/eslint.config.js rename to packages/angular-query/eslint.config.js diff --git a/packages/angular-query/package.json b/packages/angular-query/package.json new file mode 100644 index 00000000000..447dc7513d3 --- /dev/null +++ b/packages/angular-query/package.json @@ -0,0 +1,109 @@ +{ + "name": "@tanstack/angular-query", + "version": "4.0.0", + "description": "Signals for managing, caching and syncing asynchronous and remote data in Angular", + "author": "Arnoud de Vries", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/TanStack/query.git", + "directory": "packages/angular-query" + }, + "homepage": "https://tanstack.com/query", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "keywords": [ + "angular query", + "angular", + "cache", + "performance", + "reactive", + "signals", + "state management", + "state", + "tanstack" + ], + "scripts": { + "clean": "premove ./dist ./coverage ./dist-ts", + "compile": "tsc --build", + "test:eslint": "eslint --concurrency=auto ./src ./schematics ./schematics-build", + "test:types": "npm-run-all --serial test:types:*", + "test:types:ts55": "node ../../node_modules/typescript55/lib/tsc.js --build", + "test:types:ts56": "node ../../node_modules/typescript56/lib/tsc.js --build", + "test:types:ts57": "node ../../node_modules/typescript57/lib/tsc.js --build", + "test:types:ts58": "node ../../node_modules/typescript58/lib/tsc.js --build", + "test:types:ts59": "node ../../node_modules/typescript59/lib/tsc.js --build", + "test:types:tscurrent": "tsc --build", + "test:types:ts60": "node ../../node_modules/typescript60/lib/tsc.js --build", + "test:lib": "vitest", + "test:lib:zoneless": "vitest run", + "test:lib:zoneful": "vitest run --config vitest.zoneful.config.ts", + "test:lib:both": "pnpm run test:lib:zoneless && pnpm run test:lib:zoneful", + "test:lib:dev": "pnpm run test:lib --watch", + "test:build": "pnpm pack && pnpm run test:schematics && publint ./dist/*.tgz --strict && attw ./dist/*.tgz; premove ./dist/*.tgz", + "test:schematics": "vitest run --config vitest.schematics.config.ts", + "build": "vite build && tsc -p tsconfig.schematics.json && node scripts/inject-schematic-package-info.js", + "prepack": "node scripts/prepack.js" + }, + "type": "module", + "schematics": "./schematics/collection.json", + "ng-add": { + "save": "dependencies" + }, + "types": "dist/index.d.ts", + "module": "dist/index.mjs", + "exports": { + ".": { + "@tanstack/custom-condition": "./src/index.ts", + "types": "./dist/index.d.ts", + "default": "./dist/index.mjs" + }, + "./internal": { + "@tanstack/custom-condition": "./src/internal.ts", + "types": "./dist/internal.d.ts", + "default": "./dist/internal.mjs" + }, + "./package.json": "./package.json" + }, + "sideEffects": false, + "files": [ + "**/*.d.ts", + "**/*.mjs", + "**/*.mjs.map" + ], + "dependencies": { + "@tanstack/query-core": "workspace:*" + }, + "devDependencies": { + "@analogjs/vite-plugin-angular": "^2.3.1", + "@angular-devkit/schematics": "^22.0.0", + "@angular/build": "^22.0.0", + "@angular/common": "^22.0.0", + "@angular/compiler": "^22.0.0", + "@angular/compiler-cli": "^22.0.0", + "@angular/core": "^22.0.0", + "@angular/platform-browser": "^22.0.0", + "@angular/platform-server": "^22.0.0", + "@angular/router": "^22.0.0", + "@schematics/angular": "^22.0.0", + "@tanstack/query-test-utils": "workspace:*", + "@testing-library/angular": "^18.1.1", + "npm-run-all2": "^5.0.0", + "rxjs": "^7.8.2", + "typescript": "6.0.3", + "vite-plugin-dts": "4.2.3", + "vite-plugin-externalize-deps": "^0.9.0", + "vite-tsconfig-paths": "^5.1.4", + "zone.js": "^0.16.0" + }, + "peerDependencies": { + "@angular/common": ">=22.0.0", + "@angular/core": ">=22.0.0" + }, + "publishConfig": { + "directory": "dist", + "linkDirectory": false + } +} diff --git a/packages/angular-query-experimental/root.eslint.config.js b/packages/angular-query/root.eslint.config.js similarity index 100% rename from packages/angular-query-experimental/root.eslint.config.js rename to packages/angular-query/root.eslint.config.js diff --git a/packages/angular-query/schematics-build/ng-add.test.ts b/packages/angular-query/schematics-build/ng-add.test.ts new file mode 100644 index 00000000000..8417badae20 --- /dev/null +++ b/packages/angular-query/schematics-build/ng-add.test.ts @@ -0,0 +1,100 @@ +import { createRequire } from 'node:module' +import { dirname, resolve } from 'node:path' +import { describe, expect, it } from 'vitest' +import { SchematicTestRunner } from '@angular-devkit/schematics/testing' +import angularQueryPackage from '../package.json' with { type: 'json' } + +const require = createRequire(import.meta.url) +const angularRunner = new SchematicTestRunner( + '@schematics/angular', + resolve( + dirname(require.resolve('@schematics/angular/package.json')), + 'collection.json', + ), +) +const queryRunner = new SchematicTestRunner( + angularQueryPackage.name, + resolve('dist/schematics/collection.json'), +) + +async function createAngularApplication(standalone = true) { + let tree = await angularRunner.runSchematic('workspace', { + name: 'workspace', + version: '20.0.0', + newProjectRoot: 'projects', + }) + + tree = await angularRunner.runSchematic( + 'application', + { + name: 'test-app', + standalone, + routing: false, + style: 'css', + skipInstall: true, + }, + tree, + ) + + return tree +} + +describe('ng-add schematic', () => { + it('configures Angular Query and devtools', async () => { + const tree = await createAngularApplication() + const result = await queryRunner.runSchematic( + 'ng-add', + { project: 'test-app' }, + tree, + ) + const packageJson = JSON.parse(result.readContent('/package.json')) as { + dependencies: Record + } + const devtoolsPackageName = Object.keys(packageJson.dependencies).find( + (packageName) => packageName.endsWith('/angular-query-devtools'), + ) + const appConfig = result.readContent( + '/projects/test-app/src/app/app.config.ts', + ) + + expect(devtoolsPackageName).toBeDefined() + expect(appConfig).toContain(`from '${angularQueryPackage.name}'`) + expect(appConfig).toContain(`from '${devtoolsPackageName}'`) + expect(appConfig).toContain( + 'provideTanStackQuery(() => new QueryClient(), withDevtools())', + ) + expect(queryRunner.tasks).toEqual([ + expect.objectContaining({ name: 'node-package' }), + ]) + }) + + it.each([ + { applicationType: 'standalone', standalone: true }, + { applicationType: 'NgModule', standalone: false }, + ])( + 'does not duplicate configuration in a $applicationType application', + async ({ standalone }) => { + const tree = await createAngularApplication(standalone) + const firstResult = await queryRunner.runSchematic( + 'ng-add', + { project: 'test-app' }, + tree, + ) + const secondResult = await queryRunner.runSchematic( + 'ng-add', + { project: 'test-app' }, + firstResult, + ) + const applicationSources = secondResult.files + .filter((filePath) => filePath.endsWith('.ts')) + .map((filePath) => secondResult.readContent(filePath)) + .join('\n') + + expect(applicationSources.match(/provideTanStackQuery\(/g)).toHaveLength( + 1, + ) + expect(applicationSources.match(/withDevtools\(\)/g)).toHaveLength(1) + expect(queryRunner.tasks).toHaveLength(0) + }, + ) +}) diff --git a/packages/angular-query/schematics/collection.json b/packages/angular-query/schematics/collection.json new file mode 100644 index 00000000000..88f809203d4 --- /dev/null +++ b/packages/angular-query/schematics/collection.json @@ -0,0 +1,10 @@ +{ + "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", + "schematics": { + "ng-add": { + "description": "Configure TanStack Query in an Angular application.", + "factory": "./ng-add/index#ngAdd", + "schema": "./ng-add/schema.json" + } + } +} diff --git a/packages/angular-query/schematics/ng-add/index.ts b/packages/angular-query/schematics/ng-add/index.ts new file mode 100644 index 00000000000..e5eb0b34d8a --- /dev/null +++ b/packages/angular-query/schematics/ng-add/index.ts @@ -0,0 +1,143 @@ +import { addRootProvider } from '@schematics/angular/utility' +import { + NodeDependencyType, + addPackageJsonDependency, +} from '@schematics/angular/utility/dependencies' +import { + getAppModulePath, + isStandaloneApp, +} from '@schematics/angular/utility/ng-ast-utils' +import { findAppConfig } from '@schematics/angular/utility/standalone/app_config' +import { + findBootstrapApplicationCall, + getMainFilePath, +} from '@schematics/angular/utility/standalone/util' +import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks' +import { chain } from '@angular-devkit/schematics' +import ts from 'typescript' +import type { Rule, Tree } from '@angular-devkit/schematics' +import type { Schema } from './schema' + +// Replaced with package metadata by scripts/inject-schematic-package-info.js. +// The published schematic contains literal values and does not read package.json. +const PACKAGE_NAME = '__ANGULAR_QUERY_PACKAGE_NAME__' +const DEVTOOLS_PACKAGE_NAME = '__ANGULAR_QUERY_DEVTOOLS_PACKAGE_NAME__' +const DEVTOOLS_PACKAGE_VERSION = '__ANGULAR_QUERY_DEVTOOLS_PACKAGE_VERSION__' + +function hasPackageDependency(tree: Tree, name: string) { + const packageJsonPath = tree.exists('/package.json') + ? '/package.json' + : 'package.json' + const packageJson = JSON.parse(tree.readText(packageJsonPath)) as Record< + string, + Record | undefined + > + + return ['dependencies', 'devDependencies', 'optionalDependencies'].some( + (dependencyType) => packageJson[dependencyType]?.[name] !== undefined, + ) +} + +function addDevtoolsDependency(): Rule { + return (tree, context) => { + if (hasPackageDependency(tree, DEVTOOLS_PACKAGE_NAME)) return tree + + addPackageJsonDependency(tree, { + type: NodeDependencyType.Default, + name: DEVTOOLS_PACKAGE_NAME, + version: DEVTOOLS_PACKAGE_VERSION, + overwrite: false, + }) + context.addTask(new NodePackageInstallTask()) + return tree + } +} + +function hasProviderCall(tree: Tree, sourcePath: string) { + const source = ts.createSourceFile( + sourcePath, + tree.readText(sourcePath), + ts.ScriptTarget.Latest, + true, + ) + const localProviderNames = new Set() + + for (const statement of source.statements) { + if ( + !ts.isImportDeclaration(statement) || + !ts.isStringLiteral(statement.moduleSpecifier) || + statement.moduleSpecifier.text !== PACKAGE_NAME || + !statement.importClause?.namedBindings || + !ts.isNamedImports(statement.importClause.namedBindings) + ) { + continue + } + + for (const element of statement.importClause.namedBindings.elements) { + if ( + (element.propertyName ?? element.name).text === 'provideTanStackQuery' + ) { + localProviderNames.add(element.name.text) + } + } + } + + if (localProviderNames.size === 0) return false + + let found = false + const visit = (node: ts.Node) => { + if ( + ts.isCallExpression(node) && + ts.isIdentifier(node.expression) && + localProviderNames.has(node.expression.text) + ) { + found = true + return + } + + if (!found) ts.forEachChild(node, visit) + } + visit(source) + return found +} + +async function hasRootQueryProvider(tree: Tree, projectName: string) { + const mainFilePath = await getMainFilePath(tree, projectName) + const rootFilePath = isStandaloneApp(tree, mainFilePath) + ? (findAppConfig( + findBootstrapApplicationCall(tree, mainFilePath), + tree, + mainFilePath, + )?.filePath ?? mainFilePath) + : getAppModulePath(tree, mainFilePath) + + return hasProviderCall(tree, rootFilePath) +} + +function addQueryProvider(project: string): Rule { + return async (tree) => { + if (await hasRootQueryProvider(tree, project)) return tree + + return addRootProvider( + project, + ({ code, external }) => + code`${external( + 'provideTanStackQuery', + PACKAGE_NAME, + )}(() => new ${external( + 'QueryClient', + PACKAGE_NAME, + )}(), ${external('withDevtools', DEVTOOLS_PACKAGE_NAME)}())`, + ) + } +} + +/** + * Configures TanStack Query at the application root. + * + * The QueryClient factory is resolved by each root injector, preventing query + * caches from being shared between SSR requests. + */ +export function ngAdd(options: Schema): Rule { + return chain([addDevtoolsDependency(), addQueryProvider(options.project)]) +} diff --git a/packages/angular-query/schematics/ng-add/schema.json b/packages/angular-query/schematics/ng-add/schema.json new file mode 100644 index 00000000000..0585b15541e --- /dev/null +++ b/packages/angular-query/schematics/ng-add/schema.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "TanStackAngularQueryNgAdd", + "title": "TanStack Angular Query ng-add options", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "The Angular project to configure.", + "$default": { + "$source": "projectName" + } + } + }, + "required": ["project"], + "additionalProperties": false +} diff --git a/packages/angular-query/schematics/ng-add/schema.ts b/packages/angular-query/schematics/ng-add/schema.ts new file mode 100644 index 00000000000..748d4572c17 --- /dev/null +++ b/packages/angular-query/schematics/ng-add/schema.ts @@ -0,0 +1,3 @@ +export interface Schema { + project: string +} diff --git a/packages/angular-query/schematics/package.json b/packages/angular-query/schematics/package.json new file mode 100644 index 00000000000..5bbefffbabe --- /dev/null +++ b/packages/angular-query/schematics/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/packages/angular-query/scripts/inject-schematic-package-info.js b/packages/angular-query/scripts/inject-schematic-package-info.js new file mode 100644 index 00000000000..c2911f1ef2e --- /dev/null +++ b/packages/angular-query/scripts/inject-schematic-package-info.js @@ -0,0 +1,47 @@ +import fs from 'node:fs' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + +const packageDir = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +) +const queryPackage = readPackageJson(path.join(packageDir, 'package.json')) +const devtoolsPackage = readPackageJson( + path.resolve(packageDir, '../angular-query-devtools/package.json'), +) + +const outputPath = path.join(packageDir, 'dist/schematics/ng-add/index.js') +let output = fs.readFileSync(outputPath, 'utf8') + +const replacements = new Map([ + ['__ANGULAR_QUERY_PACKAGE_NAME__', queryPackage.name], + ['__ANGULAR_QUERY_DEVTOOLS_PACKAGE_NAME__', devtoolsPackage.name], + ['__ANGULAR_QUERY_DEVTOOLS_PACKAGE_VERSION__', devtoolsPackage.version], +]) + +for (const [placeholder, value] of replacements) { + if (output.includes(placeholder)) { + output = output.replaceAll(placeholder, value) + } else if (!output.includes(value)) { + throw new Error( + `Could not find ${placeholder} or its expected value in ${outputPath}.`, + ) + } +} + +fs.writeFileSync(outputPath, output) + +/** + * @param {string} packageJsonPath + * @returns {{ name: string; version: string }} + */ +function readPackageJson(packageJsonPath) { + const value = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) + + if (typeof value.name !== 'string' || typeof value.version !== 'string') { + throw new Error(`Invalid package metadata in ${packageJsonPath}.`) + } + + return value +} diff --git a/packages/angular-query/scripts/prepack.js b/packages/angular-query/scripts/prepack.js new file mode 100644 index 00000000000..033e008dc09 --- /dev/null +++ b/packages/angular-query/scripts/prepack.js @@ -0,0 +1,116 @@ +import fs from 'node:fs' +import path from 'node:path' +import './inject-schematic-package-info.js' + +/** + * Prepack script that prepares the package for publishing by: + * 1. Creating a modified package.json without dev dependencies, publishConfig and build scripts + * 2. Updating file paths to remove 'dist/' prefixes (since files will be at root in published package) + * 3. Writing this modified package.json to the `dist` directory + * 4. Copying additional files like README.md to the dist directory + * + * Type declarations need to be in the package root or corresponding sub-path to support + * sub-path exports in applications still using `moduleResolution: node`. + */ + +console.log('Running prepack script') + +/** + * Files to copy to the dist directory + * @type {string[]} + */ +const FILES_TO_COPY = [ + 'README.md', + 'schematics/collection.json', + 'schematics/package.json', + 'schematics/ng-add/schema.json', +] + +/** + * Fields to remove from the package.json copy + * @type {string[]} + */ +const FIELDS_TO_REMOVE = [ + 'devDependencies', + 'files', + 'publishConfig', + 'scripts', +] + +/** + * Replaces 'dist/' or './dist/' prefix from a file path with './' + * Only matches at the start of the path to avoid false matches + * @param {string} filePath - The file path to process + * @returns {string} The path without dist prefix + */ +function replaceDist(filePath) { + // Only match dist/ at the beginning of the path, followed by a filename + // This prevents matching strings like "distributed/file.js" or "some/dist/path" + return filePath.replace(/^(?:\.\/)?dist\/(?=.+)/, './') +} + +/** + * Recursively processes package.json `exports` to remove dist prefixes + * @param {Record} exports - The exports object to process + * @returns {Record} The processed exports object + */ +function processExports(exports) { + return Object.fromEntries( + Object.entries(exports) + .filter(([key]) => key !== '@tanstack/custom-condition') + .map(([key, value]) => [ + key, + typeof value === 'string' + ? replaceDist(value) + : typeof value === 'object' && value !== null + ? processExports(value) + : value, + ]), + ) +} + +console.log('Copying modified package.json') + +/** @type {Record} */ +const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')) + +const modifiedPackageJson = { ...packageJson } + +if (modifiedPackageJson.types) { + modifiedPackageJson.types = replaceDist(modifiedPackageJson.types) +} + +if (modifiedPackageJson.module) { + modifiedPackageJson.module = replaceDist(modifiedPackageJson.module) +} + +if (modifiedPackageJson.exports) { + modifiedPackageJson.exports = processExports(modifiedPackageJson.exports) +} + +for (const field of FIELDS_TO_REMOVE) { + delete modifiedPackageJson[field] +} + +if (!fs.existsSync('dist')) { + fs.mkdirSync('dist', { recursive: true }) +} + +fs.writeFileSync( + path.join('dist', 'package.json'), + JSON.stringify(modifiedPackageJson, null, 2), +) + +console.log('Copying other files') +for (const file of FILES_TO_COPY) { + if (fs.existsSync(file)) { + const destination = path.join('dist', file) + fs.mkdirSync(path.dirname(destination), { recursive: true }) + fs.copyFileSync(file, destination) + console.log(`${file}`) + } else { + console.log(`${file} not found, skipping`) + } +} + +console.log('prepack complete') diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts b/packages/angular-query/src/__tests__/infinite-query-options.test-d.ts similarity index 100% rename from packages/angular-query-experimental/src/__tests__/infinite-query-options.test-d.ts rename to packages/angular-query/src/__tests__/infinite-query-options.test-d.ts diff --git a/packages/angular-query-experimental/src/__tests__/infinite-query-options.test.ts b/packages/angular-query/src/__tests__/infinite-query-options.test.ts similarity index 100% rename from packages/angular-query-experimental/src/__tests__/infinite-query-options.test.ts rename to packages/angular-query/src/__tests__/infinite-query-options.test.ts diff --git a/packages/angular-query/src/__tests__/inject-infinite-query.test-d.ts b/packages/angular-query/src/__tests__/inject-infinite-query.test-d.ts new file mode 100644 index 00000000000..4ea0a46baf3 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-infinite-query.test-d.ts @@ -0,0 +1,176 @@ +import { describe, expectTypeOf, it } from 'vitest' +import { injectInfiniteQuery, skipToken, toResource } from '..' +import type { CreateInfiniteQueryOptions } from '..' +import type { Signal } from '@angular/core' +import type { InfiniteData } from '@tanstack/query-core' + +describe('injectInfiniteQuery', () => { + it('should expose status predicates as Angular signals', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + expectTypeOf(query.isSuccess).toMatchTypeOf>() + expectTypeOf(query.isError).toMatchTypeOf>() + expectTypeOf(query.isPending).toMatchTypeOf>() + }) + + describe('initialData', () => { + it('should narrow a defined result after an isError check', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialData: { pages: ['initial data'], pageParams: [0] }, + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + if (query.isError()) { + expectTypeOf(query.error).toEqualTypeOf>() + } + + expectTypeOf(toResource(query).value).toEqualTypeOf< + Signal> + >() + + // @ts-expect-error Resources are created explicitly with toResource. + query.resource + }) + + it('should keep data possibly undefined when initialData is undefined', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialData: undefined, + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + expectTypeOf(query.data).toEqualTypeOf< + Signal | Signal> + >() + }) + }) + + describe('Discriminated union return type', () => { + it('data should be possibly undefined by default', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + expectTypeOf(query.data).toEqualTypeOf< + Signal | Signal> + >() + }) + + it('data should be defined when query is success', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + if (query.isSuccess()) { + expectTypeOf(query.data).toEqualTypeOf< + Signal> + >() + expectTypeOf(toResource(query).value).toEqualTypeOf< + Signal> + >() + } + }) + + it('error should be null when query is success', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + if (query.isSuccess()) { + expectTypeOf(query.error).toEqualTypeOf>() + } + }) + + it('data should be undefined when query is pending', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + if (query.isPending()) { + expectTypeOf(query.data).toEqualTypeOf>() + } + }) + + it('error should be defined when query is error', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + + if (query.isError()) { + expectTypeOf(query.error).toEqualTypeOf>() + } + }) + }) + + it('should provide the correct types to the select function', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => Promise.resolve('data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + select: (data) => { + expectTypeOf(data).toEqualTypeOf>() + return data + }, + })) + + expectTypeOf(query.data).toEqualTypeOf< + Signal | Signal> + >() + }) + + it('should work when queryFn is skipToken', () => { + const query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: skipToken, + initialPageParam: 0, + getNextPageParam: () => 1, + })) + + // TPageParam falls back to `unknown` here (unlike `infiniteQueryOptions`, which infers + // `number` for the same options) because there's no skipToken-specific overload to carry it. + expectTypeOf(query.data()).toEqualTypeOf< + InfiniteData | undefined + >() + }) +}) + +describe('injectInfiniteQuery options', () => { + it('omits observer error reporting options', () => { + expectTypeOf().not.toHaveProperty( + 'throwOnError', + ) + }) +}) diff --git a/packages/angular-query/src/__tests__/inject-infinite-query.test.ts b/packages/angular-query/src/__tests__/inject-infinite-query.test.ts new file mode 100644 index 00000000000..0ad7203b095 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-infinite-query.test.ts @@ -0,0 +1,188 @@ +import { TestBed } from '@angular/core/testing' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { ChangeDetectionStrategy, Component, signal } from '@angular/core' +import { sleep } from '@tanstack/query-test-utils' +import { QueryClient, injectInfiniteQuery, skipToken } from '..' +import { expectSignals, setupTanStackQueryTestBed } from './test-utils' + +describe('injectInfiniteQuery', () => { + let queryClient: QueryClient + + beforeEach(() => { + queryClient = new QueryClient() + vi.useFakeTimers() + setupTanStackQueryTestBed(queryClient) + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it.each(['refetch', 'fetchNextPage', 'fetchPreviousPage'] as const)( + 'applies current infinite-query options before %s', + async (method) => { + vi.useRealTimers() + const version = signal('old') + const query = TestBed.runInInjectionContext(() => + injectInfiniteQuery(() => { + const current = version() + return { + queryKey: ['infinite'], + enabled: false, + initialPageParam: 0, + initialData: { pages: ['initial'], pageParams: [0] }, + getNextPageParam: () => 1, + getPreviousPageParam: () => -1, + queryFn: async () => current, + } + }), + ) + query.data() + const execute = query[method] + version.set('new') + const result = await execute() + expect(result.data?.pages).toContain('new') + expect(result.data?.pages).not.toContain('old') + }, + ) + + it('should properly execute infinite query', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteQuery'], + queryFn: ({ pageParam }) => + sleep(10).then(() => 'data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expectSignals(query, { + data: undefined, + status: 'pending', + }) + + await vi.advanceTimersByTimeAsync(11) + + expectSignals(query, { + data: { + pageParams: [0], + pages: ['data on page 0'], + }, + status: 'success', + }) + + void query.fetchNextPage() + + await vi.advanceTimersByTimeAsync(11) + + expectSignals(query, { + data: { + pageParams: [0, 12], + pages: ['data on page 0', 'data on page 12'], + }, + status: 'success', + }) + }) + + it('should keep initialData visible alongside the error when a refetch fails', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectInfiniteQuery(() => ({ + queryKey: ['infiniteInitialDataError'], + queryFn: () => + sleep(10).then(() => Promise.reject(new Error('Some error'))), + initialData: { pages: [1], pageParams: [1] }, + getNextPageParam: (lastPage: number) => lastPage + 1, + initialPageParam: 0, + retry: false, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(query.data()?.pages).toEqual([1]) + expect(query.isError()).toBe(false) + expect(query.status()).toBe('success') + + await vi.advanceTimersByTimeAsync(11) + fixture.detectChanges() + + expect(query.data()?.pages).toEqual([1]) + expect(query.isError()).toBe(true) + expect(query.status()).toBe('error') + }) + + it('should not fetch when queryFn is skipToken, and fetch once it is replaced', async () => { + const queryFn = vi.fn(({ pageParam }: { pageParam: number }) => + sleep(10).then(() => `comments for 1 page ${pageParam}`), + ) + const postId = signal(undefined) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectInfiniteQuery(() => ({ + queryKey: ['skipTokenInfinite'], + queryFn: postId() != null ? queryFn : skipToken, + initialPageParam: 0, + getNextPageParam: () => 12, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(query.status()).toBe('pending') + expect(query.isFetching()).toBe(false) + + await vi.advanceTimersByTimeAsync(11) + fixture.detectChanges() + expect(queryFn).not.toHaveBeenCalled() + expect(query.status()).toBe('pending') + expect(query.isFetching()).toBe(false) + + postId.set('1') + fixture.detectChanges() + expect(query.isFetching()).toBe(true) + + await vi.advanceTimersByTimeAsync(11) + fixture.detectChanges() + expect(queryFn).toHaveBeenCalledTimes(1) + expect(query.status()).toBe('success') + expect(query.data()?.pages).toEqual(['comments for 1 page 0']) + }) + + describe('injection context', () => { + it('throws NG0203 with descriptive error outside injection context', () => { + expect(() => { + injectInfiniteQuery(() => ({ + queryKey: ['injectionContextError'], + queryFn: ({ pageParam }) => + sleep(0).then(() => 'data on page ' + pageParam), + initialPageParam: 0, + getNextPageParam: () => 12, + })) + }).toThrowError(/NG0203(.*?)injectInfiniteQuery/) + }) + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts b/packages/angular-query/src/__tests__/inject-is-fetching.test.ts similarity index 64% rename from packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts rename to packages/angular-query/src/__tests__/inject-is-fetching.test.ts index db1f7aeea11..411e261a1be 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts +++ b/packages/angular-query/src/__tests__/inject-is-fetching.test.ts @@ -1,10 +1,6 @@ import { TestBed } from '@angular/core/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { - Component, - Injector, - provideZonelessChangeDetection, -} from '@angular/core' +import { Component, input, inputBinding, signal } from '@angular/core' import { render } from '@testing-library/angular' import { queryKey, sleep } from '@tanstack/query-test-utils' import { @@ -13,6 +9,7 @@ import { injectQuery, provideTanStackQuery, } from '..' +import { provideAngularQueryChangeDetection } from './test-utils' describe('injectIsFetching', () => { let queryClient: QueryClient @@ -23,8 +20,8 @@ describe('injectIsFetching', () => { TestBed.configureTestingModule({ providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), ], }) }) @@ -49,10 +46,6 @@ describe('injectIsFetching', () => { const rendered = await render(Page) - expect(rendered.getByText('fetching: 0')).toBeInTheDocument() - - await vi.advanceTimersByTimeAsync(0) - rendered.fixture.detectChanges() expect(rendered.getByText('fetching: 1')).toBeInTheDocument() await vi.advanceTimersByTimeAsync(101) @@ -60,6 +53,12 @@ describe('injectIsFetching', () => { expect(rendered.getByText('fetching: 0')).toBeInTheDocument() }) + it('should return a read-only signal', () => { + const isFetching = TestBed.runInInjectionContext(() => injectIsFetching()) + + expect(isFetching).not.toHaveProperty('set') + }) + it('should be able to filter by queryKey', async () => { const key1 = queryKey() const key2 = queryKey() @@ -76,7 +75,7 @@ describe('injectIsFetching', () => { queryKey: key2, queryFn: () => sleep(100).then(() => 'test2'), })) - readonly isFetching = injectIsFetching({ queryKey: key1 }) + readonly isFetching = injectIsFetching(() => ({ queryKey: key1 })) } const rendered = await render(Page) @@ -90,19 +89,43 @@ describe('injectIsFetching', () => { expect(rendered.getByText('fetching: 0')).toBeInTheDocument() }) + it('should support signal reads in filter predicates', async () => { + const key = queryKey() + + void queryClient.query({ + queryKey: key, + queryFn: () => sleep(100).then(() => 'data'), + }) + + @Component({ + template: `
fetching: {{ isFetching() }}
`, + }) + class Page { + readonly includeQuery = input.required() + readonly isFetching = injectIsFetching(() => ({ + predicate: () => this.includeQuery(), + })) + } + + const subscribe = vi.spyOn(queryClient.getQueryCache(), 'subscribe') + const includeQuery = signal(true) + const rendered = await render(Page, { + bindings: [inputBinding('includeQuery', includeQuery)], + }) + + expect(rendered.getByText('fetching: 1')).toBeInTheDocument() + + includeQuery.set(false) + rendered.fixture.detectChanges() + expect(subscribe).toHaveBeenCalledTimes(1) + expect(rendered.getByText('fetching: 0')).toBeInTheDocument() + }) + describe('injection context', () => { it('should throw NG0203 with descriptive error outside injection context', () => { expect(() => { injectIsFetching() }).toThrow(/NG0203(.*?)injectIsFetching/) }) - - it('should be usable outside injection context when passing an injector', () => { - expect( - injectIsFetching(undefined, { - injector: TestBed.inject(Injector), - }), - ).not.toThrow() - }) }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts b/packages/angular-query/src/__tests__/inject-is-mutating.test.ts similarity index 67% rename from packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts rename to packages/angular-query/src/__tests__/inject-is-mutating.test.ts index 66fe678d03a..3bc7767be38 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts +++ b/packages/angular-query/src/__tests__/inject-is-mutating.test.ts @@ -1,10 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import { TestBed } from '@angular/core/testing' -import { - Component, - Injector, - provideZonelessChangeDetection, -} from '@angular/core' +import { Component, input, inputBinding, signal } from '@angular/core' import { render } from '@testing-library/angular' import { queryKey, sleep } from '@tanstack/query-test-utils' import { @@ -13,6 +9,7 @@ import { injectMutation, provideTanStackQuery, } from '..' +import { provideAngularQueryChangeDetection } from './test-utils' describe('injectIsMutating', () => { let queryClient: QueryClient @@ -23,8 +20,8 @@ describe('injectIsMutating', () => { TestBed.configureTestingModule({ providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), ], }) }) @@ -78,7 +75,7 @@ describe('injectIsMutating', () => { mutationKey: key2, mutationFn: () => sleep(100).then(() => 'data2'), })) - readonly isMutating = injectIsMutating({ mutationKey: key1 }) + readonly isMutating = injectIsMutating(() => ({ mutationKey: key1 })) } const rendered = await render(Page) @@ -95,19 +92,41 @@ describe('injectIsMutating', () => { expect(rendered.getByText('mutating: 0')).toBeInTheDocument() }) + it('should support signal reads in filter predicates', async () => { + const mutation = queryClient.getMutationCache().build(queryClient, { + mutationFn: () => sleep(100).then(() => 'data'), + }) + void mutation.execute(undefined) + + @Component({ + template: `
mutating: {{ isMutating() }}
`, + }) + class Page { + readonly includeMutation = input.required() + readonly isMutating = injectIsMutating(() => ({ + predicate: () => this.includeMutation(), + })) + } + + const subscribe = vi.spyOn(queryClient.getMutationCache(), 'subscribe') + const includeMutation = signal(true) + const rendered = await render(Page, { + bindings: [inputBinding('includeMutation', includeMutation)], + }) + + expect(rendered.getByText('mutating: 1')).toBeInTheDocument() + + includeMutation.set(false) + rendered.fixture.detectChanges() + expect(subscribe).toHaveBeenCalledTimes(1) + expect(rendered.getByText('mutating: 0')).toBeInTheDocument() + }) + describe('injection context', () => { it('should throw NG0203 with descriptive error outside injection context', () => { expect(() => { injectIsMutating() }).toThrow(/NG0203(.*?)injectIsMutating/) }) - - it('should be usable outside injection context when passing an injector', () => { - expect( - injectIsMutating(undefined, { - injector: TestBed.inject(Injector), - }), - ).not.toThrow() - }) }) }) diff --git a/packages/angular-query/src/__tests__/inject-is-restoring.test.ts b/packages/angular-query/src/__tests__/inject-is-restoring.test.ts new file mode 100644 index 00000000000..ca55f78a03e --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-is-restoring.test.ts @@ -0,0 +1,51 @@ +import { TestBed } from '@angular/core/testing' +import { describe, expect, it } from 'vitest' +import { InjectionToken, inject, signal } from '@angular/core' +import { provideIsRestoring } from '../internal' +import { QueryClient, injectIsRestoring, provideTanStackQuery } from '..' +import { provideAngularQueryChangeDetection } from './test-utils' + +describe('injectIsRestoring', () => { + let queryClient: QueryClient + + it('returns false by default when provideIsRestoring is not used', () => { + queryClient = new QueryClient() + + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + const isRestoring = TestBed.runInInjectionContext(() => { + return injectIsRestoring() + }) + + expect(isRestoring()).toBe(false) + }) + + it('resolves a restoration signal factory in the injection context', () => { + const state = signal(true) + const token = new InjectionToken('restoration state') + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + { provide: token, useValue: state }, + provideIsRestoring(() => inject(token).asReadonly()), + ], + }) + + const isRestoring = TestBed.runInInjectionContext(injectIsRestoring) + expect(isRestoring()).toBe(true) + state.set(false) + expect(isRestoring()).toBe(false) + expect(TestBed.runInInjectionContext(injectIsRestoring)).toBe(isRestoring) + }) + + it('throws NG0203 with descriptive error outside injection context', () => { + expect(() => { + injectIsRestoring() + }).toThrow(/NG0203(.*?)injectIsRestoring/) + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test-d.ts b/packages/angular-query/src/__tests__/inject-mutation-state.test-d.ts similarity index 54% rename from packages/angular-query-experimental/src/__tests__/inject-mutation-state.test-d.ts rename to packages/angular-query/src/__tests__/inject-mutation-state.test-d.ts index 0fbd5d8f652..7fbf73c47ac 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test-d.ts +++ b/packages/angular-query/src/__tests__/inject-mutation-state.test-d.ts @@ -1,6 +1,6 @@ import { describe, expectTypeOf, it } from 'vitest' import { injectMutationState } from '..' -import type { MutationState, MutationStatus } from '..' +import type { Mutation, MutationState, MutationStatus } from '..' describe('injectMutationState', () => { it('should default to QueryState', () => { @@ -19,4 +19,22 @@ describe('injectMutationState', () => { expectTypeOf(result()).toEqualTypeOf>() }) + + it('should infer the mutation type from a mutation-state result', () => { + type Result = MutationState< + string, + Error, + { id: number }, + { previous: string } + > + + injectMutationState(() => ({ + select: (mutation) => { + expectTypeOf(mutation).toEqualTypeOf< + Mutation + >() + return mutation.state + }, + })) + }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts b/packages/angular-query/src/__tests__/inject-mutation-state.test.ts similarity index 55% rename from packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts rename to packages/angular-query/src/__tests__/inject-mutation-state.test.ts index 4eee7699aeb..89f0330bb94 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts +++ b/packages/angular-query/src/__tests__/inject-mutation-state.test.ts @@ -1,21 +1,21 @@ import { + ChangeDetectionStrategy, Component, - Injector, input, - provideZonelessChangeDetection, + inputBinding, signal, } from '@angular/core' import { TestBed } from '@angular/core/testing' +import { render } from '@testing-library/angular' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { By } from '@angular/platform-browser' -import { queryKey, sleep } from '@tanstack/query-test-utils' +import { sleep } from '@tanstack/query-test-utils' import { QueryClient, injectMutation, injectMutationState, provideTanStackQuery, } from '..' -import { setFixtureSignalInputs } from './test-utils' +import { provideAngularQueryChangeDetection } from './test-utils' describe('injectMutationState', () => { let queryClient: QueryClient @@ -25,8 +25,8 @@ describe('injectMutationState', () => { vi.useFakeTimers() TestBed.configureTestingModule({ providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), ], }) }) @@ -35,9 +35,50 @@ describe('injectMutationState', () => { vi.useRealTimers() }) + it('publishes selected mutation values unchanged and compares snapshot entries by identity', () => { + const version = signal('before') + queryClient.getMutationCache().build(queryClient, { mutationKey: ['a'] }) + queryClient.getMutationCache().build(queryClient, { mutationKey: ['b'] }) + let selected: Array<{ value: string }> = [] + const states = TestBed.runInInjectionContext(() => + injectMutationState(() => { + selected = [] + return { + select: (m) => { + const value = { + value: + m.options.mutationKey![0] === 'a' ? version() : 'unchanged', + } + selected.push(value) + return value + }, + } + }), + ) + const before = states() + version.set('after') + const after = states() + expect(after[0]).toBe(selected[0]) + expect(after[1]).toBe(selected[1]) + expect(after[1]).not.toBe(before[1]) + // Stable entries avoid array-only updates, without deep comparison. + const filter = signal('a') + const selectedValue = { value: 'same' } + const filtered = TestBed.runInInjectionContext(() => + injectMutationState(() => ({ + filters: { mutationKey: [filter()] }, + select: () => selectedValue, + })), + ) + const initial = filtered() + filter.set('b') + expect(filtered()).toBe(initial) + expect(filtered()[0]).toBe(selectedValue) + }) + describe('injectMutationState', () => { it('should return variables after calling mutate 1', () => { - const mutationKey = queryKey() + const mutationKey = ['mutation'] const variables = 'foo123' const mutation = TestBed.runInInjectionContext(() => { @@ -59,9 +100,9 @@ describe('injectMutationState', () => { expect(mutationState()).toEqual([variables]) }) - it('should update injectMutationState when reactive options change', () => { - const mutationKey1 = queryKey() - const mutationKey2 = queryKey() + it('reactive options should update injectMutationState', () => { + const mutationKey1 = ['mutation1'] + const mutationKey2 = ['mutation2'] const variables1 = 'foo123' const variables2 = 'bar234' @@ -96,9 +137,28 @@ describe('injectMutationState', () => { expect(mutationState()).toEqual([variables2]) }) + it('preserves result identity when an unrelated mutation changes', () => { + const mutationState = TestBed.runInInjectionContext(() => + injectMutationState(() => ({ + filters: { mutationKey: ['matching'] }, + })), + ) + const unrelatedMutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationKey: ['unrelated'], + mutationFn: () => Promise.resolve(), + })), + ) + const initialResult = mutationState() + + unrelatedMutation.mutate() + + expect(mutationState()).toBe(initialResult) + }) + it('should return variables after calling mutate 2', () => { queryClient.clear() - const mutationKey = queryKey() + const mutationKey = ['mutation'] const variables = 'bar234' const mutation = TestBed.runInInjectionContext(() => { @@ -145,6 +205,7 @@ describe('injectMutationState', () => { {{ mutation.status }} } `, + changeDetection: ChangeDetectionStrategy.OnPush, }) class FakeComponent { name = input.required() @@ -157,41 +218,34 @@ describe('injectMutationState', () => { })) } - const fixture = TestBed.createComponent(FakeComponent) - const { debugElement } = fixture - setFixtureSignalInputs(fixture, { name: fakeName }) - await vi.advanceTimersByTimeAsync(0) + TestBed.resetTestingModule() + const name = signal(fakeName) + const rendered = await render(FakeComponent, { + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() - let spans = debugElement - .queryAll(By.css('span')) - .map((span) => span.nativeNode.textContent) + await vi.advanceTimersByTimeAsync(0) - expect(spans).toEqual(['pending', 'pending']) + expect(rendered.getAllByText('pending')).toHaveLength(2) await vi.advanceTimersByTimeAsync(11) - fixture.detectChanges() - - spans = debugElement - .queryAll(By.css('span')) - .map((span) => span.nativeNode.textContent) + rendered.fixture.detectChanges() - expect(spans).toEqual(['success', 'error']) + expect(rendered.getByText('success')).toBeInTheDocument() + expect(rendered.getByText('error')).toBeInTheDocument() }) describe('injection context', () => { - it('should throw NG0203 with descriptive error outside injection context', () => { + it('throws NG0203 with descriptive error outside injection context', () => { expect(() => { injectMutationState() - }).toThrow(/NG0203(.*?)injectMutationState/) - }) - - it('should be usable outside injection context when passing an injector', () => { - const injector = TestBed.inject(Injector) - expect( - injectMutationState(undefined, { - injector, - }), - ).not.toThrow() + }).toThrowError(/NG0203(.*?)injectMutationState/) }) }) }) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts b/packages/angular-query/src/__tests__/inject-mutation.test-d.ts similarity index 95% rename from packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts rename to packages/angular-query/src/__tests__/inject-mutation.test-d.ts index cae6bac1af6..489fa0302c1 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test-d.ts +++ b/packages/angular-query/src/__tests__/inject-mutation.test-d.ts @@ -1,6 +1,7 @@ import { describe, expectTypeOf, it } from 'vitest' import { sleep } from '@tanstack/query-test-utils' import { injectMutation } from '..' +import type { CreateMutationOptions } from '..' import type { Signal } from '@angular/core' import type { MutationFunctionContext, @@ -156,3 +157,9 @@ describe('injectMutation', () => { })) }) }) + +describe('injectMutation options', () => { + it('omits observer error reporting options', () => { + expectTypeOf().not.toHaveProperty('throwOnError') + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts b/packages/angular-query/src/__tests__/inject-mutation.test.ts similarity index 62% rename from packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts rename to packages/angular-query/src/__tests__/inject-mutation.test.ts index b54dc3735ea..56c7cff3dc5 100644 --- a/packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts +++ b/packages/angular-query/src/__tests__/inject-mutation.test.ts @@ -1,19 +1,27 @@ import { ApplicationRef, + ChangeDetectionStrategy, Component, - Injector, + ErrorHandler, + effect, input, - provideZonelessChangeDetection, + inputBinding, signal, } from '@angular/core' import { TestBed } from '@angular/core/testing' -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { By } from '@angular/platform-browser' import { render } from '@testing-library/angular' import { queryKey, sleep } from '@tanstack/query-test-utils' -import { noop } from '@tanstack/query-core' -import { QueryClient, injectMutation, provideTanStackQuery } from '..' -import { expectSignals, setFixtureSignalInputs } from './test-utils' +import { firstValueFrom } from 'rxjs' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + QueryClient, + injectIsMutating, + injectMutation, + injectMutationState, + injectQuery, + provideTanStackQuery, +} from '..' +import { expectSignals, provideAngularQueryChangeDetection } from './test-utils' describe('injectMutation', () => { let queryClient: QueryClient @@ -23,8 +31,8 @@ describe('injectMutation', () => { vi.useFakeTimers() TestBed.configureTestingModule({ providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), ], }) }) @@ -33,6 +41,39 @@ describe('injectMutation', () => { vi.useRealTimers() }) + it('publishes mutation failures to observers and cache signals without global reporting', async () => { + vi.useRealTimers() + queryClient.setDefaultOptions({ mutations: { throwOnError: true } }) + const cacheReport = vi.fn() + queryClient.getMutationCache().config.onError = cacheReport + const report = vi.spyOn(TestBed.inject(ErrorHandler), 'handleError') + const error = new Error('mutation failed') + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: async () => { + throw error + }, + })), + ) + const count = TestBed.runInInjectionContext(() => injectIsMutating()) + const states = TestBed.runInInjectionContext(() => injectMutationState()) + await TestBed.inject(ApplicationRef).whenStable() + const promise = mutation.mutateAsync() + expect(mutation.status()).toBe('pending') + expect(count()).toBe(1) + expect(states()[0]?.status).toBe('pending') + await expect(promise).rejects.toBe(error) + expect(mutation.error()).toBe(error) + expect(mutation.status()).toBe('error') + expect(count()).toBe(0) + expect(states()[0]?.status).toBe('error') + expect(cacheReport).toHaveBeenCalledTimes(1) + expect(report).not.toHaveBeenCalled() + mutation.mutate() + await TestBed.inject(ApplicationRef).whenStable() + expect(mutation.error()).toBe(error) + }) + it('should be in idle state initially', () => { const mutation = TestBed.runInInjectionContext(() => { return injectMutation(() => ({ @@ -51,110 +92,76 @@ describe('injectMutation', () => { it('should change state after invoking mutate', async () => { const result = 'Mock data' - @Component({ - template: ` -
isIdle: {{ mutation.isIdle() }}
-
isPending: {{ mutation.isPending() }}
-
isError: {{ mutation.isError() }}
-
isSuccess: {{ mutation.isSuccess() }}
-
data: {{ mutation.data() ?? 'none' }}
-
error: {{ mutation.error()?.message ?? 'none' }}
- `, - }) - class Page { - readonly mutation = injectMutation(() => ({ + const mutation = TestBed.runInInjectionContext(() => { + return injectMutation(() => ({ mutationFn: (params: string) => sleep(10).then(() => params), })) - } + }) - const rendered = await render(Page) + TestBed.tick() - rendered.fixture.componentInstance.mutation.mutate(result) + mutation.mutate(result) await vi.advanceTimersByTimeAsync(0) - rendered.fixture.detectChanges() - expect(rendered.getByText('isIdle: false')).toBeInTheDocument() - expect(rendered.getByText('isPending: true')).toBeInTheDocument() - expect(rendered.getByText('isError: false')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: false')).toBeInTheDocument() - expect(rendered.getByText('data: none')).toBeInTheDocument() - expect(rendered.getByText('error: none')).toBeInTheDocument() + expectSignals(mutation, { + isIdle: false, + isPending: true, + isError: false, + isSuccess: false, + data: undefined, + error: null, + }) }) it('should return error when request fails', async () => { - @Component({ - template: ` -
isIdle: {{ mutation.isIdle() }}
-
isPending: {{ mutation.isPending() }}
-
isError: {{ mutation.isError() }}
-
isSuccess: {{ mutation.isSuccess() }}
-
data: {{ mutation.data() ?? 'none' }}
-
error: {{ mutation.error()?.message ?? 'none' }}
- `, - }) - class Page { - readonly mutation = injectMutation(() => ({ + const mutation = TestBed.runInInjectionContext(() => { + return injectMutation(() => ({ mutationFn: () => sleep(10).then(() => Promise.reject(new Error('Some error'))), })) - } - - const rendered = await render(Page) + }) - rendered.fixture.componentInstance.mutation.mutate() + mutation.mutate() await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('isIdle: false')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(rendered.getByText('isError: true')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: false')).toBeInTheDocument() - expect(rendered.getByText('data: none')).toBeInTheDocument() - expect(rendered.getByText('error: Some error')).toBeInTheDocument() + expectSignals(mutation, { + isIdle: false, + isPending: false, + isError: true, + isSuccess: false, + data: undefined, + error: Error('Some error'), + }) }) it('should return data when request succeeds', async () => { const result = 'Mock data' - - @Component({ - template: ` -
isIdle: {{ mutation.isIdle() }}
-
isPending: {{ mutation.isPending() }}
-
isError: {{ mutation.isError() }}
-
isSuccess: {{ mutation.isSuccess() }}
-
data: {{ mutation.data() ?? 'none' }}
-
error: {{ mutation.error()?.message ?? 'none' }}
- `, - }) - class Page { - readonly mutation = injectMutation(() => ({ + const mutation = TestBed.runInInjectionContext(() => { + return injectMutation(() => ({ mutationFn: (params: string) => sleep(10).then(() => params), })) - } - - const rendered = await render(Page) + }) - rendered.fixture.componentInstance.mutation.mutate(result) + mutation.mutate(result) await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() - expect(rendered.getByText('isIdle: false')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(rendered.getByText('isError: false')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: true')).toBeInTheDocument() - expect(rendered.getByText(`data: ${result}`)).toBeInTheDocument() - expect(rendered.getByText('error: none')).toBeInTheDocument() + expectSignals(mutation, { + isIdle: false, + isPending: false, + isError: false, + isSuccess: true, + data: result, + error: null, + }) }) - it('should update mutation when reactive options change', () => { + it('reactive options should update mutation', () => { const mutationCache = queryClient.getMutationCache() // Signal will be updated before the mutation is called // this test confirms that the mutation uses the updated value - const key1 = queryKey() - const key2 = queryKey() - const mutationKey = signal(key1) + const mutationKey = signal(['1']) const mutation = TestBed.runInInjectionContext(() => { return injectMutation(() => ({ mutationKey: mutationKey(), @@ -162,54 +169,123 @@ describe('injectMutation', () => { })) }) - mutationKey.set(key2) + mutationKey.set(['2']) mutation.mutate('xyz') - const mutations = mutationCache.find({ mutationKey: key2 }) + const mutations = mutationCache.find({ mutationKey: ['2'] }) - expect(mutations?.options.mutationKey).toEqual(key2) + expect(mutations?.options.mutationKey).toEqual(['2']) }) - it('should reset state after invoking mutation.reset', async () => { - @Component({ - template: ` -
isIdle: {{ mutation.isIdle() }}
-
isPending: {{ mutation.isPending() }}
-
isError: {{ mutation.isError() }}
-
isSuccess: {{ mutation.isSuccess() }}
-
data: {{ mutation.data() ?? 'none' }}
-
error: {{ mutation.error()?.message ?? 'none' }}
- `, - }) - class Page { + it('reflects a mutation started in ngOnInit', async () => { + const statusesInsideOnMutate: Array = [] + + @Component({ template: '' }) + class TestComponent { readonly mutation = injectMutation(() => ({ - mutationFn: () => - sleep(10).then(() => Promise.reject(new Error('Some error'))), + mutationFn: () => sleep(10).then(() => 'done'), + onMutate: () => { + statusesInsideOnMutate.push(this.mutation.status()) + }, })) + + ngOnInit() { + this.mutation.mutate() + } } - const rendered = await render(Page) + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() - rendered.fixture.componentInstance.mutation.mutate() + expect(statusesInsideOnMutate).toEqual(['pending']) + expect(fixture.componentInstance.mutation.status()).toBe('pending') await vi.advanceTimersByTimeAsync(11) - rendered.fixture.detectChanges() + TestBed.tick() + expect(fixture.componentInstance.mutation.status()).toBe('success') + }) + + it('allows cache listeners to read the result while options update', () => { + const mutationKey = signal('one') + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationKey: ['reentrant-options', mutationKey()], + mutationFn: () => Promise.resolve(), + })), + ) + + mutation.status() + TestBed.tick() + + const readResult = vi.fn(() => mutation.status()) + const unsubscribe = queryClient.getMutationCache().subscribe((event) => { + // setOptions emits synchronously; this read must not re-enter the signal + // computation that caused the options update. + if (event.type === 'observerOptionsUpdated') readResult() + }) + + mutationKey.set('two') + + expect(() => TestBed.tick()).not.toThrow() + expect(readResult).toHaveBeenCalled() + + unsubscribe() + }) + + it('does not track mutation state when mutating from a reactive context', async () => { + const trigger = signal(false) + const runs = vi.fn() + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: () => sleep(10), + })), + ) + + TestBed.runInInjectionContext(() => + effect(() => { + runs() + if (trigger()) mutation.mutate() + }), + ) + TestBed.tick() + expect(runs).toHaveBeenCalledOnce() + + trigger.set(true) + TestBed.tick() + expect(runs).toHaveBeenCalledTimes(2) - expect(rendered.getByText('isError: true')).toBeInTheDocument() - expect(rendered.getByText('error: Some error')).toBeInTheDocument() + await vi.advanceTimersByTimeAsync(11) + TestBed.tick() + expect(runs).toHaveBeenCalledTimes(2) + }) + + it('should reset state after invoking mutation.reset', async () => { + const mutation = TestBed.runInInjectionContext(() => { + return injectMutation(() => ({ + mutationFn: () => + sleep(10).then(() => Promise.reject(new Error('Some error'))), + })) + }) - rendered.fixture.componentInstance.mutation.reset() + mutation.mutate() + + await vi.advanceTimersByTimeAsync(11) + + expect(mutation.isError()).toBe(true) + + mutation.reset() await vi.advanceTimersByTimeAsync(0) - rendered.fixture.detectChanges() - expect(rendered.getByText('isIdle: true')).toBeInTheDocument() - expect(rendered.getByText('isPending: false')).toBeInTheDocument() - expect(rendered.getByText('isError: false')).toBeInTheDocument() - expect(rendered.getByText('isSuccess: false')).toBeInTheDocument() - expect(rendered.getByText('data: none')).toBeInTheDocument() - expect(rendered.getByText('error: none')).toBeInTheDocument() + expectSignals(mutation, { + isIdle: true, + isPending: false, + isError: false, + isSuccess: false, + data: undefined, + error: null, + }) }) describe('side effects', () => { @@ -328,142 +404,6 @@ describe('injectMutation', () => { expect(onSettled).toHaveBeenCalledTimes(1) }) - it('should call onSuccess and onSettled when passed as arguments of mutate function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (params: string) => sleep(10).then(() => params), - })) - }) - - mutation.mutate('', { - onSuccess: () => callbacks.push('mutate.onSuccess'), - onSettled: () => callbacks.push('mutate.onSettled'), - }) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual(['mutate.onSuccess', 'mutate.onSettled']) - }) - - it('should call onError and onSettled when passed as arguments of mutate function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (_params: string) => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - })) - }) - - mutation.mutate('', { - onError: () => callbacks.push('mutate.onError'), - onSettled: () => callbacks.push('mutate.onSettled'), - }) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual(['mutate.onError', 'mutate.onSettled']) - }) - - it('should call onSuccess and onSettled when passed as arguments of mutateAsync function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (params: string) => sleep(10).then(() => params), - })) - }) - - mutation.mutateAsync('', { - onSuccess: () => callbacks.push('mutateAsync.onSuccess'), - onSettled: () => callbacks.push('mutateAsync.onSettled'), - }) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual([ - 'mutateAsync.onSuccess', - 'mutateAsync.onSettled', - ]) - }) - - it('should call onError and onSettled when passed as arguments of mutateAsync function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (_params: string) => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - })) - }) - - mutation - .mutateAsync('', { - onError: () => callbacks.push('mutateAsync.onError'), - onSettled: () => callbacks.push('mutateAsync.onSettled'), - }) - .catch(noop) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual([ - 'mutateAsync.onError', - 'mutateAsync.onSettled', - ]) - }) - - it('should call onSuccess when passed as an argument of mutateAsync function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (params: string) => sleep(10).then(() => params), - })) - }) - - mutation.mutateAsync('', { - onSuccess: () => callbacks.push('mutateAsync.onSuccess'), - }) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual(['mutateAsync.onSuccess']) - }) - - it('should call onError when passed as an argument of mutateAsync function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (_params: string) => - sleep(10).then(() => Promise.reject(new Error('Some error'))), - })) - }) - - mutation - .mutateAsync('', { - onError: () => callbacks.push('mutateAsync.onError'), - }) - .catch(noop) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual(['mutateAsync.onError']) - }) - - it('should call onSettled when passed as an argument of mutateAsync function', async () => { - const callbacks: Array = [] - const mutation = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationFn: (params: string) => sleep(10).then(() => params), - })) - }) - - mutation.mutateAsync('', { - onSettled: () => callbacks.push('mutateAsync.onSettled'), - }) - - await vi.advanceTimersByTimeAsync(10) - - expect(callbacks).toEqual(['mutateAsync.onSettled']) - }) - it('should fire both onSettled functions', async () => { const onSettled = vi.fn() const onSettledOnFunction = vi.fn() @@ -600,6 +540,7 @@ describe('injectMutation', () => { {{ mutation.data() }} `, + changeDetection: ChangeDetectionStrategy.OnPush, }) class FakeComponent { name = input.required() @@ -614,20 +555,24 @@ describe('injectMutation', () => { } } - const fixture = TestBed.createComponent(FakeComponent) - const { debugElement } = fixture - setFixtureSignalInputs(fixture, { name: 'value' }) + const name = signal('value') + const rendered = await render(FakeComponent, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() - const button = debugElement.query(By.css('button')) - button.triggerEventHandler('click') + rendered.getByRole('button').click() await vi.advanceTimersByTimeAsync(11) - fixture.detectChanges() + rendered.fixture.detectChanges() - const text = debugElement.query(By.css('span')).nativeElement.textContent - expect(text).toEqual('value') - const mutation = mutationCache.find({ mutationKey: ['fake', 'value'] }) - expect(mutation?.options.mutationKey).toStrictEqual(['fake', 'value']) + expect(rendered.getByText('value')).toBeInTheDocument() + const mutation = mutationCache.find({ + mutationKey: ['fake', 'value'], + }) + expect(mutation).toBeDefined() + expect(mutation!.options.mutationKey).toStrictEqual(['fake', 'value']) }) it('should update options on required signal input change', async () => { @@ -639,6 +584,7 @@ describe('injectMutation', () => { {{ mutation.data() }} `, + changeDetection: ChangeDetectionStrategy.OnPush, }) class FakeComponent { name = input.required() @@ -653,26 +599,27 @@ describe('injectMutation', () => { } } - const fixture = TestBed.createComponent(FakeComponent) - const { debugElement } = fixture - setFixtureSignalInputs(fixture, { name: 'value' }) - - const button = debugElement.query(By.css('button')) - const span = debugElement.query(By.css('span')) + const name = signal('value') + const rendered = await render(FakeComponent, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() - button.triggerEventHandler('click') + rendered.getByRole('button').click() await vi.advanceTimersByTimeAsync(11) - fixture.detectChanges() + rendered.fixture.detectChanges() - expect(span.nativeElement.textContent).toEqual('value') + expect(rendered.getByText('value')).toBeInTheDocument() - setFixtureSignalInputs(fixture, { name: 'updatedValue' }) + name.set('updatedValue') + rendered.fixture.detectChanges() - button.triggerEventHandler('click') + rendered.getByRole('button').click() await vi.advanceTimersByTimeAsync(11) - fixture.detectChanges() + rendered.fixture.detectChanges() - expect(span.nativeElement.textContent).toEqual('updatedValue') + expect(rendered.getByText('updatedValue')).toBeInTheDocument() const mutations = mutationCache.findAll() expect(mutations.length).toBe(2) @@ -681,126 +628,14 @@ describe('injectMutation', () => { expect(mutation2!.options.mutationKey).toEqual(['fake', 'updatedValue']) }) - describe('throwOnError', () => { - it('should evaluate throwOnError when mutation is expected to throw', async () => { - const key = queryKey() - const err = new Error('Expected mock error. All is well!') - const boundaryFn = vi.fn() - const { mutate } = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationKey: key, - mutationFn: () => { - return Promise.reject(err) - }, - throwOnError: boundaryFn, - })) - }) - - TestBed.tick() - - mutate() - - await vi.advanceTimersByTimeAsync(0) - - expect(boundaryFn).toHaveBeenCalledTimes(1) - expect(boundaryFn).toHaveBeenCalledWith(err) - }) - - it('should throw when throwOnError is true and mutate is used', async () => { - const key = queryKey() - const { mutate } = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationKey: key, - mutationFn: () => { - return Promise.reject( - new Error('Expected mock error. All is well!'), - ) - }, - throwOnError: true, - })) - }) - - TestBed.tick() - - mutate() - - await expect(vi.advanceTimersByTimeAsync(0)).rejects.toThrow( - 'Expected mock error. All is well!', - ) - }) - }) - - it('should throw when throwOnError is true', async () => { - const key = queryKey() - const err = new Error('Expected mock error. All is well!') - const { mutateAsync } = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationKey: key, - mutationFn: () => { - return Promise.reject(err) - }, - throwOnError: true, - })) - }) - - await expect(() => mutateAsync()).rejects.toThrow(err) - }) - - it('should throw when throwOnError function returns true', async () => { - const key = queryKey() - const err = new Error('Expected mock error. All is well!') - const { mutateAsync } = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationKey: key, - mutationFn: () => { - return Promise.reject(err) - }, - throwOnError: () => true, - })) - }) - - await expect(() => mutateAsync()).rejects.toThrow(err) - }) - - it('should resolve mutateAsync with the value returned from mutationFn', async () => { - const key = queryKey() - const { mutateAsync } = TestBed.runInInjectionContext(() => { - return injectMutation(() => ({ - mutationKey: key, - mutationFn: (params: string) => sleep(10).then(() => params), - })) - }) - - const promise = mutateAsync('Mock data') - await vi.advanceTimersByTimeAsync(10) - - await expect(promise).resolves.toBe('Mock data') - }) - describe('injection context', () => { - it('should throw NG0203 with descriptive error outside injection context', () => { - const key = queryKey() + it('throws NG0203 with descriptive error outside injection context', () => { expect(() => { injectMutation(() => ({ - mutationKey: key, + mutationKey: ['injectionContextError'], mutationFn: () => Promise.resolve(), })) - }).toThrow(/NG0203(.*?)injectMutation/) - }) - - it('should be usable outside injection context when passing an injector', () => { - const key = queryKey() - expect(() => { - injectMutation( - () => ({ - mutationKey: key, - mutationFn: () => Promise.resolve(), - }), - { - injector: TestBed.inject(Injector), - }, - ) - }).not.toThrow() + }).toThrowError(/NG0203(.*?)injectMutation/) }) it('should complete mutation before whenStable() resolves', async () => { @@ -808,10 +643,9 @@ describe('injectMutation', () => { let mutationStarted = false let mutationCompleted = false - const key = queryKey() const mutation = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationKey: key, + mutationKey: ['pendingTasksTest'], mutationFn: async (data: string) => { mutationStarted = true await sleep(50) @@ -821,11 +655,9 @@ describe('injectMutation', () => { })), ) - // Initial state expect(mutation.data()).toBeUndefined() expect(mutationStarted).toBe(false) - // Start mutation mutation.mutate('test') // Wait for mutation to start and Angular to be "stable" @@ -841,6 +673,14 @@ describe('injectMutation', () => { }) it('should handle synchronous mutation with retry', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + const app = TestBed.inject(ApplicationRef) let attemptCount = 0 @@ -858,7 +698,6 @@ describe('injectMutation', () => { })), ) - // Start mutation mutation.mutate('retry-test') // Synchronize pending effects for each retry attempt @@ -883,14 +722,20 @@ describe('injectMutation', () => { }) it('should handle multiple synchronous mutations on same key', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + const app = TestBed.inject(ApplicationRef) let callCount = 0 - const key = queryKey() - const mutation1 = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationKey: key, + mutationKey: ['sync-mutation-key'], mutationFn: async (data: string) => { callCount++ return `mutation1: ${data}` @@ -900,7 +745,7 @@ describe('injectMutation', () => { const mutation2 = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationKey: key, + mutationKey: ['sync-mutation-key'], mutationFn: async (data: string) => { callCount++ return `mutation2: ${data}` @@ -908,7 +753,6 @@ describe('injectMutation', () => { })), ) - // Start both mutations mutation1.mutate('test1') mutation2.mutate('test2') @@ -929,22 +773,40 @@ describe('injectMutation', () => { }) it('should handle synchronous mutation with optimistic updates', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + const app = TestBed.inject(ApplicationRef) - const testQueryKey = queryKey() + const testQueryKey = ['sync-optimistic'] let onMutateCalled = false let onSuccessCalled = false + let queryDataInsideOnMutate: string | undefined - // Set initial data queryClient.setQueryData(testQueryKey, 'initial') + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: testQueryKey, + queryFn: () => Promise.resolve('initial'), + staleTime: Infinity, + })), + ) + const mutation = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationFn: async (data: string) => `final: ${data}`, // Synchronous resolution + mutationFn: async (data: string) => { + await sleep(50) + return `final: ${data}` + }, onMutate: async (variables) => { onMutateCalled = true - const previousData = queryClient.getQueryData(testQueryKey) queryClient.setQueryData(testQueryKey, `optimistic: ${variables}`) - return { previousData } + queryDataInsideOnMutate = query.data() }, onSuccess: (data) => { onSuccessCalled = true @@ -953,37 +815,84 @@ describe('injectMutation', () => { })), ) - // Start mutation - mutation.mutate('test') - - // Synchronize pending effects TestBed.tick() - const stablePromise = app.whenStable() + expect(queryClient.getQueryData(testQueryKey)).toBe('initial') + mutation.mutate('test') + // Flush microtasks to allow TanStack Query's scheduled notifications to process await Promise.resolve() - await vi.advanceTimersByTimeAsync(1) - await stablePromise + // The observed query signal must be current inside the onMutate callback, + // immediately after the optimistic cache write. expect(onMutateCalled).toBe(true) + expect(queryClient.getQueryData(testQueryKey)).toBe('optimistic: test') + expect(queryDataInsideOnMutate).toBe('optimistic: test') + + // Check stability before the mutation completes, waiting for the next macro task + await vi.advanceTimersByTimeAsync(0) + expect(mutation.isPending()).toBe(true) + expect(await firstValueFrom(app.isStable)).toBe(false) + + // Wait for the mutation to complete + const stablePromise = app.whenStable() + await vi.advanceTimersByTimeAsync(60) + await stablePromise + expect(onSuccessCalled).toBe(true) expect(mutation.isSuccess()).toBe(true) expect(mutation.data()).toBe('final: test') expect(queryClient.getQueryData(testQueryKey)).toBe('final: test') }) + it('reads an optimistic update inside onMutate before the query signal or effects initialize', async () => { + const testQueryKey = ['unread-sync-optimistic'] + const seenInsideOnMutate: Array = [] + queryClient.setQueryData(testQueryKey, 'initial') + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: testQueryKey, + queryFn: () => Promise.resolve('initial'), + staleTime: Infinity, + })), + ) + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: async () => 'server', + onMutate: () => { + queryClient.setQueryData(testQueryKey, 'optimistic') + seenInsideOnMutate.push(query.data()) + }, + })), + ) + + // No signal read and no TestBed.tick() occurs before mutateAsync. The + // first query read must still pull the optimistic cache value; its + // observer subscription can be committed later. + await mutation.mutateAsync() + + expect(seenInsideOnMutate).toEqual(['optimistic']) + }) + it('should handle synchronous mutation cancellation', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + const app = TestBed.inject(ApplicationRef) - const key = queryKey() const mutation = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationKey: key, + mutationKey: ['cancel-sync'], mutationFn: async (data: string) => `processed: ${data}`, // Synchronous resolution })), ) - // Start mutation mutation.mutate('test') // Synchronize pending effects diff --git a/packages/angular-query/src/__tests__/inject-queries.test-d.ts b/packages/angular-query/src/__tests__/inject-queries.test-d.ts new file mode 100644 index 00000000000..7be6f773913 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-queries.test-d.ts @@ -0,0 +1,307 @@ +import { describe, expectTypeOf, it } from 'vitest' +import { injectQueries, skipToken, toResource } from '..' +import { queryOptions } from '../query-options' +import type { CreateQueryOptions, CreateQueryResult, OmitKeyof } from '..' +import type { Resource, Signal } from '@angular/core' + +describe('InjectQueries config object overload', () => { + it('infers tagged custom errors in parallel queries and combine', () => { + class CustomError extends Error { + detail = 'detail' + } + const options = queryOptions({ + queryKey: ['tagged'], + queryFn: () => 1, + }) + const queries = injectQueries(() => ({ queries: [options] })) + expectTypeOf(queries()[0].error()).toEqualTypeOf() + expectTypeOf(queries()[0].data()).toEqualTypeOf() + const combined = injectQueries(() => ({ + queries: [options], + combine: (results) => results[0].error, + })) + expectTypeOf(combined()).toEqualTypeOf() + }) + it('preserves custom errors from annotated options without a query-key tag', () => { + class CustomError extends Error { + detail = 'detail' + } + const options: CreateQueryOptions = { + queryKey: ['annotated'], + queryFn: () => 1, + } + const queries = injectQueries(() => ({ queries: [options] })) + expectTypeOf(queries()[0].error()).toEqualTypeOf() + expectTypeOf(queries()[0].data()).toEqualTypeOf() + const combined = injectQueries(() => ({ + queries: [options], + combine: (results) => results[0].error, + })) + expectTypeOf(combined()).toEqualTypeOf() + }) + + it('should expose Angular resource views', () => { + const queryResults = injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => Promise.resolve('data'), + }, + ], + })) + + expectTypeOf(toResource(queryResults()[0])).toMatchTypeOf< + Resource + >() + + // @ts-expect-error Resources are created explicitly with toResource. + queryResults()[0].resource + }) + + it('TData should always be defined when initialData is provided as an object', () => { + const query1 = { + queryKey: ['key1'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: false, + }, + } + + const query2 = { + queryKey: ['key2'], + queryFn: () => 'Query Data', + initialData: 'initial data', + } + + const query3 = { + queryKey: ['key2'], + queryFn: () => 'Query Data', + } + + const queryResults = injectQueries(() => ({ + queries: [query1, query2, query3], + })) + + const query1Data = queryResults()[0].data() + const query2Data = queryResults()[1].data() + const query3Data = queryResults()[2].data() + + expectTypeOf(query1Data).toEqualTypeOf<{ wow: boolean }>() + expectTypeOf(query2Data).toEqualTypeOf() + expectTypeOf(query3Data).toEqualTypeOf() + }) + + it('TData should be defined when passed through queryOptions', () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: true, + }, + }) + const queryResults = injectQueries(() => ({ queries: [options] })) + + const data = queryResults()[0].data() + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean }>() + }) + + it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into injectQuery', () => { + const query1 = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + select: (data) => data > 1, + }) + + const query2 = { + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + select: (data: number) => data > 1, + } + + const queryResults = injectQueries(() => ({ queries: [query1, query2] })) + const query1Data = queryResults()[0].data() + const query2Data = queryResults()[1].data() + + expectTypeOf(query1Data).toEqualTypeOf() + expectTypeOf(query2Data).toEqualTypeOf() + }) + + it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { + const queryResults = injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + }, + ], + })) + + const data = queryResults()[0].data() + + expectTypeOf(data).toEqualTypeOf<{ wow: boolean } | undefined>() + }) + + describe('custom injectable', () => { + it('should allow custom hooks using UseQueryOptions', () => { + type Data = string + + const injectCustomQueries = ( + options?: OmitKeyof, 'queryKey' | 'queryFn'>, + ) => { + return injectQueries(() => ({ + queries: [ + { + ...options, + queryKey: ['todos-key'], + queryFn: () => Promise.resolve('data'), + }, + ], + })) + } + + const queryResults = injectCustomQueries() + const data = queryResults()[0].data() + + expectTypeOf(data).toEqualTypeOf() + }) + }) + + it('TData should have correct type when conditional skipToken is passed', () => { + const queryResults = injectQueries(() => ({ + queries: [ + { + queryKey: ['withSkipToken'], + queryFn: Math.random() > 0.5 ? skipToken : () => Promise.resolve(5), + }, + ], + })) + + const firstResult = queryResults()[0] + + expectTypeOf(firstResult).toEqualTypeOf>() + expectTypeOf(firstResult.data()).toEqualTypeOf() + }) + + it('should return correct data for dynamic queries with mixed result types', () => { + const Queries1 = { + get: () => + queryOptions({ + queryKey: ['key1'], + queryFn: () => Promise.resolve(1), + }), + } + const Queries2 = { + get: () => + queryOptions({ + queryKey: ['key2'], + queryFn: () => Promise.resolve(true), + }), + } + + const queries1List = [1, 2, 3].map(() => ({ ...Queries1.get() })) + const result = injectQueries(() => ({ + queries: [...queries1List, { ...Queries2.get() }], + })) + + expectTypeOf(result).toBeFunction() + }) +}) + +describe('InjectQueries combine', () => { + it('should provide the correct type for the combine function', () => { + injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + }, + { + queryKey: ['key2'], + queryFn: () => Promise.resolve(true), + }, + ], + combine: (results) => { + expectTypeOf(results[0].data).toEqualTypeOf() + expectTypeOf(results[0].refetch).toBeCallableWith() + expectTypeOf(results[1].data).toEqualTypeOf() + expectTypeOf(results[1].refetch).toBeCallableWith() + }, + })) + }) + + it('should provide the correct types on the combined result with initial data', () => { + injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + initialData: 1, + }, + ], + combine: (results) => { + expectTypeOf(results[0].data).toEqualTypeOf() + expectTypeOf(results[0].refetch).toBeCallableWith() + }, + })) + }) + + it('should provide the correct result type', () => { + const queryResults = injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + }, + { + queryKey: ['key2'], + queryFn: () => Promise.resolve(true), + }, + ], + combine: (results) => ({ + data: { + 1: results[0].data, + 2: results[1].data, + }, + fn: () => {}, + }), + })) + + expectTypeOf(queryResults).branded.toEqualTypeOf< + Signal<{ + data: { + 1: number | undefined + 2: boolean | undefined + } + fn: () => void + }> + >() + }) + + it('should provide the correct types on the result with initial data', () => { + const queryResults = injectQueries(() => ({ + queries: [ + { + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + initialData: 1, + }, + ], + })) + + expectTypeOf(queryResults()[0].data()).toEqualTypeOf() + }) +}) diff --git a/packages/angular-query/src/__tests__/inject-queries.test.ts b/packages/angular-query/src/__tests__/inject-queries.test.ts new file mode 100644 index 00000000000..10cde4532d6 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-queries.test.ts @@ -0,0 +1,1615 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { render } from '@testing-library/angular' +import { + ApplicationRef, + ChangeDetectionStrategy, + Component, + ErrorHandler, + computed, + effect, + input, + inputBinding, + signal, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { queryKey, sleep } from '@tanstack/query-test-utils' +import { provideIsRestoring } from '../internal' +import { QueryClient, onlineManager, provideTanStackQuery, skipToken } from '..' +import { injectQueries } from '../inject-queries' +import { + provideAngularQueryChangeDetection, + setupTanStackQueryTestBed, +} from './test-utils' + +// cspell:ignore ZONEFUL + +let queryClient: QueryClient + +beforeEach(() => { + vi.useFakeTimers() + queryClient = new QueryClient() + vi.useFakeTimers({ shouldAdvanceTime: true }) + setupTanStackQueryTestBed(queryClient) +}) + +afterEach(() => { + onlineManager.setOnline(true) + vi.useRealTimers() +}) + +describe('injectQueries', () => { + it.each([false, true])( + 'uses current options and slot identity for multi-query refetch (combine=%s)', + async (combine) => { + vi.useRealTimers() + const keys = signal(['a', 'b']) + const version = signal('old') + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => { + const current = version() + return { + queries: keys().map((key) => ({ + queryKey: [key], + enabled: false, + queryFn: async () => `${key}:${current}`, + })), + combine: combine ? (results) => results : undefined, + } + }), + ) + const refetch = queries()[0]!.refetch + version.set('new') + expect((await refetch()).data).toBe('a:new') + keys.set(['b', 'a']) + expect((await refetch()).data).toBe('b:new') + keys.set([]) + expect(queries()).toHaveLength(0) + await expect(refetch()).rejects.toThrow('removed query at index 0') + keys.set(['a']) + const restoredRefetch = queries()[0]!.refetch + expect(restoredRefetch).not.toBe(refetch) + expect((await restoredRefetch()).data).toBe('a:new') + }, + ) + + it('should keep failed refetch state and cached data readable without global reporting', async () => { + vi.useRealTimers() + queryClient.setDefaultOptions({ + queries: { retry: false, throwOnError: true }, + }) + const report = vi.spyOn(TestBed.inject(ErrorHandler), 'handleError') + const error = new Error('query failed') + const options = { + queryKey: ['error'], + initialData: 'cached', + enabled: false, + queryFn: async (): Promise => { + throw error + }, + } + const query = TestBed.runInInjectionContext( + () => injectQueries(() => ({ queries: [options] }))()[0], + ) + await TestBed.inject(ApplicationRef).whenStable() + await expect(query.refetch()).resolves.toMatchObject({ + status: 'error', + error, + }) + expect(query.error()).toBe(error) + expect(query.data()).toBe('cached') + expect(query.isError()).toBe(true) + await expect(query.refetch({ throwOnError: true })).rejects.toBe(error) + expect(report).not.toHaveBeenCalled() + }) + + it('throws NG0203 with descriptive error outside injection context', () => { + expect(() => { + injectQueries(() => ({ + queries: [ + { + queryKey: ['injectionContextError'], + queryFn: () => Promise.resolve(1), + }, + ], + })) + }).toThrowError(/NG0203(.*?)injectQueries/) + }) + + it('allows cache listeners to read a result while options update', () => { + const staleTime = signal(0) + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['reentrant-options'], + queryFn: () => 'data', + enabled: false, + staleTime: staleTime(), + }, + ], + })), + ) + + TestBed.tick() + const query = queries()[0] + const readResult = vi.fn(() => query.status()) + const subscriptionEvents: Array = [] + const unsubscribe = queryClient.getQueryCache().subscribe((event) => { + // setQueries emits synchronously; this read must not re-enter the signal + // computation that caused the options update. + if (event.type === 'observerOptionsUpdated') readResult() + if (event.type === 'observerAdded' || event.type === 'observerRemoved') { + subscriptionEvents.push(event.type) + } + }) + + staleTime.set(1) + + expect(() => TestBed.tick()).not.toThrow() + expect(readResult).toHaveBeenCalled() + expect(subscriptionEvents).toEqual([]) + + unsubscribe() + }) + + it('allows deferred cache-listener reads during restoration detach and reattach', async () => { + const isRestoring = signal(false) + setupTanStackQueryTestBed(queryClient, { + providers: [provideIsRestoring(() => isRestoring.asReadonly())], + }) + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['reentrant-restoration-subscription'], + queryFn: () => 'data', + enabled: false, + }, + ], + })), + ) + expect(queries()[0].status()).toBe('pending') + TestBed.tick() + + const nestedStatuses: Array = [] + const unsubscribe = queryClient.getQueryCache().subscribe((event) => { + if (event.type === 'observerAdded' || event.type === 'observerRemoved') { + void Promise.resolve().then(() => + nestedStatuses.push(queries()[0].status()), + ) + } + }) + + isRestoring.set(true) + expect(() => TestBed.tick()).not.toThrow() + isRestoring.set(false) + expect(() => TestBed.tick()).not.toThrow() + + await Promise.resolve() + expect(nestedStatuses).toEqual(['pending', 'pending']) + unsubscribe() + }) + + it('allows deferred cache-listener reads while a list change creates a query', async () => { + const key = signal('old') + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['reentrant-query-added', key()], + queryFn: () => 'data', + enabled: false, + }, + ], + })), + ) + + expect(queries()[0].status()).toBe('pending') + TestBed.tick() + const nestedStatuses: Array = [] + const unsubscribe = queryClient.getQueryCache().subscribe((event) => { + if (event.type === 'added') + void Promise.resolve().then(() => + nestedStatuses.push(queries()[0].status()), + ) + }) + + key.set('new') + + expect(() => queries()[0].status()).not.toThrow() + expect(nestedStatuses).toEqual([]) + expect(() => TestBed.tick()).not.toThrow() + await Promise.resolve() + expect(nestedStatuses).toEqual(['pending']) + unsubscribe() + }) + + it('should return the correct states', async () => { + const key1 = queryKey() + const key2 = queryKey() + const results: Array>> = [] + + @Component({ + template: ` +
+
+ data1: {{ queries()[0].data() ?? 'null' }}, data2: + {{ queries()[1].data() ?? 'null' }} +
+
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + toString(val: any) { + return String(val) + } + queries = injectQueries(() => ({ + queries: [ + { + queryKey: key1, + queryFn: () => sleep(10).then(() => 1), + }, + { + queryKey: key2, + queryFn: () => sleep(100).then(() => 2), + }, + ], + })) + + _pushResults = effect(() => { + const snapshot = this.queries().map((q) => ({ data: q.data() })) + results.push(snapshot) + }) + } + + const rendered = await render(Page, { + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + + expect(results.some((snapshot) => snapshot[0]?.data === 1)).toBe(true) + + await vi.advanceTimersByTimeAsync(91) + rendered.fixture.detectChanges() + + expect(rendered.getByText('data1: 1, data2: 2')).toBeInTheDocument() + + expect( + results.some( + (snapshot) => + snapshot.length === 2 && + snapshot[0]?.data === undefined && + snapshot[1]?.data === undefined, + ), + ).toBe(true) + expect( + results.some( + (snapshot) => + snapshot.length === 2 && + snapshot[0]?.data === 1 && + snapshot[1]?.data === undefined, + ), + ).toBe(true) + expect( + results.some( + (snapshot) => + snapshot.length === 2 && + snapshot[0]?.data === 1 && + snapshot[1]?.data === 2, + ), + ).toBe(true) + }) + + it('should update a result field first read after an earlier update', async () => { + let count = 0 + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['late-field-tracking'], + queryFn: async () => { + await sleep(10) + return ++count + }, + }, + ], + })) + } + + const rendered = await render(Page) + rendered.fixture.autoDetectChanges() + const query = rendered.fixture.componentInstance.queries()[0] + + // Only status is read before the first observer update. + expect(query.status()).toBe('pending') + + await vi.advanceTimersByTimeAsync(11) + expect(query.status()).toBe('success') + + // Data starts being observed after the result signal already contains a + // subscription update. It must still participate in future notifications. + expect(query.data()).toBe(1) + + const refetch = query.refetch() + await vi.advanceTimersByTimeAsync(11) + await refetch + + expect(query.data()).toBe(2) + }) + + it('should support combining results', async () => { + const key1 = queryKey() + const key2 = queryKey() + let count = 0 + + const results: Array<{ data: string; refetch: () => void }> = [] + + @Component({ + template: `
data: {{ queries().data }}
`, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: key1, + queryFn: async () => { + await new Promise((r) => setTimeout(r, 10)) + count++ + return count + }, + }, + { + queryKey: key2, + queryFn: async () => { + await new Promise((r) => setTimeout(r, 100)) + count++ + return count + }, + }, + ], + combine: (queryResults) => { + return { + refetch: () => queryResults.forEach((r) => r.refetch()), + data: queryResults.map((r) => r.data).join(','), + } + }, + })) + + _pushResults = effect(() => { + results.push(this.queries()) + }) + } + + const rendered = await render(Page) + const instance = rendered.fixture.componentInstance + await rendered.findByText('data: 1,2') + expect(instance.queries().data).toBe('1,2') + + instance.queries().refetch() + + await rendered.findByText('data: 3,4') + expect(instance.queries().data).toBe('3,4') + + const dataChanges = results.filter( + (result, index) => result.data !== results[index - 1]?.data, + ) + expect(dataChanges.map((result) => result.data)).toEqual([ + ',', + '1,', + '1,2', + '3,2', + '3,4', + ]) + expect( + results.every((result) => typeof result.refetch === 'function'), + ).toBe(true) + }) + + it('updates the result shape when combine configuration changes', () => { + const shouldCombine = signal(false) + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['committed-combine'], + enabled: false, + }, + ], + combine: shouldCombine() + ? (results: Array) => ({ count: results.length }) + : undefined, + })), + ) + + expect(Array.isArray(queries())).toBe(true) + TestBed.tick() + + shouldCombine.set(true) + expect(queries()).toEqual({ count: 1 }) + }) + + it('removes a combined result when combine configuration changes', () => { + const shouldCombine = signal(true) + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['committed-combine-removal'], + enabled: false, + }, + ], + combine: shouldCombine() + ? (results: Array) => ({ count: results.length }) + : undefined, + })), + ) + + expect(queries()).toEqual({ count: 1 }) + TestBed.tick() + + shouldCombine.set(false) + expect(Array.isArray(queries())).toBe(true) + expect(queries()).toHaveLength(1) + }) + + it('combines fresh raw results when a signal changes what combine selects', () => { + const showData = signal(false) + const key = ['reactive-combine-selection'] + queryClient.setQueryData(key, 1) + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [{ queryKey: key, enabled: false }], + combine: (results) => ({ value: showData() ? results[0].data : 0 }), + })), + ) + + expect(queries()).toEqual({ value: 0 }) + TestBed.tick() + queryClient.setQueryData(key, 2) + const previous = queries() + queryClient.setQueryData(key, 3) + expect(queries()).toEqual(previous) + expect(queries()).not.toBe(previous) + showData.set(true) + expect(queries()).toEqual({ value: 3 }) + }) + + it('should handle mixed success and error query states', async () => { + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['mixed-error'], + retry: false, + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 10)) + throw new Error('mixed-error') + }, + }, + { + queryKey: ['mixed-success'], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 20)) + return 'mixed-success' + }, + }, + ], + })) + } + + const rendered = await render(Page) + await vi.advanceTimersByTimeAsync(25) + await Promise.resolve() + + const [errorQuery, successQuery] = + rendered.fixture.componentInstance.queries() + expect(errorQuery.status()).toBe('error') + expect(errorQuery.error()?.message).toBe('mixed-error') + expect(successQuery.status()).toBe('success') + expect(successQuery.data()).toBe('mixed-success') + }) + + it('should cleanup pending tasks when component with active queries is destroyed', async () => { + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['destroy-query-1'], + queryFn: async () => { + await sleep(100) + return 'one' + }, + }, + { + queryKey: ['destroy-query-2'], + queryFn: async () => { + await sleep(100) + return 'two' + }, + }, + ], + })) + } + + // Use a fixture here on purpose: we need component teardown + whenStable() semantics. + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + expect(fixture.isStable()).toBe(false) + + fixture.destroy() + + const stablePromise = fixture.whenStable() + await vi.advanceTimersByTimeAsync(150) + await stablePromise + + expect(fixture.isStable()).toBe(true) + }) + + it('should react to enabled signal changes', async () => { + const enabled = signal(false) + const fetchSpy = vi.fn(() => sleep(10).then(() => 'enabled-data')) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + enabled = enabled + fetchSpy = fetchSpy + + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['enabled', this.enabled()], + queryFn: this.fetchSpy, + enabled: this.enabled(), + }, + ], + })) + } + + const rendered = await render(Page) + const query = rendered.fixture.componentInstance.queries()[0] + + expect(fetchSpy).not.toHaveBeenCalled() + expect(query.status()).toBe('pending') + + enabled.set(true) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(11) + await Promise.resolve() + + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(query.status()).toBe('success') + expect(query.data()).toBe('enabled-data') + }) + + it('should not resubscribe or refetch unchanged stale queries when options change', async () => { + const multiplier = signal(1) + const fetchSpy = vi.fn(() => sleep(10).then(() => 2)) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => { + const currentMultiplier = multiplier() + + return { + queries: [ + { + queryKey: ['reactive-select'], + queryFn: fetchSpy, + select: (data: number) => data * currentMultiplier, + staleTime: 0, + }, + ], + } + }) + } + + const rendered = await render(Page) + await vi.advanceTimersByTimeAsync(11) + + const query = rendered.fixture.componentInstance.queries()[0] + const cachedQuery = queryClient + .getQueryCache() + .find({ queryKey: ['reactive-select'] })! + + expect(query.data()).toBe(2) + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(cachedQuery.getObserversCount()).toBe(1) + + multiplier.set(2) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + expect(query.data()).toBe(4) + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(cachedQuery.getObserversCount()).toBe(1) + + rendered.fixture.destroy() + expect(cachedQuery.getObserversCount()).toBe(0) + }) + + it('should refetch only changed keys when queries length stays the same', async () => { + const ids = signal<[string, string]>(['a', 'b']) + const firstSpy = vi.fn((context: any) => + sleep(10).then(() => `first-${context.queryKey[1]}`), + ) + const secondSpy = vi.fn((context: any) => + sleep(10).then(() => `second-${context.queryKey[1]}`), + ) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + ids = ids + firstSpy = firstSpy + secondSpy = secondSpy + + queries = injectQueries(() => ({ + queries: [ + { + staleTime: Number.POSITIVE_INFINITY, + queryKey: ['first', this.ids()[0]], + queryFn: this.firstSpy, + }, + { + staleTime: Number.POSITIVE_INFINITY, + queryKey: ['second', this.ids()[1]], + queryFn: this.secondSpy, + }, + ], + })) + } + + const rendered = await render(Page) + await vi.advanceTimersByTimeAsync(11) + await Promise.resolve() + + let [firstQuery, secondQuery] = rendered.fixture.componentInstance.queries() + expect(firstQuery.data()).toBe('first-a') + expect(secondQuery.data()).toBe('second-b') + expect(firstSpy).toHaveBeenCalledTimes(1) + expect(secondSpy).toHaveBeenCalledTimes(1) + + ids.set(['c', 'b']) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(11) + await Promise.resolve() + ;[firstQuery, secondQuery] = rendered.fixture.componentInstance.queries() + expect(firstQuery.data()).toBe('first-c') + expect(secondQuery.data()).toBe('second-b') + expect(firstSpy).toHaveBeenCalledTimes(2) + expect(secondSpy).toHaveBeenCalledTimes(1) + }) + + it('should retain result proxy and field signal identities across updates', async () => { + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['proxy-cache-update'], + queryFn: () => Promise.resolve('initial'), + }, + ], + })) + } + + const rendered = await render(Page) + await vi.advanceTimersByTimeAsync(0) + + const initialQuery = rendered.fixture.componentInstance.queries()[0] + const initialDataSignal = initialQuery.data + + queryClient.setQueryData(['proxy-cache-update'], 'updated') + await vi.advanceTimersByTimeAsync(0) + + const updatedQuery = rendered.fixture.componentInstance.queries()[0] + expect(updatedQuery).toBe(initialQuery) + expect(updatedQuery.data).toBe(initialDataSignal) + expect(updatedQuery.data()).toBe('updated') + }) + + it('should cache result proxies by index as the queries array changes', async () => { + const queryIds = signal(['a', 'b']) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + ids = queryIds + + queries = injectQueries(() => ({ + queries: this.ids().map((id) => ({ + queryKey: ['proxy-cache-list', id], + queryFn: () => Promise.resolve(id), + staleTime: Number.POSITIVE_INFINITY, + })), + })) + } + + const rendered = await render(Page) + await vi.advanceTimersByTimeAsync(0) + + const instance = rendered.fixture.componentInstance + const initialQueries = instance.queries() + const firstQuery = initialQueries[0] + const secondQuery = initialQueries[1] + + queryIds.set(['a', 'b', 'c']) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + const appendedQueries = instance.queries() + expect(appendedQueries).toHaveLength(3) + expect(appendedQueries[0]).toBe(firstQuery) + expect(appendedQueries[1]).toBe(secondQuery) + expect(appendedQueries[2]).not.toBe(firstQuery) + + queryIds.set(['a']) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + const removedQueries = instance.queries() + expect(removedQueries).toHaveLength(1) + expect(removedQueries[0]).toBe(firstQuery) + + queryIds.set(['a', 'd']) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + const queriesAfterAddingAgain = instance.queries() + expect(queriesAfterAddingAgain[0]).toBe(firstQuery) + expect(queriesAfterAddingAgain[1]).not.toBe(secondQuery) + + queryIds.set(['d', 'a']) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + const reorderedQueries = instance.queries() + expect(reorderedQueries[0]).toBe(firstQuery) + expect(reorderedQueries[1]).not.toBe(secondQuery) + expect(reorderedQueries[0]!.data()).toBe('d') + expect(reorderedQueries[1]!.data()).toBe('a') + }) + + it('should support changes on the queries array', async () => { + const results: Array>> = [] + + @Component({ + template: `
data: {{ mapped() }}
`, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: queries().map((q) => ({ + queryKey: ['query', q], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 20 * q)) + return q + }, + })), + })) + + mapped = computed(() => { + const queryData = this.queries().map((q) => q.data()) + if (queryData.length === 0) return 'empty' + return queryData.join(',') + }) + + _pushResults = effect(() => { + const snapshot = this.queries().map((q) => ({ data: q.data() })) + results.push(snapshot) + }) + } + + const queries = signal([1, 2, 4]) + + const rendered = await render(Page) + rendered.fixture.autoDetectChanges() + const instance = rendered.fixture.componentInstance + + await vi.advanceTimersByTimeAsync(20) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(20) + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(40) + rendered.fixture.detectChanges() + await rendered.findByText('data: 1,2,4') + expect(instance.mapped()).toBe('1,2,4') + + const hasSnapshot = (expected: Array): boolean => + results.some( + (snapshot) => + snapshot.length === expected.length && + expected.every((data, index) => snapshot[index]?.data === data), + ) + expect(hasSnapshot([undefined, undefined, undefined])).toBe(true) + expect(hasSnapshot([1, undefined, undefined])).toBe(true) + expect(hasSnapshot([1, 2, undefined])).toBe(true) + expect(hasSnapshot([1, 2, 4])).toBe(true) + + queries.set([3, 4]) + rendered.fixture.detectChanges() + expect(instance.queries()[0]!.data()).toBeUndefined() + expect(instance.queries()[1]!.data()).toBe(4) + expect(instance.mapped()).toBe(',4') + await rendered.findByText('data: 3,4') + expect(instance.mapped()).toBe('3,4') + + expect(results[results.length - 1]).toMatchObject([ + { data: 3 }, + { data: 4 }, + ]) + + queries.set([]) + await rendered.findByText('data: empty') + expect(instance.mapped()).toBe('empty') + + expect(results[results.length - 1]).toMatchObject([]) + }) + + it('should change the rendered component when the queries array changes', async () => { + const userIds = signal([1, 2]) + + @Component({ + template: ` +
    + @for (query of queries(); track $index) { + @if (query.data(); as data) { +
  • {{ data.value }}
  • + } + } +
+ `, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + userIds = userIds + + queries = injectQueries(() => ({ + queries: this.userIds().map((id) => ({ + queryKey: ['user', id], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 20)) + return { value: String(id) } + }, + })), + })) + } + + const rendered = await render(Page) + + await rendered.findByText('1') + await rendered.findByText('2') + + userIds.set([3]) + rendered.fixture.detectChanges() + + await rendered.findByText('3') + expect(rendered.queryByText('1')).toBeNull() + expect(rendered.queryByText('2')).toBeNull() + }) + + it('should support required signal inputs', async () => { + @Component({ + selector: 'app-fake', + template: `{{ queries()[0].data() }}`, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class FakeComponent { + name = input.required() + + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['fake', this.name()], + queryFn: () => this.name(), + }, + ], + })) + } + + const name = signal('signal-input-required-test') + const rendered = await render(FakeComponent, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.autoDetectChanges() + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(0) + + const result = rendered.fixture.nativeElement.textContent + expect(result).toEqual('signal-input-required-test') + }) + + it('should allow reading query state in ngOnInit with required signal inputs', async () => { + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + name = input.required() + initialStatus!: string + + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-ng-on-init', this.name()], + queryFn: () => this.name(), + }, + ], + })) + + ngOnInit() { + this.initialStatus = this.queries()[0].status() + } + } + + const name = signal('queries-ng-on-init') + const rendered = await render(Page, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + + rendered.fixture.detectChanges() + + expect(rendered.fixture.componentInstance.initialStatus).toBe('pending') + + await vi.advanceTimersByTimeAsync(0) + expect(rendered.fixture.componentInstance.queries()[0].data()).toBe( + 'queries-ng-on-init', + ) + }) + + describe('pending tasks', () => { + it('should handle synchronous success and error before whenStable resolves', async () => { + const app = TestBed.inject(ApplicationRef) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-sync-success'], + queryFn: () => 'instant-data', + }, + { + queryKey: ['queries-sync-error'], + queryFn: () => { + throw new Error('instant-error') + }, + retry: false, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + const [successQuery, errorQuery] = fixture.componentInstance.queries() + + expect(successQuery.status()).toBe('pending') + expect(errorQuery.status()).toBe('pending') + + const stablePromise = app.whenStable() + await Promise.resolve() + await vi.advanceTimersByTimeAsync(10) + await stablePromise + + expect(successQuery.status()).toBe('success') + expect(successQuery.data()).toBe('instant-data') + expect(errorQuery.status()).toBe('error') + expect(errorQuery.error()).toEqual(new Error('instant-error')) + }) + + it('tracks invalidation even when combine returns an unchanged value', async () => { + vi.useRealTimers() + const key = ['combined-pending-invalidation'] + let complete!: (value: number) => void + let calls = 0 + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: key, + queryFn: () => + ++calls === 1 + ? 1 + : new Promise((resolve) => { + complete = resolve + }), + }, + ], + combine: () => 'unchanged', + })), + ) + const app = TestBed.inject(ApplicationRef) + await app.whenStable() + expect(queries()).toBe('unchanged') + let isStable = false + const subscription = app.isStable.subscribe((value) => { + isStable = value + }) + expect(isStable).toBe(true) + + const invalidation = queryClient.invalidateQueries({ queryKey: key }) + expect(isStable).toBe(false) + complete(2) + await invalidation + await app.whenStable() + expect(isStable).toBe(true) + expect(queries()).toBe('unchanged') + subscription.unsubscribe() + }) + + it('should not register pending tasks for disabled queries', async () => { + const queryFn = vi.fn(() => Promise.resolve('disabled-data')) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-disabled-enabled'], + queryFn, + enabled: false, + }, + { + queryKey: ['queries-disabled-skip-token'], + queryFn: skipToken, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + await Promise.resolve() + + const [disabledQuery, skippedQuery] = fixture.componentInstance.queries() + expect(disabledQuery.fetchStatus()).toBe('idle') + expect(skippedQuery.fetchStatus()).toBe('idle') + expect(queryFn).not.toHaveBeenCalled() + expect(fixture.isStable()).toBe(true) + }) + + it('should stay pending while queries are paused offline', async () => { + const app = TestBed.inject(ApplicationRef) + onlineManager.setOnline(false) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-start-offline'], + queryFn: () => sleep(10).then(() => 'online-data'), + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + await Promise.resolve() + + const query = fixture.componentInstance.queries()[0] + expect(query.fetchStatus()).toBe('paused') + + let stableResolved = false + const stablePromise = app.whenStable().then(() => { + stableResolved = true + }) + await Promise.resolve() + expect(stableResolved).toBe(false) + + onlineManager.setOnline(true) + await vi.advanceTimersByTimeAsync(20) + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toBe('online-data') + }) + + it('should handle rapid refetches without leaking a pending task', async () => { + const app = TestBed.inject(ApplicationRef) + let count = 0 + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-rapid-refetch'], + queryFn: async () => { + await sleep(10) + return ++count + }, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + const query = fixture.componentInstance.queries()[0] + + query.refetch() + query.refetch() + query.refetch() + + const stablePromise = app.whenStable() + await vi.advanceTimersByTimeAsync(20) + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toBeGreaterThan(0) + expect(fixture.isStable()).toBe(true) + }) + + it('should release the pending task when a query is cancelled', async () => { + const app = TestBed.inject(ApplicationRef) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-cancel'], + queryFn: () => sleep(100).then(() => 'data'), + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + const query = fixture.componentInstance.queries()[0] + + await vi.advanceTimersByTimeAsync(20) + await queryClient.cancelQueries({ queryKey: ['queries-cancel'] }) + await app.whenStable() + + expect(query.status()).toBe('pending') + expect(query.fetchStatus()).toBe('idle') + expect(fixture.isStable()).toBe(true) + }) + + it('should keep the pending task through retries', async () => { + const app = TestBed.inject(ApplicationRef) + let attemptCount = 0 + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['queries-retry'], + retry: 2, + retryDelay: 10, + queryFn: () => { + attemptCount++ + if (attemptCount <= 2) { + throw new Error(`Attempt ${attemptCount} failed`) + } + return 'success-data' + }, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + const query = fixture.componentInstance.queries()[0] + + const stablePromise = app.whenStable() + await vi.advanceTimersByTimeAsync(50) + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toBe('success-data') + expect(attemptCount).toBe(3) + }) + }) + + it('should pause fetching while restoring and fetch once restoring is disabled', async () => { + const isRestoring = signal(true) + const fetchSpy = vi.fn(() => sleep(10).then(() => 'restored-data')) + setupTanStackQueryTestBed(queryClient, { + providers: [provideIsRestoring(() => isRestoring.asReadonly())], + }) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['restoring'], + queryFn: fetchSpy, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + + expect(fetchSpy).not.toHaveBeenCalled() + expect(fixture.componentInstance.queries()[0].status()).toBe('pending') + + const stablePromise = fixture.whenStable() + await Promise.resolve() + await stablePromise + + isRestoring.set(false) + fixture.detectChanges() + + await vi.advanceTimersByTimeAsync(11) + await fixture.whenStable() + + const result = fixture.componentInstance.queries()[0] + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(result.status()).toBe('success') + expect(result.data()).toBe('restored-data') + + const cachedQuery = queryClient + .getQueryCache() + .find({ queryKey: ['restoring'] })! + expect(cachedQuery.getObserversCount()).toBe(1) + + isRestoring.set(true) + fixture.detectChanges() + expect(cachedQuery.getObserversCount()).toBe(0) + + isRestoring.set(false) + fixture.detectChanges() + expect(cachedQuery.getObserversCount()).toBe(1) + + fixture.destroy() + expect(cachedQuery.getObserversCount()).toBe(0) + }) + + it.each(['options-first', 'restoring-first'] as const)( + 'uses the latest query list when leaving restoration in the same turn (%s)', + async (writeOrder) => { + const isRestoring = signal(true) + const key = signal('old') + const queriedKeys: Array = [] + setupTanStackQueryTestBed(queryClient, { + providers: [provideIsRestoring(() => isRestoring.asReadonly())], + }) + + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['same-turn-restoration', key()], + queryFn: ({ queryKey: currentQueryKey }) => { + const currentKey = currentQueryKey[1] as string + queriedKeys.push(currentKey) + return Promise.resolve(currentKey) + }, + }, + ], + })), + ) + + expect(queries()[0].status()).toBe('pending') + expect(queriedKeys).toEqual([]) + + if (writeOrder === 'options-first') { + key.set('new') + isRestoring.set(false) + } else { + isRestoring.set(false) + key.set('new') + } + TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() + + expect(queriedKeys).toEqual(['new']) + expect(queries()[0].data()).toBe('new') + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['same-turn-restoration', 'old'] })?.state + .fetchStatus, + ).toBe('idle') + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['same-turn-restoration', 'new'] }) + ?.getObserversCount(), + ).toBe(1) + }, + ) + + it('allows a cache listener to read a result during restoration-boundary configuration', () => { + const isRestoring = signal(true) + const staleTime = signal(0) + setupTanStackQueryTestBed(queryClient, { + providers: [provideIsRestoring(() => isRestoring.asReadonly())], + }) + + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['restoration-reentrant'], + queryFn: () => 'data', + enabled: false, + staleTime: staleTime(), + }, + ], + })), + ) + + expect(queries()[0].status()).toBe('pending') + TestBed.tick() + + const boundaryRead = vi.fn(() => queries()[0].status()) + let appliedStaleTime: number | undefined + const unsubscribe = queryClient.getQueryCache().subscribe((event) => { + if (event.type === 'observerOptionsUpdated') { + appliedStaleTime = event.observer.options.staleTime as number + boundaryRead() + } + }) + + staleTime.set(1) + isRestoring.set(false) + + expect(() => TestBed.tick()).not.toThrow() + expect(boundaryRead).toHaveBeenCalled() + expect(appliedStaleTime).toBe(1) + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['restoration-reentrant'] }) + ?.getObserversCount(), + ).toBe(1) + + unsubscribe() + }) + + it('converges to the current query list after an early restoration read', () => { + const isRestoring = signal(true) + const key = signal('old') + const oldQueryKey = ['early-restoration-read', 'old'] as const + const newQueryKey = ['early-restoration-read', 'new'] as const + setupTanStackQueryTestBed(queryClient, { + providers: [provideIsRestoring(() => isRestoring.asReadonly())], + }) + + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['early-restoration-read', key()], + queryFn: () => 'unused', + enabled: false, + }, + ], + })), + ) + + expect(queries()[0].status()).toBe('pending') + TestBed.tick() + const oldQuery = queryClient + .getQueryCache() + .find({ queryKey: oldQueryKey })! + expect(oldQuery.getObserversCount()).toBe(0) + + key.set('new') + isRestoring.set(false) + + expect(queries()[0].status()).toBe('pending') + expect( + queryClient.getQueryCache().find({ queryKey: newQueryKey }), + ).toBeDefined() + + queryClient.setQueryData(newQueryKey, 'early data') + const newQuery = queryClient + .getQueryCache() + .find({ queryKey: newQueryKey })! + expect(newQuery.getObserversCount()).toBe(0) + TestBed.tick() + + expect(oldQuery.getObserversCount()).toBe(0) + expect(newQuery.getObserversCount()).toBe(1) + expect(queries()[0].data()).toBe('early data') + }) + + it('reads the optimistic query list before moving subscriptions on the next tick', () => { + const key = signal('old') + const oldQueryKey = ['early-options-read', 'old'] as const + const newQueryKey = ['early-options-read', 'new'] as const + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [ + { + queryKey: ['early-options-read', key()], + queryFn: () => 'unused', + enabled: false, + }, + ], + })), + ) + + expect(queries()[0].status()).toBe('pending') + TestBed.tick() + const oldQuery = queryClient + .getQueryCache() + .find({ queryKey: oldQueryKey })! + expect(oldQuery.getObserversCount()).toBe(1) + + key.set('new') + + expect(queries()[0].status()).toBe('pending') + // The existing observer list stays attached until the configuration effect + // can move it atomically. + expect(oldQuery.getObserversCount()).toBe(1) + expect( + queryClient.getQueryCache().find({ queryKey: newQueryKey }), + ).toBeDefined() + + queryClient.setQueryData(newQueryKey, 'early data') + const newQuery = queryClient + .getQueryCache() + .find({ queryKey: newQueryKey })! + expect(newQuery.getObserversCount()).toBe(0) + TestBed.tick() + + expect(oldQuery.getObserversCount()).toBe(0) + expect(newQuery.getObserversCount()).toBe(1) + expect(queries()[0].data()).toBe('early data') + }) + + it('should complete queries before whenStable resolves', async () => { + const app = TestBed.inject(ApplicationRef) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['query-1'], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 10)) + return 1 + }, + }, + { + queryKey: ['query-2'], + queryFn: async () => { + await new Promise((resolve) => setTimeout(resolve, 20)) + return 2 + }, + }, + ], + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.detectChanges() + + const stablePromise = app.whenStable() + let stableResolved = false + void stablePromise.then(() => { + stableResolved = true + }) + + await Promise.resolve() + expect(stableResolved).toBe(false) + + await vi.advanceTimersByTimeAsync(25) + await stablePromise + + const result = fixture.componentInstance.queries() + expect(result[0].status()).toBe('success') + expect(result[1].status()).toBe('success') + expect(result[0].data()).toBe(1) + expect(result[1].data()).toBe(2) + }) + + it('should use latest query key for aliased refetch function', async () => { + const key = signal('one') + const fetchSpy = vi.fn(async (context: any) => { + await new Promise((resolve) => setTimeout(resolve, 10)) + return context.queryKey[1] + }) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class Page { + key = key + fetchSpy = fetchSpy + + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['query', this.key()], + queryFn: this.fetchSpy, + enabled: false, + }, + ], + })) + } + + const rendered = await render(Page) + const query = rendered.fixture.componentInstance.queries()[0] + const refetch = query.refetch + + key.set('two') + rendered.fixture.detectChanges() + + const refetchPromise = refetch() + await vi.advanceTimersByTimeAsync(15) + await refetchPromise + + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(fetchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ['query', 'two'], + }), + ) + }) +}) diff --git a/packages/angular-query/src/__tests__/inject-query-lifecycle.test.ts b/packages/angular-query/src/__tests__/inject-query-lifecycle.test.ts new file mode 100644 index 00000000000..77f67a79744 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-query-lifecycle.test.ts @@ -0,0 +1,79 @@ +import { DestroyRef, NgZone, PendingTasks, runInInjectionContext } from '@angular/core' +import { describe, expect, it, vi } from 'vitest' +import { injectPendingTasksLifecycle } from '../utils/inject-pending-tasks-lifecycle' +import type { Injector } from '@angular/core' + +function setupLifecycle() { + const destroyCallbacks: Array<() => void> = [] + const taskCleanups: Array> = [] + const pendingTasks = { + add: vi.fn(() => { + const cleanup = vi.fn() + taskCleanups.push(cleanup) + return cleanup + }), + } + const destroyRef: { + destroyed: boolean + onDestroy: ReturnType + } = { + destroyed: false, + onDestroy: vi.fn((callback: () => void) => { + destroyCallbacks.push(callback) + return () => { + const index = destroyCallbacks.indexOf(callback) + if (index !== -1) destroyCallbacks.splice(index, 1) + } + }), + } + const injector = { + get(token: unknown) { + if (token === NgZone) return { run: (callback: () => void) => callback() } + if (token === PendingTasks) return pendingTasks + if (token === DestroyRef) return destroyRef + throw new Error(`Unexpected token: ${String(token)}`) + }, + } as Injector + + return { + lifecycle: runInInjectionContext(injector, () => + injectPendingTasksLifecycle(), + ), + pendingTasks, + taskCleanups, + destroy: () => { + destroyRef.destroyed = true + for (const callback of [...destroyCallbacks]) callback() + }, + } +} + +describe('QueryLifecycle', () => { + it('tracks pending state without registering duplicate tasks', () => { + const { lifecycle, pendingTasks, taskCleanups } = setupLifecycle() + + lifecycle.setPending(true) + lifecycle.setPending(true) + + expect(pendingTasks.add).toHaveBeenCalledOnce() + + lifecycle.setPending(false) + expect(taskCleanups[0]).toHaveBeenCalledOnce() + + lifecycle.setPending(true) + expect(pendingTasks.add).toHaveBeenCalledTimes(2) + }) + + it('tracks destruction, releases its task, and cannot restart', () => { + const { lifecycle, pendingTasks, taskCleanups, destroy } = setupLifecycle() + + lifecycle.setPending(true) + destroy() + + expect(lifecycle.destroyed).toBe(true) + expect(taskCleanups[0]).toHaveBeenCalledOnce() + + lifecycle.setPending(true) + expect(pendingTasks.add).toHaveBeenCalledOnce() + }) +}) diff --git a/packages/angular-query/src/__tests__/inject-query.test-d.ts b/packages/angular-query/src/__tests__/inject-query.test-d.ts new file mode 100644 index 00000000000..ecc517df018 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-query.test-d.ts @@ -0,0 +1,250 @@ +import { describe, expectTypeOf, it } from 'vitest' +import { sleep } from '@tanstack/query-test-utils' +import { injectQuery, queryOptions, toResource } from '..' +import type { CreateQueryOptions } from '..' +import type { Resource, Signal } from '@angular/core' + +describe('initialData', () => { + describe('Config object overload', () => { + it('TData should always be defined when initialData is provided as an object', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => ({ wow: true }), + initialData: { wow: true }, + })) + const resource = toResource(query) + + expectTypeOf(query.data).toEqualTypeOf>() + expectTypeOf(resource.value).toEqualTypeOf>() + }) + + it('TData should be defined when passed through queryOptions', () => { + const options = () => + queryOptions({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: { + wow: true, + }, + }) + const { data } = injectQuery(options) + + expectTypeOf(data).toEqualTypeOf>() + }) + + it('should support selection function with select', () => { + const options = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => '1', + select: (data) => { + expectTypeOf(data).toEqualTypeOf() + return parseInt(data) + }, + })) + expectTypeOf(options.data).toEqualTypeOf>() + }) + + it('should be possible to define a different TData than TQueryFnData using select with queryOptions spread into useQuery', () => { + const options = queryOptions({ + queryKey: ['key'], + queryFn: () => Promise.resolve(1), + }) + + const query = injectQuery(() => ({ + ...options, + select: (data) => data > 1, + })) + + expectTypeOf(query.data).toEqualTypeOf>() + }) + + it('TData should always be defined when initialData is provided as a function which ALWAYS returns the data', () => { + const { data } = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => ({ + wow: true, + }), + })) + + expectTypeOf(data).toEqualTypeOf>() + }) + + it('TData should have undefined in the union when initialData is NOT provided', () => { + const { data } = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + })) + + expectTypeOf(data).toEqualTypeOf>() + }) + + it('TData should have undefined in the union when initialData is provided as a function which can return undefined', () => { + const { data } = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + })) + + expectTypeOf(data).toEqualTypeOf>() + }) + + it('TData should be narrowed after an isSuccess check when initialData is provided as a function which can return undefined', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => { + return { + wow: true, + } + }, + initialData: () => undefined as { wow: boolean } | undefined, + })) + + if (query.isSuccess()) { + expectTypeOf(query.data).toEqualTypeOf>() + } + }) + + it('should keep data defined after an isError check when initialData is provided', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => Promise.resolve('Some data'), + initialData: 'initial data', + })) + + if (query.isError()) { + expectTypeOf(query.data).toEqualTypeOf>() + } + }) + + it('should make an isPending branch impossible when initialData is provided', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => Promise.resolve('Some data'), + initialData: 'initial data', + })) + + if (query.isPending()) { + expectTypeOf(query).toEqualTypeOf() + } + }) + }) + + describe('structuralSharing', () => { + it('should be able to use structuralSharing with unknown types', () => { + // https://github.com/TanStack/query/issues/6525#issuecomment-1938411343 + injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => 5, + structuralSharing: (oldData, newData) => { + expectTypeOf(oldData).toBeUnknown() + expectTypeOf(newData).toBeUnknown() + return newData + }, + })) + }) + }) +}) + +describe('Discriminated union return type', () => { + it('should expose status predicates as Angular signals', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + expectTypeOf(query.isSuccess).toMatchTypeOf>() + expectTypeOf(query.isError).toMatchTypeOf>() + expectTypeOf(query.isPending).toMatchTypeOf>() + }) + + it('should expose an Angular resource view', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + const resource = toResource(query) + expectTypeOf(resource).toMatchTypeOf>() + expectTypeOf(resource.reload).toBeCallableWith() + + // @ts-expect-error Resources are created explicitly with toResource. + query.resource + }) + + it('data should be possibly undefined by default', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + expectTypeOf(query.data).toEqualTypeOf>() + }) + + it('data should be defined when query is success', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + if (query.isSuccess()) { + expectTypeOf(query.data).toEqualTypeOf>() + expectTypeOf(toResource(query).value).toEqualTypeOf>() + } + }) + + it('error should be null when query is success', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + if (query.isSuccess()) { + expectTypeOf(query.error).toEqualTypeOf>() + } + }) + + it('data should be undefined when query is pending', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + if (query.isPending()) { + expectTypeOf(query.data).toEqualTypeOf>() + } + }) + + it('error should be defined when query is error', () => { + const query = injectQuery(() => ({ + queryKey: ['key'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + + if (query.isError()) { + expectTypeOf(query.error).toEqualTypeOf>() + } + }) +}) + +describe('injectQuery options', () => { + it('omits observer error reporting options', () => { + expectTypeOf().not.toHaveProperty('throwOnError') + }) +}) diff --git a/packages/angular-query/src/__tests__/inject-query.test.ts b/packages/angular-query/src/__tests__/inject-query.test.ts new file mode 100644 index 00000000000..29f082632e3 --- /dev/null +++ b/packages/angular-query/src/__tests__/inject-query.test.ts @@ -0,0 +1,1636 @@ +import { + ApplicationRef, + ChangeDetectionStrategy, + Component, + ErrorHandler, + computed, + effect, + input, + inputBinding, + signal, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { HttpClient, provideHttpClient } from '@angular/common/http' +import { + HttpTestingController, + provideHttpClientTesting, +} from '@angular/common/http/testing' +import { + afterEach, + beforeEach, + describe, + expect, + expectTypeOf, + it, + vi, +} from 'vitest' +import { render } from '@testing-library/angular' +import { queryKey, sleep } from '@tanstack/query-test-utils' +import { lastValueFrom } from 'rxjs' +import { provideIsRestoring } from '../internal' +import { + QueryCache, + QueryClient, + injectQuery, + provideTanStackQuery, + toResource, +} from '..' +import { provideAngularQueryChangeDetection } from './test-utils' +import type { CreateQueryOptions, OmitKeyof, QueryFunction } from '..' + +// cspell:ignore ZONEFUL + +describe('injectQuery', () => { + let queryCache: QueryCache + let queryClient: QueryClient + beforeEach(() => { + vi.useFakeTimers() + queryCache = new QueryCache() + queryClient = new QueryClient({ queryCache }) + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it('should keep failed refetch state and cached data readable without global reporting', async () => { + vi.useRealTimers() + queryClient.setDefaultOptions({ + queries: { retry: false, throwOnError: true }, + }) + const report = vi.spyOn(TestBed.inject(ErrorHandler), 'handleError') + const error = new Error('query failed') + const options = { + queryKey: ['error'], + initialData: 'cached', + enabled: false, + queryFn: async (): Promise => { + throw error + }, + } + const query = TestBed.runInInjectionContext(() => + injectQuery(() => options), + ) + const cacheReport = vi.fn() + queryCache.config.onError = cacheReport + const second = TestBed.runInInjectionContext(() => + injectQuery(() => options), + ) + await TestBed.inject(ApplicationRef).whenStable() + await expect(query.refetch()).resolves.toMatchObject({ + status: 'error', + error, + }) + expect(query.error()).toBe(error) + expect(second.error()).toBe(error) + expect(cacheReport).toHaveBeenCalledTimes(1) + expect(query.data()).toBe('cached') + expect(query.isError()).toBe(true) + await expect(query.refetch({ throwOnError: true })).rejects.toBe(error) + expect(report).not.toHaveBeenCalled() + }) + + it('should return the correct types', () => { + const key = queryKey() + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + // unspecified query function should default to unknown + noQueryFn = injectQuery(() => ({ + queryKey: key, + })) + + // it should infer the result type from the query function + fromQueryFn = injectQuery(() => ({ + queryKey: key, + queryFn: () => 'test', + })) + + // it should be possible to specify the result type + withResult = injectQuery(() => ({ + queryKey: key, + queryFn: () => 'test', + })) + + // it should be possible to specify the error type + withError = injectQuery(() => ({ + queryKey: key, + queryFn: () => 'test', + })) + + // it should infer the result type from the configuration + withResultInfer = injectQuery(() => ({ + queryKey: key, + queryFn: () => true, + })) + + // it should be possible to specify a union type as result type + unionTypeSync = injectQuery(() => ({ + queryKey: key, + queryFn: () => (Math.random() > 0.5 ? ('a' as const) : ('b' as const)), + })) + + unionTypeAsync = injectQuery<'a' | 'b'>(() => ({ + queryKey: key, + queryFn: () => Promise.resolve(Math.random() > 0.5 ? 'a' : 'b'), + })) + + // it should infer the result type from a generic query function + fromGenericQueryFn = (() => { + function queryFn(): Promise { + return Promise.resolve({} as T) + } + return injectQuery(() => ({ + queryKey: key, + queryFn: () => queryFn(), + })) + })() + + // todo use query options? + fromGenericOptionsQueryFn = (() => { + function queryFn(): Promise { + return Promise.resolve({} as T) + } + return injectQuery(() => ({ + queryKey: key, + queryFn: () => queryFn(), + })) + })() + + fromMyDataArrayKeyQueryFn = (() => { + type MyData = number + type MyQueryKey = readonly ['my-data', number] + const getMyDataArrayKey: QueryFunction = ({ + queryKey: [, n], + }) => { + return n + 42 + } + return injectQuery(() => ({ + queryKey: ['my-data', 100] as const, + queryFn: getMyDataArrayKey, + })) + })() + + // it should handle query-functions that return Promise + fromPromiseAnyQueryFn = injectQuery(() => ({ + queryKey: key, + queryFn: () => fetch('return Promise').then((resp) => resp.json()), + })) + + fromGetMyDataStringKeyQueryFn = (() => { + type MyData = number + const getMyDataStringKey: QueryFunction = (context) => { + expectTypeOf(context.queryKey).toEqualTypeOf<['1']>() + return Number(context.queryKey[0]) + 42 + } + return injectQuery(() => ({ + queryKey: ['1'] as ['1'], + queryFn: getMyDataStringKey, + })) + })() + + // Wrapped queries + fromWrappedQuery = (() => { + const createWrappedQuery = < + TQueryKey extends [string, Record?], + TQueryFnData, + TError, + TData = TQueryFnData, + >( + qk: TQueryKey, + fetcher: (obj: TQueryKey[1], token: string) => Promise, + options?: OmitKeyof< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData', + 'safely' + >, + ) => + injectQuery(() => ({ + queryKey: qk, + queryFn: () => fetcher(qk[1], 'token'), + ...options, + })) + return createWrappedQuery([''], () => Promise.resolve('1')) + })() + + fromWrappedFuncStyleQuery = (() => { + const createWrappedFuncStyleQuery = < + TQueryKey extends [string, Record?], + TQueryFnData, + TError, + TData = TQueryFnData, + >( + qk: TQueryKey, + fetcher: () => Promise, + options?: OmitKeyof< + CreateQueryOptions, + 'queryKey' | 'queryFn' | 'initialData', + 'safely' + >, + ) => injectQuery(() => ({ queryKey: qk, queryFn: fetcher, ...options })) + return createWrappedFuncStyleQuery([''], () => Promise.resolve(true)) + })() + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const { + noQueryFn, + fromQueryFn, + withResult, + withError, + withResultInfer, + unionTypeSync, + unionTypeAsync, + fromGenericQueryFn, + fromGenericOptionsQueryFn, + fromMyDataArrayKeyQueryFn, + fromPromiseAnyQueryFn, + fromGetMyDataStringKeyQueryFn, + fromWrappedQuery, + fromWrappedFuncStyleQuery, + } = fixture.componentInstance + + expectTypeOf(noQueryFn.data()).toEqualTypeOf() + expectTypeOf(noQueryFn.error()).toEqualTypeOf() + + expectTypeOf(fromQueryFn.data()).toEqualTypeOf() + expectTypeOf(fromQueryFn.error()).toEqualTypeOf() + + expectTypeOf(withResult.data()).toEqualTypeOf() + expectTypeOf(withResult.error()).toEqualTypeOf() + + expectTypeOf(withError.data()).toEqualTypeOf() + expectTypeOf(withError.error()).toEqualTypeOf<{ message: string } | null>() + + expectTypeOf(withResultInfer.data()).toEqualTypeOf() + expectTypeOf(withResultInfer.error()).toEqualTypeOf() + + expectTypeOf(unionTypeSync.data()).toEqualTypeOf<'a' | 'b' | undefined>() + expectTypeOf(unionTypeAsync.data()).toEqualTypeOf<'a' | 'b' | undefined>() + + expectTypeOf(fromGenericQueryFn.data()).toEqualTypeOf() + expectTypeOf(fromGenericQueryFn.error()).toEqualTypeOf() + + expectTypeOf(fromGenericOptionsQueryFn.data()).toEqualTypeOf< + string | undefined + >() + expectTypeOf( + fromGenericOptionsQueryFn.error(), + ).toEqualTypeOf() + + expectTypeOf(fromMyDataArrayKeyQueryFn.data()).toEqualTypeOf< + number | undefined + >() + + expectTypeOf(fromPromiseAnyQueryFn.data()).toEqualTypeOf() + expectTypeOf(fromGetMyDataStringKeyQueryFn.data()).toEqualTypeOf< + number | undefined + >() + expectTypeOf(fromWrappedQuery.data()).toEqualTypeOf() + expectTypeOf(fromWrappedFuncStyleQuery.data()).toEqualTypeOf< + boolean | undefined + >() + }) + + it('should return pending status initially', () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['key1'], + queryFn: () => sleep(10).then(() => 'Some data'), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + fixture.autoDetectChanges() + const query = fixture.componentInstance.query + + expect(query.status()).toBe('pending') + expect(query.isPending()).toBe(true) + expect(query.isFetching()).toBe(true) + expect(query.isStale()).toBe(true) + expect(query.isFetched()).toBe(false) + }) + + it('should resolve to success and update signal: injectQuery()', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['key2'], + queryFn: () => sleep(10).then(() => 'result2'), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + await vi.advanceTimersByTimeAsync(11) + expect(query.status()).toBe('success') + expect(query.data()).toBe('result2') + expect(query.isPending()).toBe(false) + expect(query.isFetching()).toBe(false) + expect(query.isFetched()).toBe(true) + expect(query.isSuccess()).toBe(true) + }) + + it('should reject and update signal', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + retry: false, + queryKey: ['key3'], + queryFn: () => + sleep(10).then(() => Promise.reject(new Error('Some error'))), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + await vi.advanceTimersByTimeAsync(11) + expect(query.status()).toBe('error') + expect(query.data()).toBe(undefined) + expect(query.error()).toMatchObject({ message: 'Some error' }) + expect(query.isPending()).toBe(false) + expect(query.isFetching()).toBe(false) + expect(query.isError()).toBe(true) + expect(query.failureCount()).toBe(1) + expect(query.failureReason()).toMatchObject({ message: 'Some error' }) + }) + + it('should expose the query result as an Angular resource', async () => { + const queryFn = vi.fn(() => sleep(10).then(() => 'result')) + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['resource-query'], + queryFn, + })), + ) + + TestBed.tick() + expect('resource' in query).toBe(false) + const resource = toResource(query) + expect(resource.status()).toBe('loading') + expect(resource.snapshot()).toEqual({ status: 'loading', value: undefined }) + expect(resource.isLoading()).toBe(true) + expect(resource.hasValue()).toBe(false) + + await vi.advanceTimersByTimeAsync(11) + + expect(resource.status()).toBe('resolved') + expect(resource.snapshot()).toEqual({ status: 'resolved', value: 'result' }) + expect(resource.isLoading()).toBe(false) + expect(resource.hasValue()).toBe(true) + expect(resource.value()).toBe('result') + + expect(resource.reload()).toBe(true) + await Promise.resolve() + expect(queryFn).toHaveBeenCalledTimes(2) + + expect(resource.status()).toBe('reloading') + expect(resource.snapshot()).toEqual({ + status: 'reloading', + value: 'result', + }) + expect(resource.isLoading()).toBe(true) + expect(resource.value()).toBe('result') + expect(resource.reload()).toBe(false) + expect(queryFn).toHaveBeenCalledTimes(2) + + await vi.advanceTimersByTimeAsync(11) + + expect(resource.status()).toBe('resolved') + expect(resource.value()).toBe('result') + }) + + it.each([undefined, null, 'query failed'])( + 'should normalize a %s query rejection for the resource error state', + async (rejection) => { + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['resource-query-error', rejection], + queryFn: () => Promise.reject(rejection), + retry: false, + })), + ) + + TestBed.tick() + await vi.advanceTimersByTimeAsync(1) + + const resource = toResource(query) + const resourceError = resource.error() + expect(resource.status()).toBe('error') + expect(resourceError).toBeInstanceOf(Error) + expect(resourceError?.cause).toBe(rejection) + expect(resource.snapshot()).toEqual({ + status: 'error', + error: resourceError, + }) + + let thrown: unknown + try { + resource.value() + } catch (error) { + thrown = error + } + expect(thrown).toBe(resourceError) + }, + ) + + it('should preserve Error-like query rejections', async () => { + const rejection = { name: 'QueryError', message: 'query failed' } + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['resource-query-error-like'], + queryFn: () => Promise.reject(rejection), + retry: false, + })), + ) + + TestBed.tick() + await vi.advanceTimersByTimeAsync(1) + + const resource = toResource(query) + expect(resource.status()).toBe('error') + expect(resource.error()).toBe(rejection) + }) + + it('should not reload fresh query resources', async () => { + const queryFn = vi.fn(() => sleep(10).then(() => 'fresh-result')) + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['fresh-resource-query'], + queryFn, + staleTime: Infinity, + })), + ) + + TestBed.tick() + await vi.advanceTimersByTimeAsync(11) + + const resource = toResource(query) + expect(resource.status()).toBe('resolved') + expect(resource.reload()).toBe(false) + expect(queryFn).toHaveBeenCalledTimes(1) + }) + + it('should expose disabled queries as idle resources', () => { + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + enabled: false, + queryKey: ['idle-resource-query'], + queryFn: () => sleep(10).then(() => 'result'), + })), + ) + + TestBed.tick() + const resource = toResource(query) + + expect(resource.status()).toBe('idle') + expect(resource.snapshot()).toEqual({ status: 'idle', value: undefined }) + expect(resource.isLoading()).toBe(false) + expect(resource.hasValue()).toBe(false) + expect(resource.value()).toBe(undefined) + expect(resource.reload()).toBe(false) + }) + + it('should update query on options contained signal change', async () => { + const key = signal(['key6', 'key7']) + const spy = vi.fn(() => sleep(10).then(() => 'Some data')) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + key = key + spy = spy + query = injectQuery(() => ({ + queryKey: this.key(), + queryFn: this.spy, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + await vi.advanceTimersByTimeAsync(0) + expect(spy).toHaveBeenCalledTimes(1) + + await vi.advanceTimersByTimeAsync(10) + expect(query.status()).toBe('success') + + key.set(['key8']) + fixture.detectChanges() + + expect(spy).toHaveBeenCalledTimes(2) + // should call queryFn with context containing the new queryKey + expect(spy).toHaveBeenNthCalledWith(2, { + client: queryClient, + meta: undefined, + queryKey: ['key8'], + signal: expect.anything(), + }) + }) + + it('allows cache listeners to read the result while options update', () => { + const key = signal('one') + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['reentrant-options', key()], + queryFn: () => 'data', + enabled: false, + })), + ) + + TestBed.tick() + const readResult = vi.fn(() => query.status()) + const unsubscribe = queryCache.subscribe((event) => { + // setOptions emits synchronously; this read must not re-enter the signal + // computation that caused the options update. + if (event.type === 'observerOptionsUpdated') readResult() + }) + + key.set('two') + + expect(() => TestBed.tick()).not.toThrow() + expect(readResult).toHaveBeenCalled() + + unsubscribe() + }) + + it('allows deferred cache-listener reads during restoration detach and reattach', async () => { + const isRestoring = signal(false) + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['reentrant-restoration-subscription'], + queryFn: () => 'data', + enabled: false, + })), + ) + expect(query.status()).toBe('pending') + TestBed.tick() + + const nestedStatuses: Array = [] + const unsubscribe = queryCache.subscribe((event) => { + if (event.type === 'observerAdded' || event.type === 'observerRemoved') { + void Promise.resolve().then(() => nestedStatuses.push(query.status())) + } + }) + + isRestoring.set(true) + expect(() => TestBed.tick()).not.toThrow() + isRestoring.set(false) + expect(() => TestBed.tick()).not.toThrow() + + await Promise.resolve() + expect(nestedStatuses).toEqual(['pending', 'pending']) + unsubscribe() + }) + + it('allows deferred cache-listener reads while an option change creates a query', async () => { + const key = signal('old') + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['reentrant-query-added', key()], + queryFn: () => 'data', + enabled: false, + })), + ) + + expect(query.status()).toBe('pending') + TestBed.tick() + const nestedStatuses: Array = [] + const unsubscribe = queryCache.subscribe((event) => { + if (event.type === 'added') + void Promise.resolve().then(() => nestedStatuses.push(query.status())) + }) + + key.set('new') + + expect(() => query.status()).not.toThrow() + expect(nestedStatuses).toEqual([]) + expect(() => TestBed.tick()).not.toThrow() + await Promise.resolve() + expect(nestedStatuses).toEqual(['pending']) + unsubscribe() + }) + + it('keeps the external subscription when non-key options change', () => { + const staleTime = signal(0) + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['stable-option-subscription'], + queryFn: () => 'data', + enabled: false, + staleTime: staleTime(), + })), + ) + + expect(query.status()).toBe('pending') + TestBed.tick() + const cachedQuery = queryCache.find({ + queryKey: ['stable-option-subscription'], + })! + const subscriptionEvents: Array = [] + const unsubscribe = queryCache.subscribe((event) => { + if (event.type === 'observerAdded' || event.type === 'observerRemoved') { + subscriptionEvents.push(event.type) + } + }) + + staleTime.set(1) + expect(query.status()).toBe('pending') + TestBed.tick() + + expect(cachedQuery.getObserversCount()).toBe(1) + expect(subscriptionEvents).toEqual([]) + unsubscribe() + }) + + it('should only run query once enabled signal is set to true', async () => { + const spy = vi.fn(() => sleep(10).then(() => 'Some data')) + const enabled = signal(false) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + enabled = enabled + spy = spy + query = injectQuery(() => ({ + queryKey: ['key9'], + queryFn: this.spy, + enabled: this.enabled(), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(spy).not.toHaveBeenCalled() + expect(query.status()).toBe('pending') + + enabled.set(true) + fixture.detectChanges() + + await vi.advanceTimersByTimeAsync(10) + expect(spy).toHaveBeenCalledTimes(1) + expect(query.status()).toBe('success') + }) + + it('should properly execute dependant queries', async () => { + const dependentQueryFn = vi + .fn() + .mockImplementation(() => sleep(1000).then(() => 'Some data')) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query1 = injectQuery(() => ({ + queryKey: ['dependant1'], + queryFn: () => sleep(10).then(() => 'Some data'), + })) + + query2 = injectQuery( + computed(() => ({ + queryKey: ['dependent2'], + queryFn: dependentQueryFn, + enabled: !!this.query1.data(), + })), + ) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + fixture.autoDetectChanges() + const { query1, query2 } = fixture.componentInstance + + expect(query1.data()).toStrictEqual(undefined) + expect(query2.fetchStatus()).toStrictEqual('idle') + expect(dependentQueryFn).not.toHaveBeenCalled() + + await vi.advanceTimersByTimeAsync(11) + + expect(query1.data()).toStrictEqual('Some data') + expect(query2.fetchStatus()).toStrictEqual('fetching') + + await vi.advanceTimersByTimeAsync(1002) + + expect(query2.fetchStatus()).toStrictEqual('idle') + expect(query2.status()).toStrictEqual('success') + expect(dependentQueryFn).toHaveBeenCalledTimes(1) + expect(dependentQueryFn).toHaveBeenCalledWith( + expect.objectContaining({ queryKey: ['dependent2'] }), + ) + }) + + it('should use the current value for the queryKey when refetch is called', async () => { + const fetchFn = vi.fn(() => sleep(10).then(() => 'Some data')) + const keySignal = signal('key11') + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + keySignal = keySignal + fetchFn = fetchFn + query = injectQuery(() => ({ + queryKey: ['key10', this.keySignal()], + queryFn: this.fetchFn, + enabled: false, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(fetchFn).not.toHaveBeenCalled() + + const first = query.refetch() + await vi.advanceTimersByTimeAsync(10) + await first + expect(fetchFn).toHaveBeenCalledTimes(1) + expect(fetchFn).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ['key10', 'key11'], + }), + ) + + keySignal.set('key12') + const second = query.refetch() + await vi.advanceTimersByTimeAsync(10) + await second + expect(fetchFn).toHaveBeenCalledTimes(2) + expect(fetchFn).toHaveBeenCalledWith( + expect.objectContaining({ + queryKey: ['key10', 'key12'], + }), + ) + }) + + it('should keep initialData visible alongside the error when a refetch fails', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['initialDataError'], + queryFn: () => + sleep(10).then(() => Promise.reject(new Error('Some error'))), + initialData: 'initial', + retry: false, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(query.data()).toBe('initial') + expect(query.isError()).toBe(false) + expect(query.status()).toBe('success') + + await vi.advanceTimersByTimeAsync(11) + fixture.detectChanges() + + expect(query.data()).toBe('initial') + expect(query.isError()).toBe(true) + expect(query.status()).toBe('error') + }) + + it('should support selection function with select', async () => { + const app = TestBed.inject(ApplicationRef) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['key13'], + queryFn: () => [{ id: 1 }, { id: 2 }], + select: (data) => data.map((item) => item.id), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + // Wait for query to complete (even synchronous queryFn needs time to process) + const stablePromise = app.whenStable() + await Promise.resolve() + await vi.advanceTimersByTimeAsync(10) + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toEqual([1, 2]) + }) + + it('should set state to error when queryFn returns reject promise', async () => { + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + retry: false, + queryKey: ['key15'], + queryFn: () => + sleep(10).then(() => Promise.reject(new Error('Some error'))), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(query.status()).toBe('pending') + + await vi.advanceTimersByTimeAsync(11) + + expect(query.status()).toBe('error') + }) + + it('should render with required signal inputs', async () => { + @Component({ + selector: 'app-fake', + template: `{{ query.data() }}`, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class FakeComponent { + name = input.required() + + query = injectQuery(() => ({ + queryKey: ['fake', this.name()], + queryFn: () => this.name(), + })) + } + + const name = signal('signal-input-required-test') + const rendered = await render(FakeComponent, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() + rendered.fixture.autoDetectChanges() + await vi.advanceTimersByTimeAsync(0) + + const result = rendered.fixture.nativeElement.textContent + expect(result).toEqual('signal-input-required-test') + }) + + it('should support aliasing query.data on required signal inputs', async () => { + @Component({ + selector: 'app-fake', + template: `{{ data() }}`, + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class FakeComponent { + name = input.required() + + query = injectQuery(() => ({ + queryKey: ['fake-alias', this.name()], + queryFn: () => this.name(), + })) + + data = this.query.data + } + + const name = signal('signal-input-alias-test') + const rendered = await render(FakeComponent, { + bindings: [inputBinding('name', name.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() + rendered.fixture.autoDetectChanges() + await vi.advanceTimersByTimeAsync(0) + + const result = rendered.fixture.nativeElement.textContent + expect(result).toEqual('signal-input-alias-test') + }) + + it('should allow reading the query data on effect registered before injection', () => { + const spy = vi.fn() + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + readEffect = effect(() => { + spy(this.query.data()) + }) + + query = injectQuery(() => ({ + queryKey: ['effect-before-injection'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + expect(spy).toHaveBeenCalledWith(undefined) + }) + + it('should render with an initial value for input signal if available before change detection', async () => { + const key1 = queryKey() as [string] + const key2 = queryKey() as [string] + queryClient.setQueryData(key1, 'value 1') + queryClient.setQueryData(key2, 'value 2') + + @Component({ + selector: 'app-test', + template: '{{ query.data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + inputKey = input.required<[string]>() + query = injectQuery(() => ({ + queryKey: this.inputKey(), + queryFn: () => sleep(0).then(() => 'Some data'), + })) + } + + const inputKey = signal<[string]>(key1) + const rendered = await render(TestComponent, { + bindings: [inputBinding('inputKey', inputKey.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() + + const instance = rendered.fixture.componentInstance + const query = instance.query + + expect(() => instance.inputKey()).not.toThrow() + + expect(instance.inputKey()).toEqual(key1) + expect(query.data()).toEqual('value 1') + + inputKey.set(key2) + rendered.fixture.detectChanges() + + expect(instance.inputKey()).toEqual(key2) + expect(query.data()).toEqual('value 2') + }) + + it('should allow reading the query data on component ngOnInit with required signal input', async () => { + const spy = vi.fn() + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + key = input.required<[string]>() + query = injectQuery(() => ({ + queryKey: this.key(), + queryFn: () => Promise.resolve(() => 'Some data'), + })) + + initialStatus!: string + + ngOnInit() { + this.initialStatus = this.query.status() + + // effect should not have been called yet + expect(spy).not.toHaveBeenCalled() + } + + _spyEffect = effect(() => { + spy() + }) + } + + const key = signal<[string]>(['ngOnInitTest']) + const rendered = await render(TestComponent, { + bindings: [inputBinding('key', key.asReadonly())], + detectChangesOnRender: false, + }) + rendered.fixture.detectChanges() + + const fixture = rendered.fixture + expect(spy).toHaveBeenCalled() + + const instance = fixture.componentInstance + expect(instance.initialStatus).toEqual('pending') + }) + + it('should update query data synchronously when query data changes', () => { + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['test'], + initialData: 'initial data', + })), + ) + + TestBed.tick() + + expect(query.data()).toBe('initial data') + queryClient.setQueryData(['test'], 'new data') + expect(query.data()).toBe('new data') + }) + + it('should pause fetching while restoring and fetch once restoring is disabled', async () => { + const isRestoring = signal(true) + const fetchSpy = vi.fn(() => sleep(10).then(() => 'restored-data')) + + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['restoring'], + queryFn: fetchSpy, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + + const query = fixture.componentInstance.query + expect(fetchSpy).not.toHaveBeenCalled() + expect(query.status()).toBe('pending') + + const stablePromise = fixture.whenStable() + await Promise.resolve() + await stablePromise + + isRestoring.set(false) + fixture.detectChanges() + + await vi.advanceTimersByTimeAsync(11) + await fixture.whenStable() + + expect(fetchSpy).toHaveBeenCalledTimes(1) + expect(query.status()).toBe('success') + expect(query.data()).toBe('restored-data') + + const cachedQuery = queryClient + .getQueryCache() + .find({ queryKey: ['restoring'] })! + expect(cachedQuery.getObserversCount()).toBe(1) + + isRestoring.set(true) + fixture.detectChanges() + expect(cachedQuery.getObserversCount()).toBe(0) + + isRestoring.set(false) + fixture.detectChanges() + expect(cachedQuery.getObserversCount()).toBe(1) + + fixture.destroy() + expect(cachedQuery.getObserversCount()).toBe(0) + }) + + it.each(['options-first', 'restoring-first'] as const)( + 'uses the latest options when leaving restoration in the same turn (%s)', + async (writeOrder) => { + const isRestoring = signal(true) + const key = signal('old') + const queriedKeys: Array = [] + + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['same-turn-restoration', key()], + queryFn: ({ queryKey: currentQueryKey }) => { + const currentKey = currentQueryKey[1] as string + queriedKeys.push(currentKey) + return Promise.resolve(currentKey) + }, + })), + ) + + expect(query.status()).toBe('pending') + expect(queriedKeys).toEqual([]) + + if (writeOrder === 'options-first') { + key.set('new') + isRestoring.set(false) + } else { + isRestoring.set(false) + key.set('new') + } + TestBed.tick() + const stablePromise = TestBed.inject(ApplicationRef).whenStable() + await vi.advanceTimersByTimeAsync(0) + await stablePromise + + expect(queriedKeys).toEqual(['new']) + expect(query.data()).toBe('new') + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['same-turn-restoration', 'old'] })?.state + .fetchStatus, + ).toBe('idle') + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['same-turn-restoration', 'new'] }) + ?.getObserversCount(), + ).toBe(1) + }, + ) + + it('allows a cache listener to read the result during restoration-boundary configuration', () => { + const isRestoring = signal(true) + const key = signal('old') + + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['restoration-reentrant', key()], + queryFn: () => 'data', + enabled: false, + })), + ) + + expect(query.status()).toBe('pending') + TestBed.tick() + + const boundaryRead = vi.fn(() => query.status()) + const unsubscribe = queryCache.subscribe((event) => { + if ( + event.type === 'observerOptionsUpdated' && + event.query.getObserversCount() === 0 + ) { + boundaryRead() + } + }) + + key.set('new') + isRestoring.set(false) + + expect(() => TestBed.tick()).not.toThrow() + expect(boundaryRead).toHaveBeenCalled() + expect( + queryCache + .find({ queryKey: ['restoration-reentrant', 'new'] }) + ?.getObserversCount(), + ).toBe(1) + + unsubscribe() + }) + + it('converges to the current query after an early restoration read', async () => { + const isRestoring = signal(true) + const key = signal('old') + const queriedKeys: Array = [] + const oldQueryKey = ['early-restoration-read', 'old'] as const + const newQueryKey = ['early-restoration-read', 'new'] as const + + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['early-restoration-read', key()], + queryFn: ({ queryKey: currentQueryKey }) => { + const currentKey = currentQueryKey[1] as string + queriedKeys.push(currentKey) + return Promise.resolve(currentKey) + }, + staleTime: Infinity, + })), + ) + + expect(query.status()).toBe('pending') + TestBed.tick() + const oldQuery = queryCache.find({ queryKey: oldQueryKey })! + expect(oldQuery.getObserversCount()).toBe(0) + + key.set('new') + isRestoring.set(false) + + // Pull before the option-configuration effect gets a chance to run. + expect(query.status()).toBe('pending') + expect(queryCache.find({ queryKey: newQueryKey })).toBeDefined() + + // A cache write to the optimistic query is adopted after configuration. + queryClient.setQueryData(newQueryKey, 'early data') + const newQuery = queryCache.find({ queryKey: newQueryKey })! + expect(newQuery.getObserversCount()).toBe(0) + TestBed.tick() + const stablePromise = TestBed.inject(ApplicationRef).whenStable() + await vi.advanceTimersByTimeAsync(0) + await stablePromise + + expect(oldQuery.getObserversCount()).toBe(0) + expect(newQuery.getObserversCount()).toBe(1) + expect(queriedKeys).not.toContain('new') + expect(query.data()).toBe('early data') + }) + + it('reads optimistic options before moving the subscription on the next tick', () => { + const key = signal('old') + const oldQueryKey = ['early-options-read', 'old'] as const + const newQueryKey = ['early-options-read', 'new'] as const + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['early-options-read', key()], + queryFn: () => 'unused', + enabled: false, + })), + ) + + expect(query.status()).toBe('pending') + TestBed.tick() + const oldQuery = queryCache.find({ queryKey: oldQueryKey })! + expect(oldQuery.getObserversCount()).toBe(1) + + key.set('new') + + expect(query.status()).toBe('pending') + // An existing observer stays attached until the configuration effect can + // move it atomically. + expect(oldQuery.getObserversCount()).toBe(1) + expect(queryCache.find({ queryKey: newQueryKey })).toBeDefined() + + queryClient.setQueryData(newQueryKey, 'early data') + const newQuery = queryCache.find({ queryKey: newQueryKey })! + expect(newQuery.getObserversCount()).toBe(0) + TestBed.tick() + + expect(oldQuery.getObserversCount()).toBe(0) + expect(newQuery.getObserversCount()).toBe(1) + expect(query.data()).toBe('early data') + }) + + describe('injection context', () => { + it('throws NG0203 with descriptive error outside injection context', () => { + expect(() => { + injectQuery(() => ({ + queryKey: ['injectionContextError'], + queryFn: () => sleep(0).then(() => 'Some data'), + })) + }).toThrowError(/NG0203(.*?)injectQuery/) + }) + + + it('should complete queries before whenStable() resolves', async () => { + const app = TestBed.inject(ApplicationRef) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['pendingTasksTest'], + queryFn: async () => { + await sleep(50) + return 'test data' + }, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + expect(query.status()).toBe('pending') + expect(query.data()).toBeUndefined() + + await vi.advanceTimersByTimeAsync(60) + await app.whenStable() + + expect(query.status()).toBe('success') + expect(query.data()).toBe('test data') + }) + + it('should complete HttpClient-based queries before whenStable() resolves', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + provideHttpClient(), + provideHttpClientTesting(), + ], + }) + + const app = TestBed.inject(ApplicationRef) + const httpClient = TestBed.inject(HttpClient) + const httpTestingController = TestBed.inject(HttpTestingController) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + httpClient = httpClient + query = injectQuery(() => ({ + queryKey: ['httpClientTest'], + queryFn: () => + lastValueFrom( + this.httpClient.get<{ message: string }>('/api/test'), + ), + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + setTimeout(() => { + const req = httpTestingController.expectOne('/api/test') + req.flush({ message: 'http test data' }) + }, 10) + + expect(query.status()).toBe('pending') + + // Advance timers and wait for Angular to be "stable" + await vi.advanceTimersByTimeAsync(20) + await app.whenStable() + + // Query should be complete after whenStable() thanks to PendingTasks integration + expect(query.status()).toBe('success') + expect(query.data()).toEqual({ message: 'http test data' }) + + httpTestingController.verify() + }) + + it('should handle synchronous queryFn with staleTime', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + const app = TestBed.inject(ApplicationRef) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + callCount = 0 + query = injectQuery(() => ({ + queryKey: ['sync-stale'], + staleTime: 1000, + queryFn: () => { + this.callCount++ + return `sync-data-${this.callCount}` + }, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const component = fixture.componentInstance + const query = component.query + + const stablePromise = app.whenStable() + await vi.advanceTimersToNextTimerAsync() + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toBe('sync-data-1') + expect(component.callCount).toBe(1) + + await query.refetch() + await Promise.resolve() + await vi.runAllTimersAsync() + await app.whenStable() + + expect(query.status()).toBe('success') + expect(query.data()).toBe('sync-data-2') + expect(component.callCount).toBe(2) + }) + + it('should handle enabled/disabled transitions with synchronous queryFn', async () => { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + const app = TestBed.inject(ApplicationRef) + const enabledSignal = signal(false) + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + enabledSignal = enabledSignal + callCount = 0 + query = injectQuery(() => ({ + queryKey: ['sync-enabled'], + enabled: this.enabledSignal(), + queryFn: () => { + this.callCount++ + return `sync-data-${this.callCount}` + }, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const component = fixture.componentInstance + const query = component.query + + await vi.advanceTimersByTimeAsync(0) + await app.whenStable() + expect(query.status()).toBe('pending') + expect(query.data()).toBeUndefined() + expect(component.callCount).toBe(0) + + enabledSignal.set(true) + fixture.detectChanges() + + const stablePromise = app.whenStable() + await vi.advanceTimersToNextTimerAsync() + await stablePromise + + expect(query.status()).toBe('success') + expect(query.data()).toBe('sync-data-1') + expect(component.callCount).toBe(1) + }) + + it('should handle query invalidation with synchronous data', async () => { + vi.useRealTimers() + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + const app = TestBed.inject(ApplicationRef) + const testKey = ['sync-invalidate'] + + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + callCount = 0 + query = injectQuery(() => ({ + queryKey: testKey, + queryFn: () => { + this.callCount++ + return `sync-data-${this.callCount}` + }, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const component = fixture.componentInstance + const query = component.query + + await app.whenStable() + + expect(query.status()).toBe('success') + expect(query.data()).toBe('sync-data-1') + expect(component.callCount).toBe(1) + + await queryClient.invalidateQueries({ queryKey: testKey }) + await app.whenStable() + expect(query.status()).toBe('success') + expect(query.data()).toBe('sync-data-2') + expect(component.callCount).toBe(2) + }) + + it('should keep the application unstable while invalidation is pending', async () => { + vi.useRealTimers() + let resolveRefetch!: (value: string) => void + let callCount = 0 + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['pending-invalidation'], + queryFn: () => { + callCount++ + + if (callCount === 1) { + return 'data-1' + } + + return new Promise((resolve) => { + resolveRefetch = resolve + }) + }, + })), + ) + + const app = TestBed.inject(ApplicationRef) + await app.whenStable() + + const invalidation = queryClient.invalidateQueries({ + queryKey: ['pending-invalidation'], + }) + const stable = app.whenStable() + let stableResolved = false + void stable.then(() => { + stableResolved = true + }) + + expect(stableResolved).toBe(false) + + resolveRefetch('data-2') + + await invalidation + await stable + + expect(query.data()).toBe('data-2') + expect(query.fetchStatus()).toBe('idle') + }) + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts b/packages/angular-query/src/__tests__/mutation-options.test-d.ts similarity index 98% rename from packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts rename to packages/angular-query/src/__tests__/mutation-options.test-d.ts index 9df834a6b56..141def39454 100644 --- a/packages/angular-query-experimental/src/__tests__/mutation-options.test-d.ts +++ b/packages/angular-query/src/__tests__/mutation-options.test-d.ts @@ -176,7 +176,7 @@ describe('mutationOptions', () => { it('should infer types when used with injectIsMutating', () => { const key = queryKey() - const isMutating = injectIsMutating( + const isMutating = injectIsMutating(() => mutationOptions({ mutationKey: key, mutationFn: () => Promise.resolve(5), @@ -184,8 +184,8 @@ describe('mutationOptions', () => { ) expectTypeOf(isMutating()).toEqualTypeOf() - injectIsMutating( - // @ts-expect-error filters should have mutationKey + // @ts-expect-error filters should have mutationKey + injectIsMutating(() => mutationOptions({ mutationFn: () => Promise.resolve(5), }), diff --git a/packages/angular-query-experimental/src/__tests__/mutation-options.test.ts b/packages/angular-query/src/__tests__/mutation-options.test.ts similarity index 84% rename from packages/angular-query-experimental/src/__tests__/mutation-options.test.ts rename to packages/angular-query/src/__tests__/mutation-options.test.ts index 538e353852b..6ed633f6ea0 100644 --- a/packages/angular-query-experimental/src/__tests__/mutation-options.test.ts +++ b/packages/angular-query/src/__tests__/mutation-options.test.ts @@ -1,5 +1,5 @@ +import { Component } from '@angular/core' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { provideZonelessChangeDetection } from '@angular/core' import { TestBed } from '@angular/core/testing' import { QueryClient } from '@tanstack/query-core' import { queryKey, sleep } from '@tanstack/query-test-utils' @@ -10,6 +10,7 @@ import { mutationOptions, provideTanStackQuery, } from '..' +import { provideAngularQueryChangeDetection } from './test-utils' import type { CreateMutationOptions } from '../types' describe('mutationOptions', () => { @@ -20,8 +21,8 @@ describe('mutationOptions', () => { queryClient = new QueryClient() TestBed.configureTestingModule({ providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), ], }) }) @@ -55,15 +56,19 @@ describe('mutationOptions', () => { mutationFn: () => sleep(50).then(() => 'data'), }) - const [mutation, isMutating] = TestBed.runInInjectionContext(() => [ - injectMutation(() => mutationOpts), - injectIsMutating(), - ]) + @Component({ template: '' }) + class Host { + readonly mutation = injectMutation(() => mutationOpts) + readonly isMutating = injectIsMutating() + } + const fixture = TestBed.createComponent(Host) + fixture.detectChanges() + const { mutation, isMutating } = fixture.componentInstance expect(isMutating()).toBe(0) mutation.mutate() - expect(isMutating()).toBe(0) + expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(0) expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(51) @@ -75,15 +80,19 @@ describe('mutationOptions', () => { mutationFn: () => sleep(50).then(() => 'data'), }) - const [mutation, isMutating] = TestBed.runInInjectionContext(() => [ - injectMutation(() => mutationOpts), - injectIsMutating(), - ]) + @Component({ template: '' }) + class Host { + readonly mutation = injectMutation(() => mutationOpts) + readonly isMutating = injectIsMutating() + } + const fixture = TestBed.createComponent(Host) + fixture.detectChanges() + const { mutation, isMutating } = fixture.componentInstance expect(isMutating()).toBe(0) mutation.mutate() - expect(isMutating()).toBe(0) + expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(0) expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(51) @@ -100,19 +109,21 @@ describe('mutationOptions', () => { mutationFn: () => sleep(50).then(() => 'data2'), }) - const [mutation1, mutation2, isMutating] = TestBed.runInInjectionContext( - () => [ - injectMutation(() => mutationOpts1), - injectMutation(() => mutationOpts2), - injectIsMutating(), - ], - ) + @Component({ template: '' }) + class Host { + readonly mutation1 = injectMutation(() => mutationOpts1) + readonly mutation2 = injectMutation(() => mutationOpts2) + readonly isMutating = injectIsMutating() + } + const fixture = TestBed.createComponent(Host) + fixture.detectChanges() + const { mutation1, mutation2, isMutating } = fixture.componentInstance expect(isMutating()).toBe(0) mutation1.mutate() mutation2.mutate() - expect(isMutating()).toBe(0) + expect(isMutating()).toBe(2) await vi.advanceTimersByTimeAsync(0) expect(isMutating()).toBe(2) await vi.advanceTimersByTimeAsync(51) @@ -129,19 +140,23 @@ describe('mutationOptions', () => { mutationFn: () => sleep(50).then(() => 'data2'), }) - const [mutation1, mutation2, isMutating] = TestBed.runInInjectionContext( - () => [ - injectMutation(() => mutationOpts1), - injectMutation(() => mutationOpts2), - injectIsMutating({ mutationKey: mutationOpts1.mutationKey }), - ], - ) + @Component({ template: '' }) + class Host { + readonly mutation1 = injectMutation(() => mutationOpts1) + readonly mutation2 = injectMutation(() => mutationOpts2) + readonly isMutating = injectIsMutating(() => ({ + mutationKey: mutationOpts1.mutationKey, + })) + } + const fixture = TestBed.createComponent(Host) + fixture.detectChanges() + const { mutation1, mutation2, isMutating } = fixture.componentInstance expect(isMutating()).toBe(0) mutation1.mutate() mutation2.mutate() - expect(isMutating()).toBe(0) + expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(0) expect(isMutating()).toBe(1) await vi.advanceTimersByTimeAsync(51) diff --git a/packages/angular-query/src/__tests__/pending-tasks-ssr.test.ts b/packages/angular-query/src/__tests__/pending-tasks-ssr.test.ts new file mode 100644 index 00000000000..30db5a70f09 --- /dev/null +++ b/packages/angular-query/src/__tests__/pending-tasks-ssr.test.ts @@ -0,0 +1,128 @@ +import { + ChangeDetectionStrategy, + Component, + destroyPlatform, + provideZonelessChangeDetection, +} from '@angular/core' +import { bootstrapApplication } from '@angular/platform-browser' +import { + provideServerRendering, + renderApplication, +} from '@angular/platform-server' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { sleep } from '@tanstack/query-test-utils' +import { + QueryClient, + injectQueries, + injectQuery, + provideTanStackQuery, +} from '..' + +describe('PendingTasks SSR', () => { + beforeEach(() => { + destroyPlatform() + vi.useFakeTimers() + }) + + afterEach(() => { + vi.useRealTimers() + }) + + @Component({ + selector: 'app-root', + template: '{{ query.data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestInjectQueryComponent { + query = injectQuery(() => ({ + queryKey: ['ssr-test'], + queryFn: async () => { + await sleep(1000) + return 'data-fetched-on-ssr' + }, + })) + } + + @Component({ + selector: 'app-queries-root', + template: '{{ queries()[0].data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestInjectQueriesComponent { + queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['ssr-queries-test'], + queryFn: async () => { + await sleep(1000) + return 'queries-data-fetched-on-ssr' + }, + }, + ], + })) + } + + it('should wait for stability of injectQuery', async () => { + const htmlPromise = renderApplication( + (context) => + bootstrapApplication( + TestInjectQueryComponent, + { + providers: [ + provideServerRendering(), + provideZonelessChangeDetection(), + provideTanStackQuery( + () => + new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }), + ), + ], + }, + context, + ), + { + url: '/', + document: + '', + }, + ) + + await vi.runAllTimersAsync() + const html = await htmlPromise + + expect(html).toContain('data-fetched-on-ssr') + }) + + it('should wait for stability of injectQueries', async () => { + const htmlPromise = renderApplication( + (context) => + bootstrapApplication( + TestInjectQueriesComponent, + { + providers: [ + provideServerRendering(), + provideZonelessChangeDetection(), + provideTanStackQuery( + () => + new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }), + ), + ], + }, + context, + ), + { + url: '/', + document: + '', + }, + ) + + await vi.runAllTimersAsync() + const html = await htmlPromise + + expect(html).toContain('queries-data-fetched-on-ssr') + }) +}) diff --git a/packages/angular-query/src/__tests__/pending-tasks-ssr.zoneful.test.ts b/packages/angular-query/src/__tests__/pending-tasks-ssr.zoneful.test.ts new file mode 100644 index 00000000000..a18632811d5 --- /dev/null +++ b/packages/angular-query/src/__tests__/pending-tasks-ssr.zoneful.test.ts @@ -0,0 +1,155 @@ +import { + ChangeDetectionStrategy, + Component, + destroyPlatform, + provideZoneChangeDetection, + signal, +} from '@angular/core' +import { bootstrapApplication } from '@angular/platform-browser' +import { + provideServerRendering, + renderApplication, +} from '@angular/platform-server' +import { afterEach, describe, expect, it } from 'vitest' +import { + QueryClient, + injectQueries, + injectQuery, + provideTanStackQuery, +} from '..' +import type { ApplicationRef, Type } from '@angular/core' + +const fetchData = () => + new Promise((resolve) => { + setTimeout(() => resolve('fetched-on-server'), 20) + }) + +@Component({ + selector: 'app-root', + template: '{{ query.data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class CachedQueryComponent { + readonly query = injectQuery(() => ({ + queryKey: ['cached'], + initialData: 'cached-on-server', + queryFn: fetchData, + staleTime: 30_000, + gcTime: 24 * 60 * 60 * 1000, + })) +} + +@Component({ + selector: 'app-root', + template: '{{ query.data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class FetchingQueryComponent { + readonly query = injectQuery(() => ({ + queryKey: ['fetching'], + queryFn: fetchData, + staleTime: 30_000, + gcTime: 24 * 60 * 60 * 1000, + })) +} + +@Component({ + selector: 'app-root', + template: '{{ queries()[0].data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class FetchingQueriesComponent { + readonly queries = injectQueries(() => ({ + queries: [ + { + queryKey: ['parallel'], + queryFn: fetchData, + staleTime: 30_000, + gcTime: 24 * 60 * 60 * 1000, + }, + ], + })) +} + +@Component({ + selector: 'app-root', + template: '{{ query.data() }}', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +class ReactiveQueryComponent { + readonly key = signal('first') + readonly query = injectQuery(() => ({ + queryKey: [this.key()], + initialData: this.key(), + queryFn: fetchData, + staleTime: 30_000, + gcTime: 24 * 60 * 60 * 1000, + })) + + ngOnInit() { + setTimeout(() => this.key.set('second'), 20) + } +} + +async function render(component: Type) { + const client = new QueryClient() + let app: ApplicationRef | undefined + let deadline: ReturnType | undefined + try { + return await Promise.race([ + renderApplication( + async (context) => { + app = await bootstrapApplication( + component, + { + providers: [ + provideServerRendering(), + provideZoneChangeDetection(), + provideTanStackQuery(() => client), + ], + }, + context, + ) + return app + }, + { + url: '/', + document: '', + }, + ), + new Promise((_, reject) => { + deadline = setTimeout( + () => + reject(new Error('SSR did not become stable within one second')), + 1000, + ) + }), + ]) + } finally { + clearTimeout(deadline) + if (app && !app.destroyed) app.destroy() + client.clear() + } +} + +describe('SSR with real Zone.js timers', () => { + afterEach(() => destroyPlatform()) + + it('serializes cached data without waiting for stale or GC timers', async () => { + expect(await render(CachedQueryComponent)).toContain('cached-on-server') + }) + + it('serializes reactive query changes without waiting for background timers', async () => { + expect(await render(ReactiveQueryComponent)).toContain('second') + }) + + it('waits for a query fetch, but not its background timers', async () => { + expect(await render(FetchingQueryComponent)).toContain('fetched-on-server') + }) + + it('waits for parallel query fetches, but not their background timers', async () => { + expect(await render(FetchingQueriesComponent)).toContain( + 'fetched-on-server', + ) + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts b/packages/angular-query/src/__tests__/pending-tasks.test.ts similarity index 62% rename from packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts rename to packages/angular-query/src/__tests__/pending-tasks.test.ts index c5a1f58d81f..74f8e1b5f09 100644 --- a/packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts +++ b/packages/angular-query/src/__tests__/pending-tasks.test.ts @@ -1,7 +1,7 @@ import { ApplicationRef, + ChangeDetectionStrategy, Component, - provideZonelessChangeDetection, } from '@angular/core' import { TestBed } from '@angular/core/testing' import { HttpClient, provideHttpClient } from '@angular/common/http' @@ -10,15 +10,16 @@ import { provideHttpClientTesting, } from '@angular/common/http/testing' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' -import { queryKey, sleep } from '@tanstack/query-test-utils' +import { sleep } from '@tanstack/query-test-utils' import { lastValueFrom } from 'rxjs' import { QueryClient, injectMutation, injectQuery, onlineManager, - provideTanStackQuery, + skipToken, } from '..' +import { flushQueryUpdates, setupTanStackQueryTestBed } from './test-utils' describe('PendingTasks Integration', () => { let queryClient: QueryClient @@ -37,12 +38,7 @@ describe('PendingTasks Integration', () => { }, }) - TestBed.configureTestingModule({ - providers: [ - provideZonelessChangeDetection(), - provideTanStackQuery(queryClient), - ], - }) + setupTanStackQueryTestBed(queryClient) }) afterEach(() => { @@ -51,17 +47,58 @@ describe('PendingTasks Integration', () => { vi.useRealTimers() }) + it.each([false, true])( + 'keeps all mutation invocations pending (reset=%s)', + async (reset) => { + vi.useRealTimers() + const resolve = new Map void>() + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: (id: string) => + new Promise((done) => resolve.set(id, done)), + })), + ) + const first = mutation.mutateAsync('first') + const second = mutation.mutateAsync('second') + await Promise.resolve() + if (reset) mutation.reset() + let stable = false + const stability = TestBed.inject(ApplicationRef) + .whenStable() + .then(() => { + stable = true + }) + resolve.get('second')!('second') + await second + await Promise.resolve() + expect(stable).toBe(false) + expect(queryClient.isMutating()).toBe(1) + resolve.get('first')!('first') + await first + await stability + expect(stable).toBe(true) + }, + ) + describe('Synchronous Resolution', () => { it('should handle synchronous queryFn with whenStable()', async () => { const app = TestBed.inject(ApplicationRef) - const key = queryKey() - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, - queryFn: () => 'instant-data', // Resolves synchronously - })), - ) + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['sync'], + queryFn: () => 'instant-data', + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query // Should start as pending even with synchronous data expect(query.status()).toBe('pending') @@ -73,7 +110,6 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(10) await stablePromise - // Should work correctly even though queryFn was synchronous expect(query.status()).toBe('success') expect(query.data()).toBe('instant-data') }) @@ -81,13 +117,12 @@ describe('PendingTasks Integration', () => { it('should handle synchronous error with whenStable()', async () => { const app = TestBed.inject(ApplicationRef) - const key = queryKey() const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, + queryKey: ['sync-error'], queryFn: () => { throw new Error('instant-error') - }, // Throws synchronously + }, })), ) @@ -142,7 +177,6 @@ describe('PendingTasks Integration', () => { ) mutation.mutate() - TestBed.tick() const stablePromise = app.whenStable() @@ -153,11 +187,95 @@ describe('PendingTasks Integration', () => { expect(mutation.isError()).toBe(true) expect(mutation.error()).toEqual(new Error('sync-mutation-error')) }) + + it('should keep whenStable pending after mutate starts', async () => { + const app = TestBed.inject(ApplicationRef) + + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: async () => { + await sleep(20) + return 'done' + }, + })), + ) + + mutation.mutate() + + let stableResolved = false + const stablePromise = app.whenStable().then(() => { + stableResolved = true + }) + + await Promise.resolve() + expect(stableResolved).toBe(false) + + await vi.advanceTimersByTimeAsync(21) + await vi.advanceTimersByTimeAsync(0) + await stablePromise + expect(mutation.isSuccess()).toBe(true) + expect(mutation.data()).toBe('done') + }) }) describe('Race Conditions', () => { + it('should not register a pending task while query is disabled by enabled', async () => { + const queryFn = vi.fn(async () => { + await sleep(10) + return 'disabled-data' + }) + + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['disabled-enabled'], + queryFn, + enabled: false, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + await Promise.resolve() + + expect(query.status()).toBe('pending') + expect(query.fetchStatus()).toBe('idle') + expect(query.isPending()).toBe(true) + expect(queryFn).not.toHaveBeenCalled() + expect(fixture.isStable()).toBe(true) + }) + + it('should not register a pending task while query is disabled by skipToken', async () => { + @Component({ + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query = injectQuery(() => ({ + queryKey: ['disabled-skip-token'], + queryFn: skipToken, + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query + + await Promise.resolve() + + expect(query.status()).toBe('pending') + expect(query.fetchStatus()).toBe('idle') + expect(query.isPending()).toBe(true) + expect(query.isEnabled()).toBe(false) + expect(fixture.isStable()).toBe(true) + }) + it('should handle query that completes during initial subscription', async () => { - const key = queryKey() const app = TestBed.inject(ApplicationRef) let resolveQuery: (value: string) => void @@ -167,7 +285,7 @@ describe('PendingTasks Integration', () => { const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, + queryKey: ['race-condition'], queryFn: () => queryPromise, })), ) @@ -186,19 +304,27 @@ describe('PendingTasks Integration', () => { it('should handle rapid refetches without task leaks', async () => { const app = TestBed.inject(ApplicationRef) - let callCount = 0 - const key = queryKey() - const query = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key, + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + callCount = 0 + query = injectQuery(() => ({ + queryKey: ['rapid-refetch'], queryFn: async () => { - callCount++ + this.callCount++ await sleep(10) - return `data-${callCount}` + return `data-${this.callCount}` }, - })), - ) + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const query = fixture.componentInstance.query // Trigger multiple rapid refetches query.refetch() @@ -213,14 +339,61 @@ describe('PendingTasks Integration', () => { expect(query.data()).toMatch(/^data-\d+$/) }) + it('should keep PendingTasks active when query starts offline (never reaches fetching)', async () => { + const app = TestBed.inject(ApplicationRef) + + onlineManager.setOnline(false) + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['start-offline'], + networkMode: 'online', // Default: won't fetch while offline + queryFn: async () => { + await sleep(10) + return 'online-data' + }, + })), + ) + + // Allow query to initialize + await Promise.resolve() + await flushQueryUpdates() + + // Query should initialize directly to 'paused' (never goes through 'fetching') + expect(query.status()).toBe('pending') + expect(query.fetchStatus()).toBe('paused') + + const stablePromise = app.whenStable() + let stableResolved = false + void stablePromise.then(() => { + stableResolved = true + }) + + await Promise.resolve() + + // PendingTasks should block stability even though we never hit 'fetching' + expect(stableResolved).toBe(false) + + // Bring the app back online so the query can fetch + onlineManager.setOnline(true) + + await vi.advanceTimersByTimeAsync(20) + await Promise.resolve() + + await stablePromise + + expect(stableResolved).toBe(true) + expect(query.status()).toBe('success') + expect(query.data()).toBe('online-data') + }) + it('should keep PendingTasks active while query retry is paused offline', async () => { const app = TestBed.inject(ApplicationRef) let attempt = 0 - const key = queryKey() const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, + queryKey: ['paused-offline'], retry: 1, retryDelay: 50, // Longer delay to ensure we can go offline before retry queryFn: async () => { @@ -235,7 +408,7 @@ describe('PendingTasks Integration', () => { ) // Allow the initial attempt to start and fail - await vi.advanceTimersByTimeAsync(0) + await flushQueryUpdates() await Promise.resolve() // Wait for the first attempt to complete and start retry delay @@ -282,84 +455,97 @@ describe('PendingTasks Integration', () => { }) describe('Component Destruction', () => { - @Component({ - template: '', - }) - class TestComponent { - query = injectQuery(() => ({ - queryKey: ['component-query'], - queryFn: () => sleep(100).then(() => 'component-data'), - })) - - mutation = injectMutation(() => ({ - mutationFn: (data: string) => - sleep(100).then(() => `processed: ${data}`), - })) - } - - it('should cleanup pending tasks when component with active query is destroyed', async () => { - const app = TestBed.inject(ApplicationRef) - const fixture = TestBed.createComponent(TestComponent) + it('becomes stable after destruction before an unresolved query completes', async () => { + vi.useRealTimers() + let resolveQuery!: (value: string) => void + const queryPromise = new Promise((resolve) => { + resolveQuery = resolve + }) - // Start the query - expect(fixture.componentInstance.query.status()).toBe('pending') + @Component({ template: '' }) + class UnresolvedQueryComponent { + query = injectQuery(() => ({ + queryKey: ['unresolved-component-query'], + queryFn: () => queryPromise, + })) + } - // Destroy component while query is running - fixture.destroy() + const fixture = TestBed.createComponent(UnresolvedQueryComponent) + fixture.detectChanges() + expect(fixture.isStable()).toBe(false) - // Angular should become stable even though component was destroyed - const stablePromise = app.whenStable() - await vi.advanceTimersByTimeAsync(150) + fixture.destroy() + await fixture.whenStable() + expect(fixture.isStable()).toBe(true) - await expect(stablePromise).resolves.toEqual(undefined) + resolveQuery('eventual data') + await Promise.resolve() }) - it('should cleanup pending tasks when component with active mutation is destroyed', async () => { - const app = TestBed.inject(ApplicationRef) - const fixture = TestBed.createComponent(TestComponent) + it('becomes stable after destruction before an unresolved mutation completes', async () => { + vi.useRealTimers() + let resolveMutation!: (value: string) => void + const mutationPromise = new Promise((resolve) => { + resolveMutation = resolve + }) - fixture.componentInstance.mutation.mutate('test') + @Component({ template: '' }) + class UnresolvedMutationComponent { + mutation = injectMutation(() => ({ + mutationFn: () => mutationPromise, + })) + } - // Destroy component while mutation is running - fixture.destroy() + const fixture = TestBed.createComponent(UnresolvedMutationComponent) + fixture.detectChanges() + fixture.componentInstance.mutation.mutate() + expect(fixture.isStable()).toBe(false) - // Angular should become stable even though component was destroyed - const stablePromise = app.whenStable() - await vi.advanceTimersByTimeAsync(150) + fixture.destroy() + await fixture.whenStable() + expect(fixture.isStable()).toBe(true) - await expect(stablePromise).resolves.toEqual(undefined) + resolveMutation('eventual data') + await Promise.resolve() }) }) describe('Concurrent Operations', () => { it('should handle multiple queries running simultaneously', async () => { - const key1 = queryKey() - const key2 = queryKey() - const key3 = queryKey() const app = TestBed.inject(ApplicationRef) - const query1 = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key1, - queryFn: () => sleep(30).then(() => 'data-1'), - })), - ) + @Component({ + selector: 'app-test', + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + query1 = injectQuery(() => ({ + queryKey: ['concurrent-1'], + queryFn: async () => { + await sleep(30) + return 'data-1' + }, + })) - const query2 = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key2, - queryFn: () => sleep(50).then(() => 'data-2'), - })), - ) + query2 = injectQuery(() => ({ + queryKey: ['concurrent-2'], + queryFn: async () => { + await sleep(50) + return 'data-2' + }, + })) - const query3 = TestBed.runInInjectionContext(() => - injectQuery(() => ({ - queryKey: key3, - queryFn: () => 'instant-data', // Synchronous - })), - ) + query3 = injectQuery(() => ({ + queryKey: ['concurrent-3'], + queryFn: () => 'instant-data', + })) + } + + const fixture = TestBed.createComponent(TestComponent) + fixture.detectChanges() + const { query1, query2, query3 } = fixture.componentInstance - // All queries should start expect(query1.status()).toBe('pending') expect(query2.status()).toBe('pending') expect(query3.status()).toBe('pending') @@ -368,7 +554,6 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(60) await stablePromise - // All queries should be complete expect(query1.status()).toBe('success') expect(query1.data()).toBe('data-1') expect(query2.status()).toBe('success') @@ -382,15 +567,19 @@ describe('PendingTasks Integration', () => { const mutation1 = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationFn: (data: string) => - sleep(30).then(() => `processed-1: ${data}`), + mutationFn: async (data: string) => { + await sleep(30) + return `processed-1: ${data}` + }, })), ) const mutation2 = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationFn: (data: string) => - sleep(50).then(() => `processed-2: ${data}`), + mutationFn: async (data: string) => { + await sleep(50) + return `processed-2: ${data}` + }, })), ) @@ -403,7 +592,6 @@ describe('PendingTasks Integration', () => { })), ) - // Start all mutations mutation1.mutate('test1') mutation2.mutate('test2') mutation3.mutate('test3') @@ -414,7 +602,6 @@ describe('PendingTasks Integration', () => { await vi.advanceTimersByTimeAsync(60) await stablePromise - // All mutations should be complete expect(mutation1.isSuccess()).toBe(true) expect(mutation1.data()).toBe('processed-1: test1') expect(mutation2.isSuccess()).toBe(true) @@ -426,29 +613,31 @@ describe('PendingTasks Integration', () => { it('should handle mixed queries and mutations', async () => { const app = TestBed.inject(ApplicationRef) - const key = queryKey() const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(40).then(() => 'query-data'), + queryKey: ['mixed-query'], + queryFn: async () => { + await sleep(40) + return 'query-data' + }, })), ) const mutation = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationFn: (data: string) => - sleep(60).then(() => `mutation: ${data}`), + mutationFn: async (data: string) => { + await sleep(60) + return `mutation: ${data}` + }, })), ) - // Start both operations mutation.mutate('test') const stablePromise = app.whenStable() await vi.advanceTimersByTimeAsync(70) await stablePromise - // Both should be complete expect(query.status()).toBe('success') expect(query.data()).toBe('query-data') expect(mutation.isSuccess()).toBe(true) @@ -458,7 +647,7 @@ describe('PendingTasks Integration', () => { describe('HttpClient Integration', () => { beforeEach(() => { - TestBed.configureTestingModule({ + setupTanStackQueryTestBed(queryClient, { providers: [provideHttpClient(), provideHttpClientTesting()], }) }) @@ -468,12 +657,9 @@ describe('PendingTasks Integration', () => { const httpClient = TestBed.inject(HttpClient) const httpTestingController = TestBed.inject(HttpTestingController) - const key1 = queryKey() - const key2 = queryKey() - const query1 = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key1, + queryKey: ['http-1'], queryFn: () => lastValueFrom(httpClient.get<{ id: number }>('/api/1')), })), @@ -481,13 +667,12 @@ describe('PendingTasks Integration', () => { const query2 = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key2, + queryKey: ['http-2'], queryFn: () => lastValueFrom(httpClient.get<{ id: number }>('/api/2')), })), ) - // Schedule HTTP responses setTimeout(() => { const req1 = httpTestingController.expectOne('/api/1') req1.flush({ id: 1 }) @@ -513,10 +698,9 @@ describe('PendingTasks Integration', () => { const httpClient = TestBed.inject(HttpClient) const httpTestingController = TestBed.inject(HttpTestingController) - const key = queryKey() const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, + queryKey: ['http-cancel'], queryFn: () => lastValueFrom(httpClient.get<{ data: string }>('/api/cancel')), })), @@ -543,19 +727,21 @@ describe('PendingTasks Integration', () => { describe('Edge Cases', () => { it('should handle query cancellation mid-flight', async () => { - const key = queryKey() const app = TestBed.inject(ApplicationRef) const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, - queryFn: () => sleep(100).then(() => 'data'), + queryKey: ['cancel-test'], + queryFn: async () => { + await sleep(100) + return 'data' + }, })), ) // Cancel the query after a short delay setTimeout(() => { - queryClient.cancelQueries({ queryKey: key }) + queryClient.cancelQueries({ queryKey: ['cancel-test'] }) }, 20) // Advance to the cancellation point @@ -576,10 +762,9 @@ describe('PendingTasks Integration', () => { const app = TestBed.inject(ApplicationRef) let attemptCount = 0 - const key = queryKey() const query = TestBed.runInInjectionContext(() => injectQuery(() => ({ - queryKey: key, + queryKey: ['retry-test'], retry: 2, retryDelay: 10, queryFn: async () => { @@ -603,13 +788,16 @@ describe('PendingTasks Integration', () => { it('should handle mutation with optimistic updates', async () => { const app = TestBed.inject(ApplicationRef) - const testQueryKey = queryKey() + const testQueryKey = ['optimistic-test'] queryClient.setQueryData(testQueryKey, 'initial-data') const mutation = TestBed.runInInjectionContext(() => injectMutation(() => ({ - mutationFn: (newData: string) => sleep(50).then(() => newData), + mutationFn: async (newData: string) => { + await sleep(50) + return newData + }, onMutate: async (newData) => { // Optimistic update const previousData = queryClient.getQueryData(testQueryKey) diff --git a/packages/angular-query/src/__tests__/provide-tanstack-query.test-d.ts b/packages/angular-query/src/__tests__/provide-tanstack-query.test-d.ts new file mode 100644 index 00000000000..814f624e4b8 --- /dev/null +++ b/packages/angular-query/src/__tests__/provide-tanstack-query.test-d.ts @@ -0,0 +1,44 @@ +import { describe, expectTypeOf, it } from 'vitest' +import { + QueryClient, + provideTanStackQuery, + withHydrationKey, + withNoQueryHydration, +} from '..' +import type { QueryFeature } from '..' +import type * as PublicApi from '..' +import type * as InternalApi from '../internal' + +describe('provideTanStackQuery', () => { + it('keeps integration helpers out of the public entry point', () => { + type InternalHelpers = + | 'queryFeature' + | 'getQueryFeatureProviders' + | 'provideIsRestoring' + expectTypeOf>().toBeNever() + expectTypeOf< + Extract + >().toEqualTypeOf() + }) + + it('accepts built-in opaque query features', () => { + expectTypeOf(withHydrationKey('cache')).toEqualTypeOf() + expectTypeOf(withNoQueryHydration()).toEqualTypeOf() + + provideTanStackQuery( + () => new QueryClient(), + withHydrationKey('cache'), + withNoQueryHydration(), + ) + }) + + it('rejects structurally fabricated features', () => { + expectTypeOf(withHydrationKey('cache')).toEqualTypeOf() + + provideTanStackQuery( + () => new QueryClient(), + // @ts-expect-error QueryFeature values can only be created by package features + { ɵkind: 'Hydration', ɵproviders: {} }, + ) + }) +}) diff --git a/packages/angular-query/src/__tests__/provide-tanstack-query.test.ts b/packages/angular-query/src/__tests__/provide-tanstack-query.test.ts new file mode 100644 index 00000000000..ae5fa7a7f9f --- /dev/null +++ b/packages/angular-query/src/__tests__/provide-tanstack-query.test.ts @@ -0,0 +1,79 @@ +import { TestBed } from '@angular/core/testing' +import { describe, expect, it } from 'vitest' +import { InjectionToken, PLATFORM_ID, inject } from '@angular/core' +import { QueryClient } from '@tanstack/query-core' +import { provideTanStackQuery } from '../providers' +import { provideAngularQueryChangeDetection } from './test-utils' + +describe('provideTanStackQuery', () => { + it('should provide a QueryClient instance using a factory', () => { + const queryClient = new QueryClient() + + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + + const providedQueryClient = TestBed.inject(QueryClient) + expect(providedQueryClient).toBe(queryClient) + }) + + it('resolves an existing QueryClient through a factory in the injection context', () => { + const queryClient = new QueryClient() + const CUSTOM_QUERY_CLIENT = new InjectionToken('', { + factory: () => queryClient, + }) + + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => inject(CUSTOM_QUERY_CLIENT)), + ], + }) + + const providedQueryClient = TestBed.inject(QueryClient) + expect(providedQueryClient).toBe(queryClient) + }) +}) + +describe('server client defaults', () => { + it('uses server defaults without changing an unrelated client', () => { + const unrelated = new QueryClient() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + { provide: PLATFORM_ID, useValue: 'server' }, + provideTanStackQuery(() => new QueryClient()), + ], + }) + expect(TestBed.inject(QueryClient).getDefaultOptions()).toEqual({ + queries: { gcTime: Infinity, retry: false }, + mutations: { gcTime: Infinity }, + }) + expect(unrelated.getDefaultOptions()).toEqual({}) + }) + + it('preserves explicit server client defaults', () => { + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + { provide: PLATFORM_ID, useValue: 'server' }, + provideTanStackQuery( + () => + new QueryClient({ + defaultOptions: { + queries: { gcTime: 1000, retry: 1, staleTime: 30000 }, + mutations: { gcTime: 2000 }, + }, + }), + ), + ], + }) + expect(TestBed.inject(QueryClient).getDefaultOptions()).toEqual({ + queries: { gcTime: 1000, retry: 1, staleTime: 30000 }, + mutations: { gcTime: 2000 }, + }) + }) +}) diff --git a/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts b/packages/angular-query/src/__tests__/query-options.test-d.ts similarity index 100% rename from packages/angular-query-experimental/src/__tests__/query-options.test-d.ts rename to packages/angular-query/src/__tests__/query-options.test-d.ts index 830e8df805b..50d81d51889 100644 --- a/packages/angular-query-experimental/src/__tests__/query-options.test-d.ts +++ b/packages/angular-query/src/__tests__/query-options.test-d.ts @@ -174,19 +174,6 @@ it('should return the proper type when passed to getQueryData', () => { expectTypeOf(data).toEqualTypeOf() }) -it('should return the proper type when passed to getQueryState', () => { - const key = queryKey() - const { queryKey: tagged } = queryOptions({ - queryKey: key, - queryFn: () => Promise.resolve(5), - }) - - const queryClient = new QueryClient() - const state = queryClient.getQueryState(tagged) - - expectTypeOf(state?.data).toEqualTypeOf() -}) - it('should properly type updaterFn when passed to setQueryData', () => { const key = queryKey() const { queryKey: tagged } = queryOptions({ @@ -222,6 +209,19 @@ it('should properly type value when passed to setQueryData', () => { expectTypeOf(data).toEqualTypeOf() }) +it('should return the proper type when passed to getQueryState', () => { + const key = queryKey() + const { queryKey: tagged } = queryOptions({ + queryKey: key, + queryFn: () => Promise.resolve(5), + }) + + const queryClient = new QueryClient() + const state = queryClient.getQueryState(tagged) + + expectTypeOf(state?.data).toEqualTypeOf() +}) + it('should infer even if there is a conditional skipToken', () => { const key = queryKey() const options = queryOptions({ diff --git a/packages/angular-query-experimental/src/__tests__/query-options.test.ts b/packages/angular-query/src/__tests__/query-options.test.ts similarity index 58% rename from packages/angular-query-experimental/src/__tests__/query-options.test.ts rename to packages/angular-query/src/__tests__/query-options.test.ts index 72a53326326..40d5704be74 100644 --- a/packages/angular-query-experimental/src/__tests__/query-options.test.ts +++ b/packages/angular-query/src/__tests__/query-options.test.ts @@ -11,4 +11,14 @@ describe('queryOptions', () => { expect(queryOptions(object)).toBe(object) }) + + it('keeps notifyOnChangeProps adapter-owned', () => { + const options: CreateQueryOptions = { + queryKey: ['key'], + // @ts-expect-error Angular Query always subscribes to every result change. + notifyOnChangeProps: ['data'], + } + + expect(options).toHaveProperty('notifyOnChangeProps', ['data']) + }) }) diff --git a/packages/angular-query/src/__tests__/signal-proxy.test.ts b/packages/angular-query/src/__tests__/signal-proxy.test.ts new file mode 100644 index 00000000000..58b3b40cc0b --- /dev/null +++ b/packages/angular-query/src/__tests__/signal-proxy.test.ts @@ -0,0 +1,77 @@ +import { + ChangeDetectionStrategy, + Component, + computed, + input, + inputBinding, + isSignal, + signal, +} from '@angular/core' +import { beforeEach, describe, expect, it } from 'vitest' +import { TestBed } from '@angular/core/testing' +import { signalProxy } from '../utils/signal-proxy' +import { provideAngularQueryChangeDetection } from './test-utils' + +describe('signalProxy', () => { + it('only exposes declared fields with ordinary object semantics', async () => { + const source = signal({ value: 'data', falsy: false, zero: 0 }) + const proxy = signalProxy(source, ['value', 'falsy', 'zero']) + + expect(Object.keys(proxy)).toEqual(['value', 'falsy', 'zero']) + expect('value' in proxy).toBe(true) + expect('falsy' in proxy).toBe(true) + expect('zero' in proxy).toBe(true) + expect('missing' in proxy).toBe(false) + expect(Object.hasOwn(proxy, 'missing')).toBe(false) + expect(Reflect.get(proxy, 'then')).toBeUndefined() + expect(await Promise.resolve(proxy)).toBe(proxy) + }) + + it('should have computed fields', () => { + const source = signal({ value: 'data' }) + const proxy = signalProxy(source, ['value']) + expect(proxy.value()).toBe('data') + expect(isSignal(proxy.value)).toBe(true) + }) + + describe('in component fixture', () => { + @Component({ + selector: 'app-test', + standalone: true, + template: '{{ proxy.number() }}', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class TestComponent { + number = input.required() + obj = computed(() => ({ number: this.number() })) + proxy = signalProxy(this.obj, ['number']) + shortNumber = this.proxy.number + } + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideAngularQueryChangeDetection()], + }) + }) + + it('should retain field signals and update aliases of required inputs', () => { + const number = signal(0) + const fixture = TestBed.createComponent(TestComponent, { + bindings: [inputBinding('number', number.asReadonly())], + }) + fixture.detectChanges() + const instance = fixture.componentInstance + + expect(isSignal(instance.proxy.number)).toBe(true) + expect(instance.shortNumber).toBe(instance.proxy.number) + expect(instance.shortNumber()).toBe(0) + + number.set(1) + fixture.detectChanges() + + expect(instance.shortNumber).toBe(instance.proxy.number) + expect(instance.shortNumber()).toBe(1) + expect(fixture.nativeElement.textContent).toBe('1') + }) + }) +}) diff --git a/packages/angular-query/src/__tests__/test-utils.ts b/packages/angular-query/src/__tests__/test-utils.ts new file mode 100644 index 00000000000..cc7151fc8de --- /dev/null +++ b/packages/angular-query/src/__tests__/test-utils.ts @@ -0,0 +1,78 @@ +// cspell:ignore ZONEFUL zoneful +import { + isSignal, + provideZoneChangeDetection, + provideZonelessChangeDetection, + untracked, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { expect, vi } from 'vitest' +import { provideTanStackQuery } from '..' +import type { QueryClient } from '@tanstack/query-core' +import type { EnvironmentProviders, Provider, Signal } from '@angular/core' + +/** + * Use the same change-detection mode as the test runner. The default runner + * is zoneless; the zoneful runner sets ANGULAR_QUERY_ZONEFUL before loading + * tests and installs the real Zone.js testing patches. + */ +export function provideAngularQueryChangeDetection(): EnvironmentProviders { + return process.env.ANGULAR_QUERY_ZONEFUL === 'true' + ? provideZoneChangeDetection() + : provideZonelessChangeDetection() +} + +function evaluateSignals>( + obj: T, +): { [K in keyof T]: ReturnType } { + const result: Partial<{ [K in keyof T]: ReturnType }> = {} + + untracked(() => { + for (const key in obj) { + if ( + Object.prototype.hasOwnProperty.call(obj, key) && + isSignal(obj[key]) + ) { + const func = obj[key] + result[key] = func() + } + } + }) + + return result as { [K in keyof T]: ReturnType } +} + +export const expectSignals = >( + obj: T, + expected: Partial<{ + [K in keyof T]: T[K] extends Signal ? ReturnType : never + }>, +): void => { + expect(evaluateSignals(obj)).toMatchObject(expected) +} + +/** + * Reset Angular's TestBed and configure the standard TanStack Query providers for tests. + * Pass additional providers (including EnvironmentProviders) via the options argument. + */ +export function setupTanStackQueryTestBed( + queryClient: QueryClient, + options: { providers?: Array } = {}, +) { + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => queryClient), + ...(options.providers ?? []), + ], + }) +} + +/** + * Advances zero-delay timers and their promise continuations in fake-timer tests. + * Angular rendering may require a subsequent change-detection pass. + */ +export async function flushQueryUpdates() { + await vi.advanceTimersByTimeAsync(0) +} diff --git a/packages/angular-query/src/__tests__/upstream-issues.test.ts b/packages/angular-query/src/__tests__/upstream-issues.test.ts new file mode 100644 index 00000000000..39b33d520ea --- /dev/null +++ b/packages/angular-query/src/__tests__/upstream-issues.test.ts @@ -0,0 +1,289 @@ +import { + ApplicationRef, + ChangeDetectionStrategy, + Component, + VERSION, + effect, + input, + signal, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + QueryClient, + injectMutation, + injectQuery, + provideTanStackQuery, +} from '..' +import { provideAngularQueryChangeDetection } from './test-utils' + +// Reproductions transcribed from TanStack/query issues. Use real promises and +// Angular stability, without notification-scheduler overrides or timer flushes. +describe('upstream Angular issue reproductions', () => { + let client: QueryClient + + beforeEach(() => { + vi.useRealTimers() + if ( + process.env.ANGULAR_ISSUE_VERSION && + VERSION.full !== process.env.ANGULAR_ISSUE_VERSION + ) { + throw new Error(`Unexpected Angular runtime: ${VERSION.full}`) + } + client = new QueryClient({ + defaultOptions: { queries: { retry: false, gcTime: Infinity } }, + }) + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + provideTanStackQuery(() => client), + ], + }) + }) + + afterEach(() => { + TestBed.resetTestingModule() + client.clear() + }) + + it('#9910: a template signal read does not prevent whenStable from seeing success', async () => { + @Component({ template: '{{ query.isSuccess() }}' }) + class App { + query = injectQuery(() => ({ + queryKey: ['9910'], + queryFn: () => Promise.resolve([1, 2, 3, 4, 5]), + })) + } + const fixture = TestBed.createComponent(App) + fixture.detectChanges() + await fixture.whenStable() + fixture.detectChanges() + expect(fixture.componentInstance.query.isSuccess()).toBe(true) + expect(fixture.componentInstance.query.data()).toEqual([1, 2, 3, 4, 5]) + expect(fixture.nativeElement.textContent).toBe('true') + }) + + it('#10046: a class-field data alias is populated after whenStable', async () => { + @Component({ template: '{{ data() }}' }) + class App { + query = injectQuery(() => ({ + queryKey: ['10046'], + queryFn: () => Promise.resolve('loaded'), + })) + data = this.query.data + } + const fixture = TestBed.createComponent(App) + fixture.detectChanges() + await fixture.whenStable() + fixture.detectChanges() + expect(fixture.componentInstance.data()).toBe('loaded') + expect(fixture.nativeElement.textContent).toBe('loaded') + }) + + it.each(['TestBed', 'Testing Library'] as const)( + '#9981: an isSuccess effect updates component state using %s', + async (runner) => { + @Component({ + template: '{{ value }}', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class App { + value = 42 + query = injectQuery(() => ({ + queryKey: ['9981'], + queryFn: () => Promise.resolve([30, 40, 50]), + })) + constructor() { + effect(() => { + if (this.query.isSuccess()) this.value = 9999 + }) + } + } + const fixture = + runner === 'TestBed' + ? TestBed.createComponent(App) + : (await (await import('@testing-library/angular')).render(App)) + .fixture + fixture.detectChanges() + await fixture.whenStable() + fixture.detectChanges() + expect(fixture.componentInstance.value).toBe(9999) + }, + ) + + it('#7488: taking a data alias does not read an unset required input', async () => { + const optionsRead = vi.fn() + @Component({ template: '{{ data() }}' }) + class App { + name = input.required() + data = injectQuery(() => { + optionsRead() + return { + queryKey: ['7488', this.name()], + queryFn: () => Promise.resolve(this.name()), + } + }).data + } + const fixture = TestBed.createComponent(App) + expect(optionsRead).not.toHaveBeenCalled() + fixture.componentRef.setInput('name', 'ready') + fixture.detectChanges() + await fixture.whenStable() + expect(fixture.componentInstance.data()).toBe('ready') + expect(optionsRead).toHaveBeenCalledTimes(1) + }) + + it.each(['constructor', 'ngOnInit', 'ngAfterContentInit'] as const)( + '#9020: mutate immediately exposes pending from %s', + async (phase) => { + let finish!: (value: string) => void + const request = new Promise((resolve) => { + finish = resolve + }) + @Component({ template: '{{ mutation.status() }}' }) + class App { + immediate = '' + mutation = injectMutation(() => ({ mutationFn: () => request })) + constructor() { + if (phase === 'constructor') this.start() + } + ngOnInit() { + if (phase === 'ngOnInit') this.start() + } + ngAfterContentInit() { + if (phase === 'ngAfterContentInit') this.start() + } + start() { + this.mutation.mutate() + this.immediate = this.mutation.status() + } + } + const fixture = TestBed.createComponent(App) + fixture.detectChanges() + expect(fixture.componentInstance.immediate).toBe('pending') + expect(fixture.nativeElement.textContent).toBe('pending') + finish('done') + await fixture.whenStable() + expect(fixture.componentInstance.mutation.data()).toBe('done') + }, + ) + + it('#11176: whenStable waits for a just-triggered fire-and-forget mutation', async () => { + const app = TestBed.inject(ApplicationRef) + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: (value: string) => + new Promise((resolve) => { + setTimeout(() => resolve(value), 5) + }), + })), + ) + TestBed.tick() + mutation.mutate('mutated') + await app.whenStable() + expect(mutation.status()).toBe('success') + expect(mutation.data()).toBe('mutated') + }) + + it.each([false, true])( + '#11176: a newer mutation settling cannot release an older invocation (reset=%s)', + async (reset) => { + let finish!: () => void + const first = new Promise((resolve) => { + finish = resolve + }) + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationFn: (request: Promise) => request, + })), + ) + TestBed.tick() + mutation.mutate(first) + if (reset) mutation.reset() + const second = mutation.mutateAsync(Promise.resolve()) + let stable = false + const settled = TestBed.inject(ApplicationRef) + .whenStable() + .then(() => { + stable = true + }) + await second + expect(stable).toBe(false) + finish() + await settled + expect(stable).toBe(true) + }, + ) + + it('#9735: saving an edit exposes optimistic data in the same turn and render', async () => { + const snapshots: Array = [] + @Component({ + template: '@if (!editing()) { {{ query.data() }} }', + changeDetection: ChangeDetectionStrategy.OnPush, + }) + class App { + editing = signal(true) + query = injectQuery(() => ({ + queryKey: ['9735'], + queryFn: () => Promise.resolve('old title'), + initialData: 'old title', + staleTime: Infinity, + })) + mutation = injectMutation(() => ({ + mutationFn: async (title: string) => title, + onMutate: (title: string) => { + client.setQueryData(['9735'], title) + snapshots.push(this.query.data()) + }, + })) + save() { + this.mutation.mutate('new title') + this.editing.set(false) + snapshots.push(this.query.data()) + } + } + const fixture = TestBed.createComponent(App) + fixture.detectChanges() + fixture.componentInstance.save() + expect(snapshots).toEqual(['new title', 'new title']) + expect(client.getQueryData(['9735'])).toBe('new title') + fixture.detectChanges() + expect(fixture.nativeElement.textContent).toBe('new title') + await fixture.whenStable() + }) + + it('#6567: changing to another cached key updates data immediately', () => { + client.setQueryData(['6567', 'a'], 'A') + client.setQueryData(['6567', 'b'], 'B') + const key = signal('a') + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['6567', key()], + enabled: false, + })), + ) + expect(query.data()).toBe('A') + TestBed.tick() + key.set('b') + expect(query.data()).toBe('B') + }) + + it('#6771: enabling a fresh cached query and immediately refetching settles', async () => { + client.setQueryData(['6771'], 'cached') + const enabled = signal(false) + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['6771'], + enabled: enabled(), + staleTime: Infinity, + queryFn: () => Promise.resolve('updated'), + })), + ) + TestBed.tick() + enabled.set(true) + await query.refetch() + await TestBed.inject(ApplicationRef).whenStable() + expect(query.data()).toBe('updated') + expect(query.isRefetching()).toBe(false) + }) +}) diff --git a/packages/angular-query/src/__tests__/with-hydration.test.ts b/packages/angular-query/src/__tests__/with-hydration.test.ts new file mode 100644 index 00000000000..bcdb30b91ea --- /dev/null +++ b/packages/angular-query/src/__tests__/with-hydration.test.ts @@ -0,0 +1,325 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + Component, + EnvironmentInjector, + PLATFORM_ID, + TransferState, + createEnvironmentInjector, + effect, + inject, + makeStateKey, + provideEnvironmentInitializer, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { render } from '@testing-library/angular' +import { queryKey, sleep } from '@tanstack/query-test-utils' +import { + QueryClient, + dehydrate, + injectQuery, + noop, + provideTanStackQuery, + withHydrationKey, + withNoQueryHydration, +} from '..' +import { INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY } from '../hydration-state-key' +import { provideAngularQueryChangeDetection } from './test-utils' +import type { DehydratedState } from '@tanstack/query-core' + +beforeEach(() => { + vi.useFakeTimers() +}) + +afterEach(() => { + vi.restoreAllMocks() + vi.useRealTimers() +}) + +describe('TransferState hydration (client)', () => { + it('browser hydrates from default key', async () => { + const key = queryKey() + const sourceClient = new QueryClient() + sourceClient.setQueryData(key, 'from-server') + const dehydrated = dehydrate(sourceClient) + const appClient = new QueryClient() + + @Component({ + template: `
{{ state.data() ?? '' }}
`, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'from-client'), + })) + } + + const rendered = await render(Page, { + providers: [ + provideAngularQueryChangeDetection(), + provideEnvironmentInitializer(() => { + inject(TransferState).set( + INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, + dehydrated, + ) + }), + provideTanStackQuery(() => appClient), + ], + }) + + rendered.fixture.detectChanges() + expect(rendered.getByText('from-server')).toBeInTheDocument() + expect( + rendered.fixture.debugElement.injector + .get(TransferState) + .get(INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, null), + ).toBeNull() + }) + + it('browser hydrates from custom key', async () => { + const customKeyName = 'tanstack-test-custom-hydration' + const customKey = makeStateKey(customKeyName) + const key = queryKey() + const sourceClient = new QueryClient() + sourceClient.setQueryData(key, 'from-custom-key') + const dehydrated = dehydrate(sourceClient) + const appClient = new QueryClient() + + @Component({ + template: `
{{ state.data() ?? '' }}
`, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + staleTime: Infinity, + queryFn: () => Promise.resolve('from-client'), + })) + } + + const rendered = await render(Page, { + providers: [ + provideAngularQueryChangeDetection(), + provideEnvironmentInitializer(() => { + inject(TransferState).set(customKey, dehydrated) + }), + provideTanStackQuery(() => appClient, withHydrationKey(customKeyName)), + ], + }) + + rendered.fixture.detectChanges() + expect(rendered.getByText('from-custom-key')).toBeTruthy() + expect( + rendered.fixture.debugElement.injector + .get(TransferState) + .get(customKey, null), + ).toBeNull() + }) + + it('browser does not re-fetch when hydrated state is fresh', async () => { + const key = queryKey() + const sourceClient = new QueryClient() + sourceClient.setQueryData(key, 'cached') + const dehydrated = dehydrate(sourceClient) + const appClient = new QueryClient() + let queryFnCalls = 0 + + @Component({ + template: `
{{ state.data() ?? '' }}
`, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + staleTime: Infinity, + queryFn: () => { + queryFnCalls++ + return sleep(10).then(() => 'should-not-run') + }, + })) + + _ = effect(() => { + void this.state.data() + }) + } + + await render(Page, { + providers: [ + provideAngularQueryChangeDetection(), + provideEnvironmentInitializer(() => { + inject(TransferState).set( + INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, + dehydrated, + ) + }), + provideTanStackQuery(() => appClient), + ], + }) + + await vi.advanceTimersByTimeAsync(100) + expect(queryFnCalls).toBe(0) + }) + + it('withNoQueryHydration disables browser hydration', async () => { + const key = queryKey() + const sourceClient = new QueryClient() + sourceClient.setQueryData(key, 'from-server') + const dehydrated = dehydrate(sourceClient) + const appClient = new QueryClient() + + @Component({ + template: `
{{ state.data() ?? '' }}
`, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: () => sleep(10).then(() => 'from-client'), + })) + } + + const rendered = await render(Page, { + providers: [ + provideAngularQueryChangeDetection(), + provideEnvironmentInitializer(() => { + inject(TransferState).set( + INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, + dehydrated, + ) + }), + provideTanStackQuery(() => appClient, withNoQueryHydration()), + ], + }) + + rendered.fixture.detectChanges() + await vi.advanceTimersByTimeAsync(10) + rendered.fixture.detectChanges() + + expect(rendered.getByText('from-client')).toBeInTheDocument() + expect( + rendered.fixture.debugElement.injector + .get(TransferState) + .get(INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, null), + ).toEqual(dehydrated) + }) +}) + +describe('TransferState dehydration (server)', () => { + function createQueryInjector( + queryClient: QueryClient, + platformId: 'server' | 'browser', + ...features: Array[1]> + ) { + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + { provide: PLATFORM_ID, useValue: platformId }, + ], + }) + + return createEnvironmentInjector( + [provideTanStackQuery(() => queryClient, ...features)], + TestBed.inject(EnvironmentInjector), + ) + } + + it('server serializes dehydrated queries on TransferState.toJson', async () => { + const key = queryKey() + const queryClient = new QueryClient() + const injector = createQueryInjector(queryClient, 'server') + + void queryClient.query({ + queryKey: key, + queryFn: () => Promise.resolve('ssr-data'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(0) + + injector.get(TransferState).toJson() + + const stored = injector + .get(TransferState) + .get(INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, null) + expect(stored).not.toBeNull() + if (!stored) throw new Error('expected dehydrated state') + expect(stored.queries.length).toBe(1) + expect(stored.queries[0]?.queryKey).toEqual(key) + }) + + it('browser platform does not write server dehydration state', async () => { + const key = queryKey() + const queryClient = new QueryClient() + const injector = createQueryInjector(queryClient, 'browser') + + void queryClient.query({ + queryKey: key, + queryFn: () => Promise.resolve('data'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(0) + + injector.get(TransferState).toJson() + + expect( + injector + .get(TransferState) + .get(INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, null), + ).toBeNull() + }) + + it('withNoQueryHydration disables server dehydration', async () => { + const key = queryKey() + const queryClient = new QueryClient() + const injector = createQueryInjector( + queryClient, + 'server', + withNoQueryHydration(), + ) + + void queryClient.query({ + queryKey: key, + queryFn: () => Promise.resolve('data'), + }).catch(noop) + await vi.advanceTimersByTimeAsync(0) + + injector.get(TransferState).toJson() + + expect( + injector + .get(TransferState) + .get(INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, null), + ).toBeNull() + }) + + it('multiple query clients can use distinct hydration keys', async () => { + const keyA = queryKey() + const keyB = queryKey() + const clientA = new QueryClient() + const clientB = new QueryClient() + + TestBed.configureTestingModule({ + providers: [ + provideAngularQueryChangeDetection(), + { provide: PLATFORM_ID, useValue: 'server' }, + ], + }) + + createEnvironmentInjector( + [provideTanStackQuery(() => clientA, withHydrationKey('client-a'))], + TestBed.inject(EnvironmentInjector), + ) + createEnvironmentInjector( + [provideTanStackQuery(() => clientB, withHydrationKey('client-b'))], + TestBed.inject(EnvironmentInjector), + ) + + clientA.setQueryData(keyA, 'a') + clientB.setQueryData(keyB, 'b') + + const transferState = TestBed.inject(TransferState) + transferState.toJson() + + expect( + transferState.get(makeStateKey('client-a'), null) + ?.queries[0]?.queryKey, + ).toEqual(keyA) + expect( + transferState.get(makeStateKey('client-b'), null) + ?.queries[0]?.queryKey, + ).toEqual(keyB) + }) +}) diff --git a/packages/angular-query/src/__tests__/zoneful-integration.test.ts b/packages/angular-query/src/__tests__/zoneful-integration.test.ts new file mode 100644 index 00000000000..cf5cc85c8c7 --- /dev/null +++ b/packages/angular-query/src/__tests__/zoneful-integration.test.ts @@ -0,0 +1,191 @@ +// cspell:ignore zoneful +import { + ApplicationRef, + Component, + NgZone, + provideZoneChangeDetection, + signal, +} from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { provideIsRestoring } from '../internal' +import { + QueryClient, + injectMutation, + injectQueries, + injectQuery, + provideTanStackQuery, +} from '..' + +/** + * These tests deliberately provide Angular's zone-based scheduler directly. + * They are run by vitest.zoneful.config.ts, whose setup also loads the Zone.js + * testing patches. Keeping this small integration set separate avoids making + * every signal assertion in the zoneless suite depend on Zone.js scheduling. + */ +describe('Zone.js notification integration', () => { + let queryClient: QueryClient + + beforeEach(() => { + queryClient = new QueryClient() + TestBed.configureTestingModule({ + providers: [ + provideZoneChangeDetection(), + provideTanStackQuery(() => queryClient), + ], + }) + }) + + afterEach(() => { + TestBed.resetTestingModule() + }) + + it('keeps an injectQuery signal current after its subscription commits', () => { + const key = ['zoneful-query'] + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: key, + enabled: false, + })), + ) + + expect(query.data()).toBeUndefined() + TestBed.tick() + queryClient.setQueryData(key, 'updated') + + expect(query.data()).toBe('updated') + }) + + it('keeps injectQueries current after its subscription commits', () => { + const key = ['zoneful-queries'] + const queries = TestBed.runInInjectionContext(() => + injectQueries(() => ({ + queries: [{ queryKey: key, enabled: false }], + })), + ) + + expect(queries()[0].data()).toBeUndefined() + TestBed.tick() + queryClient.setQueryData(key, 'updated') + + expect(queries()[0].data()).toBe('updated') + }) + + it('makes committed query notifications visible inside mutation onMutate', async () => { + const key = ['zoneful-optimistic'] + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: key, + enabled: false, + })), + ) + const seenInOnMutate: Array = [] + const mutation = TestBed.runInInjectionContext(() => + injectMutation(() => ({ + mutationKey: ['zoneful-mutation'], + mutationFn: async () => 'server', + onMutate: () => { + queryClient.setQueryData(key, 'optimistic') + seenInOnMutate.push(query.data() as string | undefined) + }, + })), + ) + + query.data() + TestBed.tick() + await mutation.mutateAsync() + + expect(seenInOnMutate).toEqual(['optimistic']) + }) + + it('uses the latest options when restoration ends in the same Zone.js turn', async () => { + const isRestoring = signal(true) + const key = signal('old') + const queriedKeys: Array = [] + + TestBed.resetTestingModule() + TestBed.configureTestingModule({ + providers: [ + provideZoneChangeDetection(), + provideTanStackQuery(() => queryClient), + provideIsRestoring(() => isRestoring.asReadonly()), + ], + }) + + const query = TestBed.runInInjectionContext(() => + injectQuery(() => ({ + queryKey: ['zoneful-restoration', key()], + queryFn: ({ queryKey }) => { + const currentKey = queryKey[1] as string + queriedKeys.push(currentKey) + return Promise.resolve(currentKey) + }, + })), + ) + + expect(query.status()).toBe('pending') + key.set('new') + isRestoring.set(false) + TestBed.tick() + await TestBed.inject(ApplicationRef).whenStable() + + expect(queriedKeys).toEqual(['new']) + expect(query.data()).toBe('new') + expect( + queryClient + .getQueryCache() + .find({ queryKey: ['zoneful-restoration', 'new'] }) + ?.getObserversCount(), + ).toBe(1) + }) + + it('completes dependent queries before application stability without manual effect flushing', async () => { + @Component({ template: '{{ second.data() }}' }) + class Page { + first = injectQuery(() => ({ + queryKey: ['zoneful-dependency-first'], + queryFn: async () => 'ready', + })) + second = injectQuery(() => ({ + queryKey: ['zoneful-dependency-second'], + enabled: this.first.isSuccess(), + queryFn: async () => 'dependent data', + })) + } + + const fixture = TestBed.createComponent(Page) + fixture.autoDetectChanges() + await TestBed.inject(ApplicationRef).whenStable() + expect(fixture.componentInstance.second.data()).toBe('dependent data') + }) + it('renders outside-zone query, multi-query and mutation notifications automatically', async () => { + @Component({ + template: + '{{ query.data() }} / {{ queries()[0].data() }} / {{ mutation.status() }}', + }) + class Page { + query = injectQuery(() => ({ + queryKey: ['outside-single'], + enabled: false, + })) + queries = injectQueries(() => ({ + queries: [{ queryKey: ['outside-multiple'], enabled: false }], + })) + mutation = injectMutation(() => ({ mutationFn: async () => 'done' })) + } + const fixture = TestBed.createComponent(Page) + fixture.autoDetectChanges() + await fixture.whenStable() + const zone = TestBed.inject(NgZone) + const promise = zone.runOutsideAngular(() => { + queryClient.setQueryData(['outside-single'], 'single') + queryClient.setQueryData(['outside-multiple'], 'multiple') + return fixture.componentInstance.mutation.mutateAsync() + }) + await promise + await fixture.whenStable() + expect(fixture.nativeElement.textContent).toBe( + 'single / multiple / success', + ) + }) +}) diff --git a/packages/angular-query/src/hydration-state-key.ts b/packages/angular-query/src/hydration-state-key.ts new file mode 100644 index 00000000000..0dd2c3ee2c9 --- /dev/null +++ b/packages/angular-query/src/hydration-state-key.ts @@ -0,0 +1,15 @@ +import { InjectionToken, makeStateKey } from '@angular/core' +import type {StateKey} from '@angular/core'; +import type { DehydratedState } from '@tanstack/query-core' + +export const INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY = + makeStateKey('tanstack-query-hydration-state') + +export const INTERNAL_TANSTACK_QUERY_HYDRATION_TRANSFER_KEY = + new InjectionToken>( + 'tanstack-query-hydration-transfer-key', + { + providedIn: 'root', + factory: () => INTERNAL_TANSTACK_QUERY_HYDRATION_STATE_KEY, + }, + ) diff --git a/packages/angular-query-experimental/src/index.ts b/packages/angular-query/src/index.ts similarity index 56% rename from packages/angular-query-experimental/src/index.ts rename to packages/angular-query/src/index.ts index fc033224e51..ff9e4d81bd6 100644 --- a/packages/angular-query-experimental/src/index.ts +++ b/packages/angular-query/src/index.ts @@ -4,6 +4,8 @@ export * from '@tanstack/query-core' export * from './types' +export type { QueryResource } from './query-resource' +export { toResource } from './query-resource' export type { DefinedInitialDataOptions, @@ -22,39 +24,31 @@ export type { } from './infinite-query-options' export { infiniteQueryOptions } from './infinite-query-options' -export type { InjectInfiniteQueryOptions } from './inject-infinite-query' export { injectInfiniteQuery } from './inject-infinite-query' -export type { InjectIsFetchingOptions } from './inject-is-fetching' export { injectIsFetching } from './inject-is-fetching' -export type { InjectIsMutatingOptions } from './inject-is-mutating' export { injectIsMutating } from './inject-is-mutating' -export { injectIsRestoring, provideIsRestoring } from './inject-is-restoring' +export { injectIsRestoring } from './inject-is-restoring' -export type { InjectMutationOptions } from './inject-mutation' export { injectMutation } from './inject-mutation' -export type { InjectMutationStateOptions } from './inject-mutation-state' +export type { MutationStateOptions } from './inject-mutation-state' export { injectMutationState } from './inject-mutation-state' -export type { QueriesOptions, QueriesResults } from './inject-queries' +export type { + InjectQueriesOptions, + QueriesOptions, + QueriesResults, +} from './inject-queries.types' +export { injectQueries } from './inject-queries' -export type { InjectQueryOptions } from './inject-query' export { injectQuery } from './inject-query' -export { injectQueryClient } from './inject-query-client' - -export type { - DevtoolsFeature, - PersistQueryClientFeature, - QueryFeature, - QueryFeatures, -} from './providers' +export type { QueryFeature } from './providers' export { - provideAngularQuery, - provideQueryClient, provideTanStackQuery, - queryFeature, + withHydrationKey, + withNoQueryHydration, } from './providers' diff --git a/packages/angular-query-experimental/src/infinite-query-options.ts b/packages/angular-query/src/infinite-query-options.ts similarity index 99% rename from packages/angular-query-experimental/src/infinite-query-options.ts rename to packages/angular-query/src/infinite-query-options.ts index d114e2dac63..f22099373f5 100644 --- a/packages/angular-query-experimental/src/infinite-query-options.ts +++ b/packages/angular-query/src/infinite-query-options.ts @@ -129,7 +129,6 @@ export type DefinedInitialDataInfiniteOptions< initialData: | NonUndefinedGuard> | (() => NonUndefinedGuard>) - | undefined } /** @@ -149,7 +148,7 @@ export type DefinedInitialDataInfiniteOptions< * * @example * ```angular-ts - * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental' + * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query' * * export const projectsOptions = infiniteQueryOptions({ * queryKey: ['projects'], @@ -214,7 +213,7 @@ export function infiniteQueryOptions< * @example * A parameterized factory, so the same options object can be reused per `postId`: * ```angular-ts - * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental' + * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query' * * export const commentsOptions = (postId: string) => * infiniteQueryOptions({ @@ -288,7 +287,7 @@ export function infiniteQueryOptions< * @example * A parameterized factory, so the same options object can be reused per `postId`: * ```angular-ts - * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query-experimental' + * import { infiniteQueryOptions, injectInfiniteQuery } from '@tanstack/angular-query' * * export const commentsOptions = (postId: string) => * infiniteQueryOptions({ diff --git a/packages/angular-query/src/inject-base-query.ts b/packages/angular-query/src/inject-base-query.ts new file mode 100644 index 00000000000..d36f9f30c24 --- /dev/null +++ b/packages/angular-query/src/inject-base-query.ts @@ -0,0 +1,107 @@ +import { computed, effect, inject, untracked } from '@angular/core' +import { QueryClient } from '@tanstack/query-core' +import { injectQueryZone } from './utils/inject-query-zone' +import { injectIsRestoring } from './inject-is-restoring' +import { injectPendingTasksLifecycle } from './utils/inject-pending-tasks-lifecycle' +import { injectExternalStore } from './utils/inject-external-store' +import type { + QueryKey, + QueryObserver, + QueryObserverOptions, + QueryObserverResult, +} from '@tanstack/query-core' + +/** + * Base implementation for `injectQuery` and `injectInfiniteQuery`. + */ +export function injectBaseQuery< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey extends QueryKey, +>( + optionsFn: () => QueryObserverOptions< + TQueryFnData, + TError, + TData, + TQueryData, + TQueryKey + >, + Observer: typeof QueryObserver, +) { + const outsideZone = injectQueryZone() + const queryClient = inject(QueryClient) + const isRestoring = injectIsRestoring() + const lifecycle = injectPendingTasksLifecycle() + + const shouldBlockPendingTasks = ( + observer: QueryObserver, + result: QueryObserverResult, + ) => { + return ( + observer.getCurrentQuery().state.fetchStatus !== 'idle' || + (result.fetchStatus !== 'idle' && result.isEnabled) + ) + } + + const defaultedOptionsSignal = computed(() => { + const defaultedOptions = queryClient.defaultQueryOptions(optionsFn()) + defaultedOptions._optimisticResults = isRestoring() + ? 'isRestoring' + : 'optimistic' + defaultedOptions.notifyOnChangeProps = 'all' + return defaultedOptions + }) + + const observerSignal = computed( + () => new Observer(queryClient, untracked(defaultedOptionsSignal)), + ) + + effect(() => { + const options = defaultedOptionsSignal() + outsideZone(() => untracked(() => observerSignal().setOptions(options))) + }) + + const resultSignal = injectExternalStore(() => { + const observer = observerSignal() + const restoring = isRestoring() + return { + getSnapshot: () => observer.getOptimisticResult(defaultedOptionsSignal()), + subscribe: restoring + ? undefined + : (onStoreChange) => { + const unsubscribe = observer.subscribe(() => { + if (lifecycle.destroyed) return + onStoreChange() + }) + return () => { + lifecycle.setPending(false) + unsubscribe() + } + }, + } + }) + + effect((onCleanup) => { + const result = resultSignal() + if (isRestoring()) { + lifecycle.setPending(false) + return + } + + lifecycle.setPending(shouldBlockPendingTasks(observerSignal(), result)) + onCleanup(() => lifecycle.setPending(false)) + }) + + // Imperative methods use current options before starting work, even when + // invoked before the subscription effect runs. + const getObserver = () => + untracked(() => { + const observer = observerSignal() + observer.setOptions(defaultedOptionsSignal()) + return observer + }) + + return [resultSignal, getObserver] as const +} diff --git a/packages/angular-query/src/inject-infinite-query.ts b/packages/angular-query/src/inject-infinite-query.ts new file mode 100644 index 00000000000..f557fc1875c --- /dev/null +++ b/packages/angular-query/src/inject-infinite-query.ts @@ -0,0 +1,200 @@ +import { InfiniteQueryObserver } from '@tanstack/query-core' +import { assertInInjectionContext, untracked } from '@angular/core' +import { injectQueryZone } from './utils/inject-query-zone' +import { injectBaseQuery } from './inject-base-query' +import { signalProxy } from './utils/signal-proxy' +import { infiniteQueryResultFields } from './utils/result-fields' +import type { + DefaultError, + FetchNextPageOptions, + FetchPreviousPageOptions, + InfiniteData, + InfiniteQueryObserverResult, + QueryKey, + QueryObserver, + RefetchOptions, +} from '@tanstack/query-core' +import type { Signal } from '@angular/core' +import type { + CreateInfiniteQueryOptions, + CreateInfiniteQueryResult, + DefinedCreateInfiniteQueryResult, +} from './types' +import type { + DefinedInitialDataInfiniteOptions, + UndefinedInitialDataInfiniteOptions, +} from './infinite-query-options' + +/** + * This overload is selected when `initialData` is set, so the resulting `data` signal is never `undefined` + * (unless a `select` changes `TData` to include `undefined`). + * + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and + * `queryClient.infiniteQuery`. + * @param optionsFn - A function returning infinite-query options with `initialData` set. Similar to + * `computed` from Angular, this function runs in the reactive context. + * @returns The infinite query result, typed so that `data` is never `undefined`. + */ +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + optionsFn: () => DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): DefinedCreateInfiniteQueryResult + +/** + * Injects an infinite query: a declarative dependency on an asynchronous source of data that is tied to a unique key. + * Infinite queries can additively "load more" data onto an existing set of data or support infinite scroll. + * + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + * @see {@link infiniteQueryOptions} to share these options between `injectInfiniteQuery` and + * `queryClient.infiniteQuery`. + * @param optionsFn - A function that returns infinite query options. Similar to `computed` from Angular, + * this function runs in the reactive context, so signals read inside it drive the query. + * @returns The infinite query result. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'projects', + * template: ` + * @if (query.isPending()) { + * Loading... + * } @else if (query.isError()) { + * Error: {{ query.error()?.message }} + * } @else { + * @for (page of query.data().pages; track $index) { + * @for (project of page; track project.id) { + *

{{ project.name }}

+ * } + * } + * + * } + * `, + * }) + * export class Projects { + * readonly query = injectInfiniteQuery(() => ({ + * queryKey: ['projects'], + * queryFn: ({ pageParam }) => fetchProjects(pageParam), + * initialPageParam: 0, + * getNextPageParam: (lastPage) => lastPage.nextCursor, + * })) + * } + * ``` + */ +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + optionsFn: () => UndefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): CreateInfiniteQueryResult + +/** + * This overload accepts the general {@link CreateInfiniteQueryOptions} shape rather than the + * `initialData`-aware overloads above, so whether `data` is defined can't be inferred from the call + * site — useful when wrapping `injectInfiniteQuery` in your own helper. + * + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/infinite-queries + * @param optionsFn - A function that returns infinite query options. Similar to `computed` from Angular, + * this function runs in the reactive context, so signals read inside it drive the query. + * @returns The infinite query result. + */ +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + optionsFn: () => CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): CreateInfiniteQueryResult + +export function injectInfiniteQuery< + TQueryFnData, + TError = DefaultError, + TData = InfiniteData, + TQueryKey extends QueryKey = QueryKey, + TPageParam = unknown, +>( + optionsFn: () => + | DefinedInitialDataInfiniteOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + | CreateInfiniteQueryOptions< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + >, +): + | DefinedCreateInfiniteQueryResult + | CreateInfiniteQueryResult { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectInfiniteQuery) + } + const outsideZone = injectQueryZone() + const [resultSignal, getObserver] = injectBaseQuery( + optionsFn, + InfiniteQueryObserver as typeof QueryObserver, + ) + const getInfiniteObserver = () => + getObserver() as InfiniteQueryObserver< + TQueryFnData, + TError, + TData, + TQueryKey, + TPageParam + > + return Object.assign( + signalProxy( + resultSignal as Signal>, + infiniteQueryResultFields, + ), + { + refetch: (options?: RefetchOptions) => + outsideZone(() => + untracked(() => getInfiniteObserver().refetch(options)), + ), + fetchNextPage: (options?: FetchNextPageOptions) => + outsideZone(() => + untracked(() => getInfiniteObserver().fetchNextPage(options)), + ), + fetchPreviousPage: (options?: FetchPreviousPageOptions) => + outsideZone(() => + untracked(() => getInfiniteObserver().fetchPreviousPage(options)), + ), + }, + ) as unknown as + | DefinedCreateInfiniteQueryResult + | CreateInfiniteQueryResult +} diff --git a/packages/angular-query/src/inject-is-fetching.ts b/packages/angular-query/src/inject-is-fetching.ts new file mode 100644 index 00000000000..4d104766ce4 --- /dev/null +++ b/packages/angular-query/src/inject-is-fetching.ts @@ -0,0 +1,28 @@ +import { assertInInjectionContext, inject } from '@angular/core' +import { QueryClient } from '@tanstack/query-core' +import { injectExternalStore } from './utils/inject-external-store' +import type { QueryFilters } from '@tanstack/query-core' +import type { Signal } from '@angular/core' + +/** + * Injects a signal that tracks the number of queries that your application is loading or fetching in the + * background (useful for app-wide loading indicators). + * + * Can be used for app-wide loading indicators + * @param filters - A reactive factory for the filters. + * @returns signal with number of loading or fetching queries. + */ +export function injectIsFetching( + filters: () => QueryFilters = () => ({}), +): Signal { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectIsFetching) + } + const queryClient = inject(QueryClient) + const cache = queryClient.getQueryCache() + + return injectExternalStore(() => ({ + getSnapshot: () => queryClient.isFetching(filters()), + subscribe: (onStoreChange) => cache.subscribe(onStoreChange), + })) +} diff --git a/packages/angular-query/src/inject-is-mutating.ts b/packages/angular-query/src/inject-is-mutating.ts new file mode 100644 index 00000000000..84fd6f4051d --- /dev/null +++ b/packages/angular-query/src/inject-is-mutating.ts @@ -0,0 +1,28 @@ +import { assertInInjectionContext, inject } from '@angular/core' +import { QueryClient } from '@tanstack/query-core' +import { injectExternalStore } from './utils/inject-external-store' +import type { MutationFilters } from '@tanstack/query-core' +import type { Signal } from '@angular/core' + +/** + * Injects a signal that tracks the number of mutations that your application currently has `pending` + * (useful for app-wide loading indicators). + * + * Can be used for app-wide loading indicators + * @param filters - A reactive factory for the filters. + * @returns A read-only signal with the number of fetching mutations. + */ +export function injectIsMutating( + filters: () => MutationFilters = () => ({}), +): Signal { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectIsMutating) + } + const queryClient = inject(QueryClient) + const cache = queryClient.getMutationCache() + + return injectExternalStore(() => ({ + getSnapshot: () => queryClient.isMutating(filters()), + subscribe: (onStoreChange) => cache.subscribe(onStoreChange), + })) +} diff --git a/packages/angular-query/src/inject-is-restoring.ts b/packages/angular-query/src/inject-is-restoring.ts new file mode 100644 index 00000000000..7a155a51808 --- /dev/null +++ b/packages/angular-query/src/inject-is-restoring.ts @@ -0,0 +1,40 @@ +import { + InjectionToken, + assertInInjectionContext, + inject, + signal, +} from '@angular/core' +import type { Provider, Signal } from '@angular/core' + +/** + * Internal token used to track isRestoring state, accessible in public API through `injectIsRestoring` and set via `provideIsRestoring` + */ +const IS_RESTORING = new InjectionToken('', { + factory: () => signal(false).asReadonly(), +}) + +/** + * Injects a readonly signal that is true while the persistence integration restores + * cached query data. Returns false when no restoration is in progress. + * @returns The current restoration state. + */ +export function injectIsRestoring() { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectIsRestoring) + } + return inject(IS_RESTORING) +} + +/** + * Supplies injector-scoped restoration state to the Angular Query integrations. + * The factory runs once in Angular's injection context. + * @internal + */ +export function provideIsRestoring( + isRestoringFactory: () => Signal, +): Provider { + return { + provide: IS_RESTORING, + useFactory: isRestoringFactory, + } +} diff --git a/packages/angular-query/src/inject-mutation-state.ts b/packages/angular-query/src/inject-mutation-state.ts new file mode 100644 index 00000000000..cf7d5aa4bd3 --- /dev/null +++ b/packages/angular-query/src/inject-mutation-state.ts @@ -0,0 +1,79 @@ +import { assertInInjectionContext, inject } from '@angular/core' +import { QueryClient } from '@tanstack/query-core' +import { injectExternalStore } from './utils/inject-external-store' +import type { Signal } from '@angular/core' +import type { + Mutation, + MutationCache, + MutationFilters, + MutationState, +} from '@tanstack/query-core' + +type MutationTypeFromResult = [TResult] extends [ + MutationState< + infer TData, + infer TError, + infer TVariables, + infer TOnMutateResult + >, +] + ? Mutation + : Mutation + +export type MutationStateOptions< + TResult = MutationState, + TMutation extends Mutation = + MutationTypeFromResult, +> = { + filters?: MutationFilters + select?: (mutation: TMutation) => TResult +} + +function getResult< + TResult = MutationState, + TMutation extends Mutation = + MutationTypeFromResult, +>( + mutationCache: MutationCache, + options: MutationStateOptions, +): Array { + return mutationCache + .findAll(options.filters) + .map( + (mutation): TResult => + (options.select + ? options.select(mutation as TMutation) + : mutation.state) as TResult, + ) +} + +/** + * Injects a signal that tracks the state of all mutations. + * @param options - A function that returns mutation state options. + * @returns The signal that tracks the state of all mutations. + */ +export function injectMutationState< + TResult = MutationState, + TMutation extends Mutation = + MutationTypeFromResult, +>( + options: () => MutationStateOptions = () => ({}), +): Signal> { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectMutationState) + } + const queryClient = inject(QueryClient) + const mutationCache = queryClient.getMutationCache() + + return injectExternalStore( + () => ({ + getSnapshot: () => getResult(mutationCache, options()), + subscribe: (onStoreChange) => mutationCache.subscribe(onStoreChange), + }), + { + equal: (previous, next) => + previous.length === next.length && + previous.every((value, index) => Object.is(value, next[index])), + }, + ) +} diff --git a/packages/angular-query/src/inject-mutation.ts b/packages/angular-query/src/inject-mutation.ts new file mode 100644 index 00000000000..b319c09b11b --- /dev/null +++ b/packages/angular-query/src/inject-mutation.ts @@ -0,0 +1,153 @@ +import { + assertInInjectionContext, + computed, + effect, + inject, + untracked, +} from '@angular/core' +import { MutationObserver, QueryClient, noop } from '@tanstack/query-core' +import { injectQueryZone } from './utils/inject-query-zone' +import { signalProxy } from './utils/signal-proxy' +import { mutationResultFields } from './utils/result-fields' +import { injectPendingTasksLifecycle } from './utils/inject-pending-tasks-lifecycle' +import { injectExternalStore } from './utils/inject-external-store' +import type { DefaultError } from '@tanstack/query-core' +import type { + CreateMutateAsyncFunction, + CreateMutateFunction, + CreateMutationOptions, + CreateMutationResult, +} from './types' + +/** + * Injects a mutation: an imperative function that can be invoked which typically performs server side effects. + * Unlike queries, mutations are not run automatically. + * + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/mutations + * @see {@link mutationOptions} to share these options between `injectMutation` and `injectMutationState`. + * @param optionsFn - A function that returns mutation options. Similar to `computed` from Angular, this + * function runs in the reactive context, so signals read inside it drive the mutation. + * @returns The mutation result, including `mutate` and `mutateAsync`. + * + * @example + * ```angular-ts + * @Component({ + * template: ` + * @if (mutation.isPending()) { + * Saving... + * } @else if (mutation.isError()) { + * Error: {{ mutation.error()?.message }} + * } + * + * `, + * }) + * export class CreatePost { + * readonly mutation = injectMutation(() => ({ + * mutationFn: createPost, + * })) + * } + * ``` + */ +export function injectMutation< + TData = unknown, + TError = DefaultError, + TVariables = void, + TOnMutateResult = unknown, +>( + optionsFn: () => CreateMutationOptions< + TData, + TError, + TVariables, + TOnMutateResult + >, +): CreateMutationResult { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectMutation) + } + const outsideZone = injectQueryZone() + const queryClient = inject(QueryClient) + const lifecycle = injectPendingTasksLifecycle() + + const optionsSignal = computed(optionsFn) + + const observerSignal = computed( + () => new MutationObserver(queryClient, untracked(optionsSignal)), + ) + + // Configure the observer outside the result computation. Mutation-cache + // listeners can synchronously read the public result while setOptions emits. + effect(() => { + const options = optionsSignal() + outsideZone(() => untracked(() => observerSignal().setOptions(options))) + }) + + const mutationStateSignal = injectExternalStore(() => { + const observer = observerSignal() + return { + getSnapshot: () => observer.getCurrentResult(), + subscribe: (onStoreChange) => observer.subscribe(onStoreChange), + } + }) + + const mutate: CreateMutateFunction< + TData, + TError, + TVariables, + TOnMutateResult + > = (...args) => { + mutateAsync(...args).catch(noop) + } + + let pendingInvocations = 0 + + const mutateAsync: CreateMutateAsyncFunction< + TData, + TError, + TVariables, + TOnMutateResult + > = (...args) => { + return outsideZone(() => + untracked(() => { + const observer = observerSignal() + observer.setOptions(optionsSignal()) + pendingInvocations++ + lifecycle.setPending(true) + // Track invocations, not the observer's latest result: reset or a later + // mutation completing must not release work which is still running. + const settled = () => { + pendingInvocations-- + lifecycle.setPending(pendingInvocations > 0) + } + try { + return observer + .mutate(args[0] as TVariables, args[1]) + .finally(settled) + } catch (error) { + settled() + throw error + } + }), + ) + } + + const reset = () => { + outsideZone(() => + untracked(() => { + const observer = observerSignal() + observer.setOptions(optionsSignal()) + observer.reset() + }), + ) + } + + return Object.assign(signalProxy(mutationStateSignal, mutationResultFields), { + mutate, + mutateAsync, + reset, + }) as unknown as CreateMutationResult< + TData, + TError, + TVariables, + TOnMutateResult + > +} diff --git a/packages/angular-query/src/inject-queries.ts b/packages/angular-query/src/inject-queries.ts new file mode 100644 index 00000000000..8d243171e70 --- /dev/null +++ b/packages/angular-query/src/inject-queries.ts @@ -0,0 +1,201 @@ +import { QueriesObserver, QueryClient } from '@tanstack/query-core' +import { + assertInInjectionContext, + computed, + effect, + inject, + untracked, +} from '@angular/core' +import { injectQueryZone } from './utils/inject-query-zone' +import { signalProxy } from './utils/signal-proxy' +import { queryResultFields } from './utils/result-fields' +import { injectIsRestoring } from './inject-is-restoring' +import { injectPendingTasksLifecycle } from './utils/inject-pending-tasks-lifecycle' +import { injectExternalStore } from './utils/inject-external-store' +import type { + InjectQueriesOptions, + QueriesResults, +} from './inject-queries.types' +import type { + QueryObserverOptions, + QueryObserverResult, +} from '@tanstack/query-core' +import type { Signal } from '@angular/core' + +/** + * Injects multiple queries that run in parallel and react to Angular signals. + * + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/parallel-queries + * @param optionsFn - A function that returns the queries' options. Similar to `computed` from Angular, + * this function runs in the reactive context, so signals read inside it drive the queries. + * @returns A signal containing the query results in the same order as the input queries. + * + * @example + * ```angular-ts + * @Component({ + * selector: 'users', + * template: ` + * @for (query of userQueries(); track $index) { + * @if (query.isSuccess()) { + *

{{ query.data().name }}

+ * } + * } + * `, + * }) + * export class UsersComponent { + * readonly users = input.required>() + * + * readonly userQueries = injectQueries(() => ({ + * queries: this.users().map((user) => ({ + * queryKey: ['user', user.id], + * queryFn: () => fetchUserById(user.id), + * })), + * })) + * } + * ``` + */ +export function injectQueries< + T extends Array, + TCombinedResult = QueriesResults, +>( + optionsFn: () => InjectQueriesOptions, +): Signal { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectQueries) + } + const outsideZone = injectQueryZone() + const queryClient = inject(QueryClient) + const isRestoring = injectIsRestoring() + const lifecycle = injectPendingTasksLifecycle() + + const optionsSignal = computed(optionsFn) + + const defaultedQueries = computed(() => { + return optionsSignal().queries.map((opts) => { + const defaultedOptions = queryClient.defaultQueryOptions( + opts as QueryObserverOptions, + ) + defaultedOptions._optimisticResults = isRestoring() + ? 'isRestoring' + : 'optimistic' + defaultedOptions.notifyOnChangeProps = 'all' + + return defaultedOptions as QueryObserverOptions + }) + }) + + const shouldBlockPendingTasks = ( + observer: QueriesObserver, + results: Array, + ) => { + const queries = observer.getQueries() + + return results.some((result, index) => { + return ( + queries[index]?.state.fetchStatus !== 'idle' || + (result.fetchStatus !== 'idle' && result.isEnabled) + ) + }) + } + + const observerSignal = computed( + () => + new QueriesObserver( + queryClient, + untracked(defaultedQueries), + ), + ) + + effect(() => { + const queries = defaultedQueries() + outsideZone(() => untracked(() => observerSignal().setQueries(queries))) + }) + + // Methods address the current array slot, including methods handed to combine. + const refetches: Array = [] + const getRefetch = (index: number): QueryObserverResult['refetch'] => + (refetches[index] ??= (options) => + outsideZone(() => + untracked(() => { + const observer = observerSignal() + observer.setQueries(defaultedQueries()) + const queryObserver = observer.getObservers()[index] + if (!queryObserver) { + return Promise.reject( + new Error(`Cannot refetch removed query at index ${index}`), + ) + } + return queryObserver.refetch(options) + }), + )) + + const resultSignal = injectExternalStore(() => { + const observer = observerSignal() + const restoring = isRestoring() + return { + getSnapshot: () => { + const results = observer.getOptimisticResult( + defaultedQueries(), + undefined, + )[0] + refetches.length = results.length + return results.map((result, index) => ({ + ...result, + refetch: getRefetch(index), + })) + }, + subscribe: restoring + ? undefined + : (onStoreChange) => { + const unsubscribe = observer.subscribe(() => { + if (lifecycle.destroyed) return + onStoreChange() + }) + return () => { + lifecycle.setPending(false) + unsubscribe() + } + }, + } + }) + + effect((onCleanup) => { + const results = resultSignal() + if (isRestoring()) { + lifecycle.setPending(false) + return + } + + lifecycle.setPending(shouldBlockPendingTasks(observerSignal(), results)) + onCleanup(() => lifecycle.setPending(false)) + }) + + const createResultProxy = (index: number) => { + const resultAtIndexSignal = computed(() => resultSignal()[index]!) + return Object.assign(signalProxy(resultAtIndexSignal, queryResultFields), { + refetch: getRefetch(index), + }) + } + + const resultProxies: Array> = [] + const proxiedResultsSignal = computed(() => { + const results = resultSignal() + resultProxies.length = results.length + + return results.map((_, index) => { + return (resultProxies[index] ??= createResultProxy(index)) + }) + }) + + // Combine in Angular's tracked computation. Core must notify for every raw + // result change, including changes a previous combine function ignored. + return computed(() => { + const result = resultSignal() + const { combine } = optionsSignal() + + if (combine) + return combine(result as Parameters>[0]) + + return proxiedResultsSignal() as unknown as TCombinedResult + }) as unknown as Signal +} diff --git a/packages/angular-query/src/inject-queries.types.ts b/packages/angular-query/src/inject-queries.types.ts new file mode 100644 index 00000000000..ce6f9cd8da0 --- /dev/null +++ b/packages/angular-query/src/inject-queries.types.ts @@ -0,0 +1,223 @@ +import type { + DefaultError, + DefinedQueryObserverResult, + OmitKeyof, + QueriesPlaceholderDataFunction, + QueryFunction, + QueryKey, + QueryObserverResult, + dataTagErrorSymbol, +} from '@tanstack/query-core' +import type { + CreateQueryOptions, + CreateQueryResult, + DefinedCreateQueryResult, +} from './types' + +type QueryObserverOptionsForCreateQueries< + TQueryFnData = unknown, + TError = DefaultError, + TData = TQueryFnData, + TQueryKey extends QueryKey = QueryKey, +> = OmitKeyof< + CreateQueryOptions, + 'placeholderData' +> & { + placeholderData?: TQueryFnData | QueriesPlaceholderDataFunction +} + +type QueryError = T extends { + queryKey: { [dataTagErrorSymbol]: infer TError } +} + ? TError + : T extends CreateQueryOptions + ? unknown extends TError + ? DefaultError + : TError + : DefaultError + +type MAXIMUM_DEPTH = 20 +type SkipTokenForCreateQueries = symbol + +type GetCreateQueryOptionsForCreateQueries = T extends { + queryFnData: infer TQueryFnData + error?: infer TError + data: infer TData +} + ? QueryObserverOptionsForCreateQueries + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? QueryObserverOptionsForCreateQueries + : T extends { data: infer TData; error?: infer TError } + ? QueryObserverOptionsForCreateQueries + : T extends [infer TQueryFnData, infer TError, infer TData] + ? QueryObserverOptionsForCreateQueries + : T extends [infer TQueryFnData, infer TError] + ? QueryObserverOptionsForCreateQueries + : T extends [infer TQueryFnData] + ? QueryObserverOptionsForCreateQueries + : T extends { + queryFn?: + | QueryFunction + | SkipTokenForCreateQueries + select?: (data: any) => infer TData + } + ? QueryObserverOptionsForCreateQueries< + TQueryFnData, + QueryError, + unknown extends TData ? TQueryFnData : TData, + TQueryKey + > + : QueryObserverOptionsForCreateQueries + +type GenericGetDefinedOrUndefinedQueryResult = + T extends { initialData?: infer TInitialData } + ? unknown extends TInitialData + ? TUndefined + : TInitialData extends TData + ? TDefined + : TInitialData extends () => infer TInitialDataResult + ? unknown extends TInitialDataResult + ? TUndefined + : TInitialDataResult extends TData + ? TDefined + : TUndefined + : TUndefined + : TUndefined + +type InferDataAndError = T extends { + queryFnData: any + error?: infer TError + data: infer TData +} + ? { data: TData; error: TError } + : T extends { queryFnData: infer TQueryFnData; error?: infer TError } + ? { data: TQueryFnData; error: TError } + : T extends { data: infer TData; error?: infer TError } + ? { data: TData; error: TError } + : T extends [any, infer TError, infer TData] + ? { data: TData; error: TError } + : T extends [infer TQueryFnData, infer TError] + ? { data: TQueryFnData; error: TError } + : T extends [infer TQueryFnData] + ? { data: TQueryFnData; error: unknown } + : T extends { + queryFn?: + | QueryFunction + | SkipTokenForCreateQueries + select?: (data: any) => infer TData + } + ? { + data: unknown extends TData ? TQueryFnData : TData + error: QueryError + } + : { data: unknown; error: DefaultError } + +type GetCreateQueryResult = GenericGetDefinedOrUndefinedQueryResult< + T, + InferDataAndError['data'], + CreateQueryResult< + InferDataAndError['data'], + InferDataAndError['error'] + >, + DefinedCreateQueryResult< + InferDataAndError['data'], + InferDataAndError['error'] + > +> + +type GetQueryObserverResult = GenericGetDefinedOrUndefinedQueryResult< + T, + InferDataAndError['data'], + QueryObserverResult< + InferDataAndError['data'], + InferDataAndError['error'] + >, + DefinedQueryObserverResult< + InferDataAndError['data'], + InferDataAndError['error'] + > +> + +export type QueriesOptions< + T extends Array, + TResults extends Array = [], + TDepth extends ReadonlyArray = [], +> = TDepth['length'] extends MAXIMUM_DEPTH + ? Array + : T extends [] + ? [] + : T extends [infer Head] + ? [...TResults, GetCreateQueryOptionsForCreateQueries] + : T extends [infer Head, ...infer Tails] + ? QueriesOptions< + [...Tails], + [...TResults, GetCreateQueryOptionsForCreateQueries], + [...TDepth, 1] + > + : ReadonlyArray extends T + ? T + : T extends Array< + QueryObserverOptionsForCreateQueries< + infer TQueryFnData, + infer TError, + infer TData, + infer TQueryKey + > + > + ? Array< + QueryObserverOptionsForCreateQueries< + TQueryFnData, + TError, + TData, + TQueryKey + > + > + : Array + +export type QueriesResults< + T extends Array, + TResults extends Array = [], + TDepth extends ReadonlyArray = [], +> = TDepth['length'] extends MAXIMUM_DEPTH + ? Array + : T extends [] + ? [] + : T extends [infer Head] + ? [...TResults, GetCreateQueryResult] + : T extends [infer Head, ...infer Tails] + ? QueriesResults< + [...Tails], + [...TResults, GetCreateQueryResult], + [...TDepth, 1] + > + : { [K in keyof T]: GetCreateQueryResult } + +type RawQueriesResults< + T extends Array, + TResults extends Array = [], + TDepth extends ReadonlyArray = [], +> = TDepth['length'] extends MAXIMUM_DEPTH + ? Array + : T extends [] + ? [] + : T extends [infer Head] + ? [...TResults, GetQueryObserverResult] + : T extends [infer Head, ...infer Tails] + ? RawQueriesResults< + [...Tails], + [...TResults, GetQueryObserverResult], + [...TDepth, 1] + > + : { [K in keyof T]: GetQueryObserverResult } + +export interface InjectQueriesOptions< + T extends Array, + TCombinedResult = QueriesResults, +> { + queries: + | readonly [...QueriesOptions] + | readonly [ + ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueries }, + ] + combine?: (result: RawQueriesResults) => TCombinedResult +} diff --git a/packages/angular-query-experimental/src/inject-query.ts b/packages/angular-query/src/inject-query.ts similarity index 75% rename from packages/angular-query-experimental/src/inject-query.ts rename to packages/angular-query/src/inject-query.ts index 973538ca053..f2dfe752971 100644 --- a/packages/angular-query-experimental/src/inject-query.ts +++ b/packages/angular-query/src/inject-query.ts @@ -1,12 +1,14 @@ import { QueryObserver } from '@tanstack/query-core' -import { - Injector, - assertInInjectionContext, - inject, - runInInjectionContext, -} from '@angular/core' -import { createBaseQuery } from './create-base-query' -import type { DefaultError, QueryKey } from '@tanstack/query-core' +import { assertInInjectionContext, untracked } from '@angular/core' +import { injectQueryZone } from './utils/inject-query-zone' +import { injectBaseQuery } from './inject-base-query' +import { signalProxy } from './utils/signal-proxy' +import { queryResultFields } from './utils/result-fields' +import type { + DefaultError, + QueryKey, + RefetchOptions, +} from '@tanstack/query-core' import type { CreateQueryOptions, CreateQueryResult, @@ -17,26 +19,16 @@ import type { UndefinedInitialDataOptions, } from './query-options' -export interface InjectQueryOptions { - /** - * The `Injector` in which to create the query. - * - * If this is not provided, the current injection context will be used instead (via `inject`). - */ - injector?: Injector -} - /** - * This overload is selected when `initialData` is set on the options returned by `injectQueryFn`, so the + * This overload is selected when `initialData` is set on the options returned by `optionsFn`, so the * resulting `data` signal is never `undefined` (unless a `select` changes `TData` to include `undefined`). * * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries * @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like - * `queryClient.fetchQuery`. - * @param injectQueryFn - A function returning the {@link DefinedInitialDataOptions} to use — everything you + * `queryClient.query`. + * @param optionsFn - A function returning the {@link DefinedInitialDataOptions} to use — everything you * can pass to `injectQuery`, with `initialData` set. Similar to `computed` from Angular, this function runs * in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. - * @param options - Additional configuration * @returns The query result, typed so that `data` is never `undefined` (unless a `select` changes `TData` to * include `undefined`). * @@ -72,13 +64,12 @@ export function injectQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - injectQueryFn: () => DefinedInitialDataOptions< + optionsFn: () => DefinedInitialDataOptions< TQueryFnData, TError, TData, TQueryKey >, - options?: InjectQueryOptions, ): DefinedCreateQueryResult /** @@ -86,11 +77,10 @@ export function injectQuery< * * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries * @see {@link queryOptions} to share these options between `injectQuery` and imperative APIs like - * `queryClient.fetchQuery`. - * @param injectQueryFn - A function returning the {@link UndefinedInitialDataOptions} to use — everything + * `queryClient.query`. + * @param optionsFn - A function returning the {@link UndefinedInitialDataOptions} to use — everything * you can pass to `injectQuery`. Similar to `computed` from Angular, this function runs in the reactive * context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive the query. - * @param options - Additional configuration * @returns The query result. `status()` is `'pending'` if there is no cached data to display, `'error'` if * the last fetch attempt failed, or `'success'` if the query has data to display. `isPending`/`isSuccess`/ * `isError` are type-guard methods for convenience. @@ -149,7 +139,6 @@ export function injectQuery< * readonly postsQuery = injectQuery(() => ({ * queryKey: ['posts', this.filter()], * queryFn: () => fetchPosts(this.filter()), - * // Signals can be combined with expressions * enabled: !!this.filter(), * })) * } @@ -161,13 +150,12 @@ export function injectQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - injectQueryFn: () => UndefinedInitialDataOptions< + optionsFn: () => UndefinedInitialDataOptions< TQueryFnData, TError, TData, TQueryKey >, - options?: InjectQueryOptions, ): CreateQueryResult /** @@ -176,10 +164,8 @@ export function injectQuery< * `injectQuery` in your own helper function that forwards caller-provided options. * * @see https://tanstack.com/query/latest/docs/framework/angular/guides/queries - * @param injectQueryFn - A function that returns query options. Similar to `computed` from Angular, this - * function runs in the reactive context, so signals read inside it (in `queryKey`, `enabled`, etc.) drive - * the query. - * @param options - Additional configuration + * @param optionsFn - A function that returns query options. Similar to `computed` from Angular, this + * function runs in the reactive context, so signals read inside it drive the query. * @returns The query result. */ export function injectQuery< @@ -188,21 +174,19 @@ export function injectQuery< TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( - injectQueryFn: () => CreateQueryOptions< - TQueryFnData, - TError, - TData, - TQueryKey - >, - options?: InjectQueryOptions, + optionsFn: () => CreateQueryOptions, ): CreateQueryResult export function injectQuery( - injectQueryFn: () => CreateQueryOptions, - options?: InjectQueryOptions, -) { - !options?.injector && assertInInjectionContext(injectQuery) - return runInInjectionContext(options?.injector ?? inject(Injector), () => - createBaseQuery(injectQueryFn, QueryObserver), - ) as unknown as CreateQueryResult + optionsFn: () => CreateQueryOptions, +): DefinedCreateQueryResult | CreateQueryResult { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectQuery) + } + const outsideZone = injectQueryZone() + const [resultSignal, getObserver] = injectBaseQuery(optionsFn, QueryObserver) + return Object.assign(signalProxy(resultSignal, queryResultFields), { + refetch: (options?: RefetchOptions) => + outsideZone(() => untracked(() => getObserver().refetch(options))), + }) as unknown as DefinedCreateQueryResult | CreateQueryResult } diff --git a/packages/angular-query/src/internal.ts b/packages/angular-query/src/internal.ts new file mode 100644 index 00000000000..b975e19381b --- /dev/null +++ b/packages/angular-query/src/internal.ts @@ -0,0 +1,8 @@ +/** + * Internal APIs shared by the Angular Query packages. + * + * This entry point is not part of the application-facing API and may change + * without notice. + */ +export { getQueryFeatureProviders, queryFeature } from './providers' +export { provideIsRestoring } from './inject-is-restoring' diff --git a/packages/angular-query-experimental/src/mutation-options.ts b/packages/angular-query/src/mutation-options.ts similarity index 98% rename from packages/angular-query-experimental/src/mutation-options.ts rename to packages/angular-query/src/mutation-options.ts index 9f16afab09f..89b521bdc44 100644 --- a/packages/angular-query-experimental/src/mutation-options.ts +++ b/packages/angular-query/src/mutation-options.ts @@ -14,7 +14,7 @@ import type { CreateMutationOptions } from './types' * @example * Looking the mutation up elsewhere via its `mutationKey`, e.g. for a global "saving…" indicator: * ```angular-ts - * import { mutationOptions, injectMutationState } from '@tanstack/angular-query-experimental' + * import { mutationOptions, injectMutationState } from '@tanstack/angular-query' * * const createPostOptions = mutationOptions({ * mutationKey: ['posts', 'create'], @@ -66,7 +66,7 @@ export function mutationOptions< * @example * Sharing options across services, so `QueriesService` stays the single place a mutation is defined: * ```angular-ts - * import { mutationOptions, injectMutation } from '@tanstack/angular-query-experimental' + * import { mutationOptions, injectMutation } from '@tanstack/angular-query' * * @Injectable({ providedIn: 'root' }) * export class QueriesService { diff --git a/packages/angular-query/src/providers.ts b/packages/angular-query/src/providers.ts new file mode 100644 index 00000000000..98c681d332f --- /dev/null +++ b/packages/angular-query/src/providers.ts @@ -0,0 +1,192 @@ +import { isPlatformBrowser, isPlatformServer } from '@angular/common' +import { + DOCUMENT, + DestroyRef, + InjectionToken, + PLATFORM_ID, + TransferState, + inject, + makeEnvironmentProviders, + makeStateKey, + provideEnvironmentInitializer, +} from '@angular/core' +import { QueryClient, dehydrate, hydrate } from '@tanstack/query-core' +import { INTERNAL_TANSTACK_QUERY_HYDRATION_TRANSFER_KEY } from './hydration-state-key' +import { injectQueryZone } from './utils/inject-query-zone' +import type { DehydratedState } from '@tanstack/query-core' +import type { EnvironmentProviders } from '@angular/core' + +const INTERNAL_QUERY_CLIENT_SHOULD_HYDRATE = new InjectionToken('', { + providedIn: 'root', + factory: () => true, +}) + +function configureQueryClient() { + const queryClient = inject(QueryClient) + const destroyRef = inject(DestroyRef) + const platformId = inject(PLATFORM_ID) + + if (isPlatformServer(platformId)) { + // DOM emulation can fool core's runtime detection. Apply server defaults to + // this client without changing other clients or global scheduling providers. + const defaults = queryClient.getDefaultOptions() + queryClient.setDefaultOptions({ + ...defaults, + queries: { gcTime: Infinity, retry: false, ...defaults.queries }, + mutations: { gcTime: Infinity, ...defaults.mutations }, + }) + } + const shouldHydrate = inject(INTERNAL_QUERY_CLIENT_SHOULD_HYDRATE) + const hydrationStateKey = inject( + INTERNAL_TANSTACK_QUERY_HYDRATION_TRANSFER_KEY, + ) + + if (inject(DOCUMENT, { optional: true })) { + const transferState = inject(TransferState) + + if (shouldHydrate && isPlatformServer(platformId)) { + transferState.onSerialize(hydrationStateKey, () => dehydrate(queryClient)) + } else if (shouldHydrate && isPlatformBrowser(platformId)) { + const dehydratedState = transferState.get(hydrationStateKey, null) + if (dehydratedState) { + hydrate(queryClient, dehydratedState) + transferState.remove(hydrationStateKey) + } + } + } + + queryClient.mount() + destroyRef.onDestroy(() => queryClient.unmount()) +} + +/** + * Provides a `QueryClient` and optional TanStack Query features. + * The factory runs once per injector in Angular's injection context, so it can + * call `inject()` and each SSR request can receive an independent cache. + * + * **Example - standalone** + * + * @example + * ```ts + * import { + * provideTanStackQuery, + * QueryClient, + * } from '@tanstack/angular-query' + * + * bootstrapApplication(AppComponent, { + * providers: [provideTanStackQuery(() => new QueryClient())], + * }) + * ``` + * + * You can also enable optional developer tools by adding `withDevtools`. By + * default the tools will then be loaded when your app is in development mode. + * + * ```ts + * import { provideTanStackQuery, QueryClient } from '@tanstack/angular-query' + * import { withDevtools } from '@tanstack/angular-query-devtools' + * + * bootstrapApplication(AppComponent, { + * providers: [ + * provideTanStackQuery(() => new QueryClient(), withDevtools()), + * ], + * }) + * ``` + * + * @param queryClientFactory - Creates or resolves a `QueryClient` in the injection context. + * @param features - Optional features to configure additional Query functionality. + * @returns A single {@link EnvironmentProviders} value (do not spread into `providers`). + * @see https://tanstack.com/query/v5/docs/framework/angular/quick-start + * @see https://tanstack.com/query/v5/docs/framework/angular/devtools + * @see https://tanstack.com/query/latest/docs/framework/angular/guides/ssr + */ +export function provideTanStackQuery( + queryClientFactory: () => QueryClient, + ...features: ReadonlyArray +): EnvironmentProviders { + return makeEnvironmentProviders([ + { + provide: QueryClient, + useFactory: () => injectQueryZone()(queryClientFactory), + }, + ...features.map(getQueryFeatureProviders), + provideEnvironmentInitializer(configureQueryClient), + ]) +} + +const queryFeatureBrand: unique symbol = Symbol('QueryFeature') + +/** + * Opaque configuration returned by Query feature functions such as `withHydrationKey`. + * Pass features to `provideTanStackQuery`; applications do not construct them directly. + */ +export interface QueryFeature { + /** @internal */ + readonly [queryFeatureBrand]: true +} + +interface InternalQueryFeature extends QueryFeature { + readonly ɵproviders: EnvironmentProviders +} + +/** + * Creates a feature shared by Angular Query integration packages. + * @internal + * @param providers - + * @returns A Query feature. + */ +export function queryFeature(providers: EnvironmentProviders): QueryFeature { + const feature: InternalQueryFeature = { + [queryFeatureBrand]: true, + ɵproviders: providers, + } + + return feature +} + +/** @internal */ +export function getQueryFeatureProviders( + feature: QueryFeature, +): EnvironmentProviders { + return (feature as InternalQueryFeature).ɵproviders +} + +/** + * Sets a non-default serialization key for this injector's `QueryClient` cache (server dehydrate / + * browser hydrate via `TransferState`). Use this when you have multiple `QueryClient` instances + * so each has its own key. The default key applies when you do not add this feature. + * + * ```ts + * providers: [ + * provideTanStackQuery( + * () => new QueryClient(), + * withHydrationKey('my-secondary-query-cache'), + * ), + * ] + * ``` + * + * @param key - A unique string for this client's `TransferState` entry. + */ +export function withHydrationKey(key: string): QueryFeature { + return queryFeature( + makeEnvironmentProviders([ + { + provide: INTERNAL_TANSTACK_QUERY_HYDRATION_TRANSFER_KEY, + useValue: makeStateKey(key), + }, + ]), + ) +} + +/** + * Disables `TransferState` hydration and dehydration for the current environment injector. + */ +export function withNoQueryHydration(): QueryFeature { + return queryFeature( + makeEnvironmentProviders([ + { + provide: INTERNAL_QUERY_CLIENT_SHOULD_HYDRATE, + useValue: false, + }, + ]), + ) +} diff --git a/packages/angular-query-experimental/src/query-options.ts b/packages/angular-query/src/query-options.ts similarity index 89% rename from packages/angular-query-experimental/src/query-options.ts rename to packages/angular-query/src/query-options.ts index 0de61895e72..841c943b2ac 100644 --- a/packages/angular-query-experimental/src/query-options.ts +++ b/packages/angular-query/src/query-options.ts @@ -3,7 +3,6 @@ import type { InitialDataFunction, NonUndefinedGuard, OmitKeyof, - QueryFunction, QueryKey, QueryKeyWithDataTag, SkipToken, @@ -82,31 +81,15 @@ export type DefinedInitialDataOptions< TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> = Omit< - CreateQueryOptions, - 'queryFn' -> & { - /** - * If set, this value will be used as the initial data for the query cache (as long as the query hasn't been - * created or cached yet). If set to a function, the function will be called **once** during the shared/root - * query initialization, and be expected to synchronously return the initial data. Initial data is - * considered stale by default unless a `staleTime` has been set. `initialData` **is persisted** to the - * cache. - */ +> = CreateQueryOptions & { initialData: | NonUndefinedGuard | (() => NonUndefinedGuard) - /** - * Optional here, but omitting it is only safe when no fetch will be attempted — for example with - * `enabled: false`, or when a default query function has been defined. Otherwise, an enabled query with no - * `queryFn` still tries to fetch and fails with a "Missing queryFn" error; `initialData` does not prevent this. - */ - queryFn?: QueryFunction } /** * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options - * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * can be shared across functions and imperative APIs such as `queryClient.query`. `options.queryKey` is * required and is the query key to generate options for. * * This overload is selected when `initialData` is set, so the resulting `data` is never `undefined` (unless @@ -120,7 +103,7 @@ export type DefinedInitialDataOptions< * * @example * ```angular-ts - * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + * import { queryOptions, injectQuery } from '@tanstack/angular-query' * * export const postsOptions = queryOptions({ * queryKey: ['posts'], @@ -160,7 +143,7 @@ export function queryOptions< /** * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options - * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * can be shared across functions and imperative APIs such as `queryClient.query`. `options.queryKey` is * required and is the query key to generate options for. * * @see {@link injectQuery} to run a query with these options. @@ -171,7 +154,7 @@ export function queryOptions< * @example * A parameterized factory, so the same options object can be reused per `id`: * ```angular-ts - * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + * import { queryOptions, injectQuery } from '@tanstack/angular-query' * * export const postOptions = (id: string) => * queryOptions({ @@ -209,7 +192,7 @@ export function queryOptions< /** * You can generally pass everything to `queryOptions` that you can also pass to `injectQuery`. These options - * can be shared across functions and imperative APIs such as `queryClient.fetchQuery`. `options.queryKey` is + * can be shared across functions and imperative APIs such as `queryClient.query`. `options.queryKey` is * required and is the query key to generate options for. * * @see {@link injectQuery} to run a query with these options. @@ -221,7 +204,7 @@ export function queryOptions< * @example * A parameterized factory, so the same options object can be reused per `id`: * ```angular-ts - * import { queryOptions, injectQuery } from '@tanstack/angular-query-experimental' + * import { queryOptions, injectQuery } from '@tanstack/angular-query' * * export const postOptions = (id: string) => * queryOptions({ @@ -250,7 +233,7 @@ export function queryOptions< * @example * A factory that disables the query, type safe, until `postId` is set: * ```angular-ts - * import { queryOptions, skipToken, injectQuery } from '@tanstack/angular-query-experimental' + * import { queryOptions, skipToken, injectQuery } from '@tanstack/angular-query' * * export const postOptions = (postId: number | undefined) => * queryOptions({ diff --git a/packages/angular-query/src/query-resource.ts b/packages/angular-query/src/query-resource.ts new file mode 100644 index 00000000000..33f47894cc5 --- /dev/null +++ b/packages/angular-query/src/query-resource.ts @@ -0,0 +1,137 @@ +import { computed, untracked } from '@angular/core' +import type { QueryObserverResult } from '@tanstack/query-core' +import type { ResourceSnapshot } from './utils/resource-types' +import type { Resource, ResourceStatus, Signal } from '@angular/core' + +export interface QueryResource extends Resource { + /** + * The current query state as a single snapshot. + */ + readonly snapshot: Signal> + + /** + * Requests a new query fetch only when the current query data is stale. + * + * Used for compatibility with Angular APIs that might want to reload a resource, + * like Signal Forms' validateAsync resource interface. + * + * @returns `true` if a reload was initiated, `false` if a reload was unnecessary or unsupported. + */ + reload: () => boolean +} + +type QueryResourceSource = { + readonly data: Signal + readonly status: Signal + readonly fetchStatus: Signal + readonly error: Signal + readonly isStale: Signal + readonly refetch: () => unknown +} + +/** + * Converts an Angular Query result into Angular's Resource interface. + * + * Call this function once and reuse the returned resource. + * + * @param query An Angular Query or infinite-query result. + * @returns A Resource-compatible view of the query result. + */ +export function toResource( + query: QueryResourceSource, +): QueryResource { + // Intentionally does not use resource from snapshot to have + // support for the reload method for compatible libraries + const status = computed(() => { + const fetchStatus = query.fetchStatus() + const data = query.data() + + if (fetchStatus === 'fetching' || fetchStatus === 'paused') { + return data === undefined ? 'loading' : 'reloading' + } + + switch (query.status()) { + case 'success': + return 'resolved' + case 'error': + return 'error' + case 'pending': + return 'idle' + } + }) + + const error = computed(() => { + return query.status() === 'error' + ? normalizeError(query.error()) + : undefined + }) + + const value = computed(() => { + if (status() === 'error') { + throw error() + } + + return query.data() + }) + + const snapshot = computed>(() => { + const currentStatus = status() + + if (currentStatus === 'error') { + return { status: 'error', error: error()! } + } + + return { status: currentStatus, value: query.data() } + }) + + const hasValue = (() => + status() !== 'error' && + query.data() !== undefined) as QueryResource['hasValue'] + + return { + value, + status, + error, + snapshot, + isLoading: computed(() => { + const currentStatus = status() + return currentStatus === 'loading' || currentStatus === 'reloading' + }), + hasValue, + reload(): boolean { + const currentStatus = untracked(status) + if ( + currentStatus === 'idle' || + currentStatus === 'loading' || + currentStatus === 'reloading' + ) { + return false + } + + if (!untracked(query.isStale)) { + return false + } + + void query.refetch() + return true + }, + } +} + +function normalizeError(error: unknown): Error { + if (isErrorLike(error)) { + return error + } + + return new Error(String(error), { cause: error }) +} + +function isErrorLike(error: unknown): error is Error { + return ( + error instanceof Error || + (typeof error === 'object' && + error !== null && + typeof (error as Error).name === 'string' && + typeof (error as Error).message === 'string') + ) +} diff --git a/packages/angular-query-experimental/src/types.ts b/packages/angular-query/src/types.ts similarity index 68% rename from packages/angular-query-experimental/src/types.ts rename to packages/angular-query/src/types.ts index b0acd8579fc..2bdde723202 100644 --- a/packages/angular-query-experimental/src/types.ts +++ b/packages/angular-query/src/types.ts @@ -16,54 +16,19 @@ import type { QueryObserverResult, } from '@tanstack/query-core' import type { Signal } from '@angular/core' -import type { MapToSignals } from './signal-proxy' +import type { MapToSignals, MethodKeys } from './utils/signal-proxy' -/** - * The options shared across `angular-query-experimental`'s query functions. Extends - * {@link QueryObserverOptions} from `@tanstack/query-core` as-is — unlike `react-query`, - * `angular-query-experimental` has no extra framework-specific option here. - * - * @template TQueryFnData - The type your `queryFn` resolves to. - * @template TError - The type of errors your `queryFn` may throw. - * @template TData - The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no - * `select` is used. - * @template TQueryData - The type of the data actually held in the query cache — the input to `select` and - * `placeholderData`. Defaults to, and is usually the same as, `TQueryFnData`. - * @template TQueryKey - The type of your `queryKey`. - */ -export interface CreateBaseQueryOptions< - TQueryFnData = unknown, - TError = DefaultError, - TData = TQueryFnData, - TQueryData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey, -> extends QueryObserverOptions< - TQueryFnData, - TError, - TData, - TQueryData, - TQueryKey -> {} +type SignalFunction any> = T & Signal> -/** - * The options accepted by `injectQuery`. Same as {@link CreateBaseQueryOptions}, minus `suspense` — which - * `angular-query-experimental` doesn't support, unlike `react-query`. - * - * @template TQueryFnData - The type your `queryFn` resolves to. - * @template TError - The type of errors your `queryFn` may throw. - * @template TData - The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` when no - * `select` is used. - * @template TQueryKey - The type of your `queryKey`. - */ -export interface CreateQueryOptions< +export type CreateQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, -> extends OmitKeyof< - CreateBaseQueryOptions, - 'suspense' -> {} +> = OmitKeyof< + QueryObserverOptions, + 'notifyOnChangeProps' | 'suspense' | 'throwOnError' +> type CreateStatusBasedQueryResult< TStatus extends QueryObserverResult['status'], @@ -71,53 +36,141 @@ type CreateStatusBasedQueryResult< TError = DefaultError, > = Extract, { status: TStatus }> -/** - * The `isSuccess`/`isError`/`isPending` methods on a query result. Unlike `react-query`'s derived booleans, - * these are type-guard methods you call — `if (query.isSuccess())` — so that `query.data` narrows away - * `undefined` inside the branch, the same way `status` narrowing works on the plain object `react-query` - * returns. - * - * @template TData - The type `data` ends up as after `select` runs. - * @template TError - The type of errors your `queryFn` may throw. - */ +type CreateStatusBasedDefinedQueryResult< + TStatus extends DefinedQueryObserverResult['status'], + TData = unknown, + TError = DefaultError, +> = Extract, { status: TStatus }> + +type CreateStatusBasedInfiniteQueryResult< + TStatus extends InfiniteQueryObserverResult['status'], + TData = unknown, + TError = DefaultError, +> = Extract, { status: TStatus }> + +type CreateStatusBasedDefinedInfiniteQueryResult< + TStatus extends DefinedInfiniteQueryObserverResult['status'], + TData = unknown, + TError = DefaultError, +> = Extract< + DefinedInfiniteQueryObserverResult, + { status: TStatus } +> + export interface BaseQueryNarrowing { - isSuccess: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'success', TData, TError> + isSuccess: SignalFunction< + ( + this: CreateBaseQueryResult, + ) => this is CreateBaseQueryResult< + TData, + TError, + CreateStatusBasedQueryResult<'success', TData, TError> + > > - isError: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'error', TData, TError> + isError: SignalFunction< + ( + this: CreateBaseQueryResult, + ) => this is CreateBaseQueryResult< + TData, + TError, + CreateStatusBasedQueryResult<'error', TData, TError> + > > - isPending: ( - this: CreateBaseQueryResult, - ) => this is CreateBaseQueryResult< - TData, - TError, - CreateStatusBasedQueryResult<'pending', TData, TError> + isPending: SignalFunction< + ( + this: CreateBaseQueryResult, + ) => this is CreateBaseQueryResult< + TData, + TError, + CreateStatusBasedQueryResult<'pending', TData, TError> + > + > +} + +export interface DefinedQueryNarrowing { + isSuccess: SignalFunction< + ( + this: DefinedCreateQueryResult, + ) => this is DefinedCreateQueryResult< + TData, + TError, + CreateStatusBasedDefinedQueryResult<'success', TData, TError> + > + > + isError: SignalFunction< + ( + this: DefinedCreateQueryResult, + ) => this is DefinedCreateQueryResult< + TData, + TError, + CreateStatusBasedDefinedQueryResult<'error', TData, TError> + > + > + isPending: SignalFunction< + (this: DefinedCreateQueryResult) => this is never + > +} + +export interface BaseInfiniteQueryNarrowing< + TData = unknown, + TError = DefaultError, +> { + isSuccess: SignalFunction< + ( + this: CreateInfiniteQueryResult, + ) => this is CreateInfiniteQueryResult< + TData, + TError, + CreateStatusBasedInfiniteQueryResult<'success', TData, TError> + > + > + isError: SignalFunction< + ( + this: CreateInfiniteQueryResult, + ) => this is CreateInfiniteQueryResult< + TData, + TError, + CreateStatusBasedInfiniteQueryResult<'error', TData, TError> + > + > + isPending: SignalFunction< + ( + this: CreateInfiniteQueryResult, + ) => this is CreateInfiniteQueryResult< + TData, + TError, + CreateStatusBasedInfiniteQueryResult<'pending', TData, TError> + > + > +} + +export interface DefinedInfiniteQueryNarrowing< + TData = unknown, + TError = DefaultError, +> { + isSuccess: SignalFunction< + ( + this: DefinedCreateInfiniteQueryResult, + ) => this is DefinedCreateInfiniteQueryResult< + TData, + TError, + CreateStatusBasedDefinedInfiniteQueryResult<'success', TData, TError> + > + > + isError: SignalFunction< + ( + this: DefinedCreateInfiniteQueryResult, + ) => this is DefinedCreateInfiniteQueryResult< + TData, + TError, + CreateStatusBasedDefinedInfiniteQueryResult<'error', TData, TError> + > + > + isPending: SignalFunction< + (this: DefinedCreateInfiniteQueryResult) => this is never > } -/** - * The options accepted by `injectInfiniteQuery`. Same as {@link CreateBaseQueryOptions}, minus `suspense` — - * which `angular-query-experimental` doesn't support, unlike `react-query` — extends - * {@link InfiniteQueryObserverOptions} from `@tanstack/query-core` for the infinite-query-specific options - * (`getNextPageParam`, `initialPageParam`, etc.). - * - * @template TQueryFnData - The type of a single page, as your `queryFn` resolves it. - * @template TError - The type of errors your `queryFn` may throw. - * @template TData - The type `data` ends up as after `select` runs. Defaults to `TQueryFnData` here, though - * `injectInfiniteQuery` itself defaults it to `InfiniteData` — the shape `data` actually has - * when no `select` is used. - * @template TQueryKey - The type of your `queryKey`. - * @template TPageParam - The type of the parameter passed to `queryFn` to fetch a given page. - */ export interface CreateInfiniteQueryOptions< TQueryFnData = unknown, TError = DefaultError, @@ -132,7 +185,7 @@ export interface CreateInfiniteQueryOptions< TQueryKey, TPageParam >, - 'suspense' + 'notifyOnChangeProps' | 'suspense' | 'throwOnError' > {} /** @@ -149,9 +202,15 @@ export interface CreateInfiniteQueryOptions< export type CreateBaseQueryResult< TData = unknown, TError = DefaultError, - TState = QueryObserverResult, + TState extends QueryObserverResult = QueryObserverResult< + TData, + TError + >, > = BaseQueryNarrowing & - MapToSignals> + MapToSignals< + OmitKeyof, + MethodKeys> + > /** * The result of `injectQuery`. Same as {@link CreateBaseQueryResult}. @@ -175,9 +234,13 @@ export type CreateQueryResult< export type DefinedCreateQueryResult< TData = unknown, TError = DefaultError, - TState = DefinedQueryObserverResult, -> = BaseQueryNarrowing & - MapToSignals> + TState extends DefinedQueryObserverResult = + DefinedQueryObserverResult, +> = DefinedQueryNarrowing & + MapToSignals< + OmitKeyof, + MethodKeys> + > /** * The result of `injectInfiniteQuery` when `initialData` isn't set — `data` may be `undefined` while the @@ -191,8 +254,10 @@ export type DefinedCreateQueryResult< export type CreateInfiniteQueryResult< TData = unknown, TError = DefaultError, -> = BaseQueryNarrowing & - MapToSignals> + TState extends InfiniteQueryObserverResult = + InfiniteQueryObserverResult, +> = BaseInfiniteQueryNarrowing & + MapToSignals> /** * The result of `injectInfiniteQuery` when `initialData` is set — `data` is never `undefined`. Same shape as @@ -205,11 +270,15 @@ export type CreateInfiniteQueryResult< export type DefinedCreateInfiniteQueryResult< TData = unknown, TError = DefaultError, - TDefinedInfiniteQueryObserver = DefinedInfiniteQueryObserverResult< + TDefinedInfiniteQueryObserver extends DefinedInfiniteQueryObserverResult< TData, TError - >, -> = MapToSignals + > = DefinedInfiniteQueryObserverResult, +> = DefinedInfiniteQueryNarrowing & + MapToSignals< + TDefinedInfiniteQueryObserver, + MethodKeys + > /** * The options accepted by `injectMutation`. Same as {@link MutationObserverOptions} from @@ -228,7 +297,7 @@ export interface CreateMutationOptions< TOnMutateResult = unknown, > extends OmitKeyof< MutationObserverOptions, - '_defaulted' + '_defaulted' | 'throwOnError' > {} /** @@ -312,19 +381,6 @@ type CreateStatusBasedMutationResult< { status: TStatus } > -type SignalFunction any> = T & Signal> - -/** - * The `isSuccess`/`isError`/`isPending`/`isIdle` methods on a mutation result. Each is both a `Signal` - * (its current boolean value is read reactively without calling it) and a type-guard function you can - * call — `if (mutation.isSuccess())` — so that `mutation.data` narrows away `undefined` inside the branch. - * - * @template TData - The type your mutation function resolves to. - * @template TError - The type of errors your mutation function may throw. - * @template TVariables - The type of the variable passed to `mutate`/`mutateAsync`. - * @template TOnMutateResult - The type returned by `onMutate`, passed to `onSuccess`/`onError`/`onSettled` as - * their `onMutateResult` parameter — useful for optimistic-update rollback data. - */ export interface BaseMutationNarrowing< TData = unknown, TError = DefaultError, @@ -426,4 +482,7 @@ export type CreateMutationResult< TOnMutateResult >, > = BaseMutationNarrowing & - MapToSignals> + MapToSignals< + OmitKeyof, + MethodKeys> + > diff --git a/packages/angular-query/src/utils/__test__/inject-external-store.test.ts b/packages/angular-query/src/utils/__test__/inject-external-store.test.ts new file mode 100644 index 00000000000..d8d8c1e7011 --- /dev/null +++ b/packages/angular-query/src/utils/__test__/inject-external-store.test.ts @@ -0,0 +1,272 @@ +import { Component, computed, input, inputBinding, signal } from '@angular/core' +import { TestBed } from '@angular/core/testing' +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { injectExternalStore } from '../inject-external-store' +import { provideAngularQueryChangeDetection } from '../../__tests__/test-utils' +import type { Signal } from '@angular/core' + +function store(initial: number) { + let current = initial + const listeners = new Set<() => void>() + const cleanup = vi.fn((notify: () => void) => listeners.delete(notify)) + const subscribe = vi.fn((notify: () => void) => { + listeners.add(notify) + return () => cleanup(notify) + }) + return { + subscribe, + cleanup, + getSnapshot: () => current, + set(next: number) { + current = next + listeners.forEach((notify) => notify()) + }, + } +} + +function host( + create: () => Signal, + initialize?: (value: Signal) => void, +) { + @Component({ template: '' }) + class Host { + readonly value = create() + ngOnInit() { + initialize?.(this.value) + } + } + return TestBed.createComponent(Host) +} + +describe('injectExternalStore', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideAngularQueryChangeDetection()], + }) + }) + + it('reflects changes made during initialization after an early snapshot read', () => { + const source = store(1) + const fixture = host( + () => injectExternalStore(() => source), + (value) => { + expect(value()).toBe(1) + source.set(2) + }, + ) + fixture.detectChanges() + expect(fixture.componentInstance.value()).toBe(2) + source.set(3) + expect(fixture.componentInstance.value()).toBe(3) + }) + + it.each([false, true])( + 'reflects changes made by subscription setup (notifies: %s)', + (notifies) => { + const source = store(1) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: source.getSnapshot, + subscribe: (notify) => { + source.set(2) + if (notifies) notify() + return source.subscribe(notify) + }, + })), + ) + fixture.detectChanges() + expect(fixture.componentInstance.value()).toBe(2) + source.set(3) + expect(fixture.componentInstance.value()).toBe(3) + }, + ) + + it('preserves equal snapshots for consumers', () => { + const source = store(1) + const fixture = host(() => + injectExternalStore( + () => ({ + getSnapshot: () => ({ count: source.getSnapshot() }), + subscribe: source.subscribe, + }), + { equal: (a, b) => a.count === b.count }, + ), + ) + fixture.detectChanges() + const derive = vi.fn(() => fixture.componentInstance.value().count) + const derived = computed(derive) + expect(derived()).toBe(1) + source.set(1) + expect(derived()).toBe(1) + expect(derive).toHaveBeenCalledOnce() + source.set(2) + expect(derived()).toBe(2) + }) + + it('tracks snapshot dependencies without reconnecting for selectors or incidental reads', () => { + const source = store(0) + const multiplier = signal(10) + const incidental = signal(0) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: () => (source.getSnapshot() > 0 ? multiplier() : 0), + subscribe: (notify) => { + incidental() + return source.subscribe(notify) + }, + })), + ) + fixture.detectChanges() + const value = fixture.componentInstance.value + expect(value()).toBe(0) + source.set(1) + expect(value()).toBe(10) + multiplier.set(20) + incidental.set(1) + fixture.detectChanges() + expect(value()).toBe(20) + expect(source.subscribe).toHaveBeenCalledOnce() + }) + + it('follows the current source and releases the previous source', () => { + const first = store(1) + const second = store(10) + const requested = signal(first) + const fixture = host(() => injectExternalStore(() => requested())) + fixture.detectChanges() + const value = fixture.componentInstance.value + expect(value()).toBe(1) + requested.set(second) + expect(value()).toBe(10) + second.set(11) + fixture.detectChanges() + expect(value()).toBe(11) + expect(first.cleanup).toHaveBeenCalledOnce() + first.set(2) + expect(value()).toBe(11) + second.set(12) + expect(value()).toBe(12) + }) + + it('releases observation while paused and catches up on resume', () => { + const source = store(1) + const paused = signal(false) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: source.getSnapshot, + subscribe: paused() ? undefined : source.subscribe, + })), + ) + fixture.detectChanges() + paused.set(true) + fixture.detectChanges() + expect(source.cleanup).toHaveBeenCalledOnce() + source.set(2) + paused.set(false) + fixture.detectChanges() + expect(fixture.componentInstance.value()).toBe(2) + source.set(3) + expect(fixture.componentInstance.value()).toBe(3) + }) + + it('allows subscription setup and cleanup to read the result', () => { + const source = store(1) + const enabled = signal(true) + const seen: Array = [] + const fixture = host(() => { + const value = injectExternalStore(() => ({ + getSnapshot: source.getSnapshot, + subscribe: enabled() + ? (notify: () => void) => { + seen.push(value()) + const cleanup = source.subscribe(notify) + return () => { + seen.push(value()) + cleanup() + } + } + : undefined, + })) + return value + }) + fixture.detectChanges() + enabled.set(false) + fixture.detectChanges() + expect(seen).toEqual([1, 1]) + }) + + it('keeps snapshot errors separate from subscription errors', () => { + const source = store(1) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: source.getSnapshot, + subscribe: () => { + source.set(2) + throw new Error('subscribe failed') + }, + })), + ) + expect(() => fixture.detectChanges()).toThrow('subscribe failed') + expect(fixture.componentInstance.value()).toBe(2) + }) + + it('recovers from a snapshot error on the next store notification', () => { + const source = store(1) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: () => { + if (source.getSnapshot() === 2) throw new Error('snapshot failed') + return source.getSnapshot() + }, + subscribe: source.subscribe, + })), + ) + fixture.detectChanges() + source.set(2) + expect(() => fixture.componentInstance.value()).toThrow('snapshot failed') + source.set(3) + expect(fixture.componentInstance.value()).toBe(3) + expect(source.subscribe).toHaveBeenCalledOnce() + }) + + it.each([false, true])( + 'releases subscriptions on destruction (during setup: %s)', + (duringSetup) => { + const source = store(1) + const fixture = host(() => + injectExternalStore(() => ({ + getSnapshot: source.getSnapshot, + subscribe: (notify) => { + const cleanup = source.subscribe(notify) + if (duringSetup) fixture.destroy() + return cleanup + }, + })), + ) + fixture.detectChanges() + if (!duringSetup) fixture.destroy() + expect(source.cleanup).toHaveBeenCalledOnce() + }, + ) + + it('renders required inputs and changes made in ngOnInit', () => { + const source = store(1) + @Component({ template: '{{ value() }}' }) + class Example { + readonly source = input.required>() + readonly value = injectExternalStore(() => this.source()) + ngOnInit() { + expect(this.value()).toBe(1) + this.source().set(2) + } + } + const fixture = TestBed.createComponent(Example, { + bindings: [inputBinding('source', () => source)], + }) + fixture.detectChanges() + expect(fixture.nativeElement.textContent).toBe('2') + source.set(3) + fixture.detectChanges() + expect(fixture.nativeElement.textContent).toBe('3') + }) +}) diff --git a/packages/angular-query/src/utils/inject-external-store.ts b/packages/angular-query/src/utils/inject-external-store.ts new file mode 100644 index 00000000000..a7bc3aa5641 --- /dev/null +++ b/packages/angular-query/src/utils/inject-external-store.ts @@ -0,0 +1,88 @@ +import { + DestroyRef, + assertInInjectionContext, + computed, + effect, + inject, + signal, + untracked, +} from '@angular/core' +import { injectQueryZone } from './inject-query-zone' +import type { Signal, ValueEqualityFn } from '@angular/core' + +interface ExternalBinding { + /** Synchronous snapshot. Angular signals read here are tracked dependencies. */ + readonly getSnapshot: () => T + /** Omit to pause observation; changing eligibility requires a new descriptor. */ + readonly subscribe?: ((notify: () => void) => () => void) | undefined +} + +interface ExternalStoreOptions { + /** Snapshot equality, defaulting to Object.is. Does not control connection identity. */ + readonly equal?: ValueEqualityFn | undefined +} + +/** + * Creates a readonly Angular signal backed by a synchronous external store. + * + * Store access, subscription setup, and cleanup run outside NgZone so background + * timers do not hold Angular stability open. + * + * Reads evaluate snapshots without subscribing. An effect owns the subscription + * and invalidates the snapshot after setup, closing the gap between an early + * read and connection. The next read recomputes the snapshot; equality prevents + * unchanged values from propagating. Unread snapshots stay lazy. + * + * Notifications invalidate snapshots synchronously once connected. Snapshot-only + * dependencies do not reinstall the subscription. Subscription setup and cleanup + * may read the returned signal; factories and snapshot readers must not recurse. + * + * Only factory/snapshot errors are exposed on reads. Subscription and cleanup + * errors follow Angular's normal effect error handling; the helper does not store + * them or implement retries. Destruction stops observation without changing the + * cached snapshot. + */ +export function injectExternalStore( + binding: () => ExternalBinding, + options?: ExternalStoreOptions, +): Signal { + if (typeof ngDevMode === 'undefined' || ngDevMode) { + assertInInjectionContext(injectExternalStore) + } + const owner = inject(DestroyRef) + const outsideZone = injectQueryZone() + const revision = signal(0) + const requested = computed(() => outsideZone(binding)) + const invalidate = () => untracked(() => revision.update((n) => n + 1)) + + effect((onCleanup) => { + // The previous subscription's cleanup may have destroyed the owner. + if (owner.destroyed) return + const current = requested() + outsideZone(() => + untracked(() => { + try { + const unsubscribe = current.subscribe?.(invalidate) + if (unsubscribe) { + // subscribe() can trigger component/injector destruction before returning. + // Registering cleanup afterward misses that destruction. + if (owner.destroyed) unsubscribe() + else onCleanup(() => outsideZone(unsubscribe)) + } + } finally { + // Invalidate after subscribing so an early cached snapshot cannot miss + // changes before or during setup. Do not eagerly read the snapshot. + invalidate() + } + }), + ) + }) + + return computed( + () => { + revision() + return outsideZone(() => requested().getSnapshot()) + }, + options?.equal ? { equal: options.equal } : undefined, + ) +} diff --git a/packages/angular-query/src/utils/inject-pending-tasks-lifecycle.ts b/packages/angular-query/src/utils/inject-pending-tasks-lifecycle.ts new file mode 100644 index 00000000000..866d256d4f2 --- /dev/null +++ b/packages/angular-query/src/utils/inject-pending-tasks-lifecycle.ts @@ -0,0 +1,41 @@ +import { DestroyRef, NgZone, PendingTasks, inject } from '@angular/core' + +export interface QueryLifecycle { + readonly destroyed: boolean + setPending: (pending: boolean) => void +} + +/** Tracks pending work for the lifetime of the current injection context. */ +export function injectPendingTasksLifecycle(): QueryLifecycle { + const destroyRef = inject(DestroyRef) + const pendingTasks = inject(PendingTasks) + const ngZone = inject(NgZone) + let taskCleanup: (() => void) | undefined + + const lifecycle: QueryLifecycle = { + get destroyed() { + return destroyRef.destroyed + }, + setPending(pending) { + if (pending) { + if (!destroyRef.destroyed && !taskCleanup) { + taskCleanup = pendingTasks.add() + } + return + } + + const cleanup = taskCleanup + taskCleanup = undefined + // Enter NgZone while our task is still held to avoid transient stability + // before dependent queries are scheduled. + // In zoneless apps, NgZone.run simply invokes the cleanup. + if (cleanup) ngZone.run(cleanup) + }, + } + + destroyRef.onDestroy(() => { + lifecycle.setPending(false) + }) + + return lifecycle +} diff --git a/packages/angular-query/src/utils/inject-query-zone.ts b/packages/angular-query/src/utils/inject-query-zone.ts new file mode 100644 index 00000000000..1d14f7eb049 --- /dev/null +++ b/packages/angular-query/src/utils/inject-query-zone.ts @@ -0,0 +1,7 @@ +import { NgZone, inject } from '@angular/core' + +/** Keep Query's background timers out of Angular's stability tracking. */ +export function injectQueryZone() { + const ngZone = inject(NgZone) + return (fn: () => T): T => ngZone.runOutsideAngular(fn) +} diff --git a/packages/angular-query/src/utils/resource-types.ts b/packages/angular-query/src/utils/resource-types.ts new file mode 100644 index 00000000000..7ab266e929e --- /dev/null +++ b/packages/angular-query/src/utils/resource-types.ts @@ -0,0 +1,10 @@ +import type { ResourceStatus } from '@angular/core' + +// Angular 20 does not expose ResourceSnapshot. Remove this local type when the +// minimum supported Angular version provides it. +export type ResourceSnapshot = + | { readonly status: 'error'; readonly error: Error } + | { + readonly status: Exclude + readonly value: T + } diff --git a/packages/angular-query/src/utils/result-fields.ts b/packages/angular-query/src/utils/result-fields.ts new file mode 100644 index 00000000000..598ac07b4e7 --- /dev/null +++ b/packages/angular-query/src/utils/result-fields.ts @@ -0,0 +1,73 @@ +import type { + InfiniteQueryObserverResult, + MutationObserverResult, + QueryObserverResult, +} from '@tanstack/query-core' +import type { MethodKeys } from './signal-proxy' + +type Fields = { [K in Exclude>]: true } + +// Exhaustive maps keep the field surface aligned with query-core at compile time. +const queryFields = { + data: true, + dataUpdatedAt: true, + error: true, + errorUpdatedAt: true, + failureCount: true, + failureReason: true, + errorUpdateCount: true, + isError: true, + isFetched: true, + isFetchedAfterMount: true, + isFetching: true, + isLoading: true, + isPending: true, + isLoadingError: true, + isInitialLoading: true, + isPaused: true, + isPlaceholderData: true, + isRefetchError: true, + isRefetching: true, + isStale: true, + isSuccess: true, + isEnabled: true, + status: true, + fetchStatus: true, +} satisfies Fields + +const infiniteQueryFields = { + ...queryFields, + hasNextPage: true, + hasPreviousPage: true, + isFetchNextPageError: true, + isFetchingNextPage: true, + isFetchPreviousPageError: true, + isFetchingPreviousPage: true, +} satisfies Fields + +const mutationFields = { + context: true, + data: true, + error: true, + failureCount: true, + failureReason: true, + isPaused: true, + status: true, + variables: true, + submittedAt: true, + isError: true, + isIdle: true, + isPending: true, + isSuccess: true, +} satisfies Fields + +export const queryResultFields = /* @__PURE__ */ Object.keys( + queryFields, +) as Array +export const infiniteQueryResultFields = /* @__PURE__ */ Object.keys( + infiniteQueryFields, +) as Array + +export const mutationResultFields = /* @__PURE__ */ Object.keys( + mutationFields, +) as Array diff --git a/packages/angular-query/src/utils/signal-proxy.ts b/packages/angular-query/src/utils/signal-proxy.ts new file mode 100644 index 00000000000..cc6305bc42d --- /dev/null +++ b/packages/angular-query/src/utils/signal-proxy.ts @@ -0,0 +1,29 @@ +import { computed } from '@angular/core' +import type { Signal } from '@angular/core' + +export type MethodKeys = { + [K in keyof T]: T[K] extends (...args: Array) => any ? K : never +}[keyof T] + +export type MapToSignals = never> = { + [K in keyof T]: K extends TExcludeFields ? T[K] : Signal +} + +/** + * Maps known result fields to lazy computed signals on an ordinary object. + * Creating or enumerating the fields never evaluates the source. Imperative + * methods are defined by each adapter, outside this mapping. + * @param inputSignal - The source snapshot. + * @param fields - The fields exposed as signals. + * @returns An object containing one stable signal per field. + */ +export function signalProxy( + inputSignal: Signal, + fields: ReadonlyArray, +): { [P in TField]: Signal } { + const result = {} as { [P in TField]: Signal } + for (const field of fields) { + result[field] = computed(() => inputSignal()[field]) + } + return result +} diff --git a/packages/angular-query/test-setup.ts b/packages/angular-query/test-setup.ts new file mode 100644 index 00000000000..eb69f3f05a9 --- /dev/null +++ b/packages/angular-query/test-setup.ts @@ -0,0 +1,9 @@ +import '@testing-library/jest-dom/vitest' +import '@angular/compiler' +import { getTestBed } from '@angular/core/testing' +import { + BrowserTestingModule, + platformBrowserTesting, +} from '@angular/platform-browser/testing' + +getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting()) diff --git a/packages/angular-query/test-setup.zoneful.ts b/packages/angular-query/test-setup.zoneful.ts new file mode 100644 index 00000000000..4f24c5e4cf7 --- /dev/null +++ b/packages/angular-query/test-setup.zoneful.ts @@ -0,0 +1,13 @@ +import 'zone.js' +import 'zone.js/testing' +import '@testing-library/jest-dom/vitest' +import '@angular/compiler' +import { getTestBed } from '@angular/core/testing' +import { + BrowserTestingModule, + platformBrowserTesting, +} from '@angular/platform-browser/testing' + +process.env.ANGULAR_QUERY_ZONEFUL = 'true' + +getTestBed().initTestEnvironment(BrowserTestingModule, platformBrowserTesting()) diff --git a/packages/angular-query-experimental/tsconfig.json b/packages/angular-query/tsconfig.json similarity index 78% rename from packages/angular-query-experimental/tsconfig.json rename to packages/angular-query/tsconfig.json index 3699e26689d..46b74afcf24 100644 --- a/packages/angular-query-experimental/tsconfig.json +++ b/packages/angular-query/tsconfig.json @@ -16,11 +16,13 @@ }, "include": [ "src", + "schematics", + "schematics-build", "scripts", "test-setup.ts", - "*.config.ts", - "*.config.js", + "*.config.*", "package.json" ], - "references": [{ "path": "../query-devtools" }] + "exclude": ["src/**/*.tmp.test.ts"], + "references": [{ "path": "../query-core" }] } diff --git a/packages/angular-query-experimental/tsconfig.prod.json b/packages/angular-query/tsconfig.prod.json similarity index 100% rename from packages/angular-query-experimental/tsconfig.prod.json rename to packages/angular-query/tsconfig.prod.json diff --git a/packages/angular-query/tsconfig.schematics.json b/packages/angular-query/tsconfig.schematics.json new file mode 100644 index 00000000000..470c02914cd --- /dev/null +++ b/packages/angular-query/tsconfig.schematics.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "declaration": false, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "lib": ["ES2022"], + "module": "CommonJS", + "moduleResolution": "Node", + "ignoreDeprecations": "6.0", + "noEmitOnError": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist/schematics", + "rootDir": "schematics", + "skipLibCheck": true, + "strict": true, + "target": "ES2022", + "types": ["node"] + }, + "include": ["schematics/**/*.ts"] +} diff --git a/packages/angular-query/tsconfig.spec.json b/packages/angular-query/tsconfig.spec.json new file mode 100644 index 00000000000..c70b776b257 --- /dev/null +++ b/packages/angular-query/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": ["vitest/globals", "node"], + "noEmit": false, + "emitDeclarationOnly": false, + "declaration": false, + "declarationMap": false, + "composite": false, + "target": "ES2022" + }, + "files": ["test-setup.ts"], + "include": ["src/**/*.test.ts", "../query-test-utils/src/**/*.ts"], + "exclude": ["src/**/*.test-d.ts", "src/**/*.tmp.test.ts"] +} diff --git a/packages/angular-query-experimental/vite.config.ts b/packages/angular-query/vite.config.ts similarity index 69% rename from packages/angular-query-experimental/vite.config.ts rename to packages/angular-query/vite.config.ts index d5337c94ea6..5cfbcd37b9f 100644 --- a/packages/angular-query-experimental/vite.config.ts +++ b/packages/angular-query/vite.config.ts @@ -1,8 +1,7 @@ -import { defineConfig, mergeConfig } from 'vitest/config' +import { defineConfig, mergeConfig } from 'vite' import { externalizeDeps } from 'vite-plugin-externalize-deps' import tsconfigPaths from 'vite-tsconfig-paths' import dts from 'vite-plugin-dts' -import packageJson from './package.json' import type { Options } from '@tanstack/vite-config' function ensureImportFileExtension({ @@ -14,14 +13,20 @@ function ensureImportFileExtension({ }) { // replace e.g. `import { foo } from './foo'` with `import { foo } from './foo.js'` content = content.replace( - /(im|ex)port\s[\w{}/*\s,]+from\s['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm, - `$&.${extension}`, + /(im|ex)port\s[\w{}/*\s,]+from\s['"](?:\.\.?\/)+?[^'"]+(?=['"];?)/gm, + (specifier) => + /\.(?:[cm]?js|json)$/.test(specifier) + ? specifier + : `${specifier}.${extension}`, ) // replace e.g. `import('./foo')` with `import('./foo.js')` content = content.replace( - /import\(['"](?:\.\.?\/)+?[^.'"]+(?=['"];?)/gm, - `$&.${extension}`, + /import\(['"](?:\.\.?\/)+?[^'"]+(?=['"];?)/gm, + (specifier) => + /\.(?:[cm]?js|json)$/.test(specifier) + ? specifier + : `${specifier}.${extension}`, ) return content } @@ -31,29 +36,6 @@ const config = defineConfig({ resolve: { conditions: ['@tanstack/custom-condition'], }, - environments: { - ssr: { - resolve: { - conditions: ['@tanstack/custom-condition'], - }, - }, - }, - test: { - name: packageJson.name, - dir: './src', - watch: false, - environment: 'jsdom', - setupFiles: ['test-setup.ts'], - coverage: { - enabled: !!process.env.CI, - provider: 'istanbul', - include: ['src/**/*'], - exclude: ['src/__tests__/**'], - }, - typecheck: { enabled: true }, - globals: true, - restoreMocks: true, - }, }) // copy from @tanstack/config/vite with changes: @@ -116,15 +98,8 @@ export default mergeConfig( config, tanstackViteConfig({ cjs: false, - entry: [ - './src/index.ts', - './src/inject-queries-experimental/index.ts', - './src/devtools-panel/index.ts', - './src/devtools-panel/stub.ts', - './src/devtools/index.ts', - './src/devtools/stub.ts', - ], - exclude: ['src/__tests__'], + entry: ['./src/index.ts', './src/internal.ts'], + exclude: ['src/**/__tests__/**', 'src/**/__test__/**'], srcDir: './src', tsconfigPath: 'tsconfig.prod.json', }), diff --git a/packages/angular-query/vitest.config.ts b/packages/angular-query/vitest.config.ts new file mode 100644 index 00000000000..da0e57d9dd9 --- /dev/null +++ b/packages/angular-query/vitest.config.ts @@ -0,0 +1,57 @@ +import { defineConfig } from 'vitest/config' +import angular from '@analogjs/vite-plugin-angular' +import packageJson from './package.json' with { type: 'json' } + +export default defineConfig({ + // fix from https://github.com/vitest-dev/vitest/issues/6992#issuecomment-2509408660 + resolve: { + conditions: ['@tanstack/custom-condition'], + }, + environments: { + ssr: { + resolve: { + conditions: ['@tanstack/custom-condition'], + }, + }, + }, + esbuild: { + target: 'es2022', + }, + plugins: [ + angular({ + tsconfig: './tsconfig.spec.json', + // Vitest sets VITEST; Analog defaults jit: true, which skips ngtsc transforms for + // signal inputs so inputBinding() fails (NG0315). jit: false needs compiler-cli on + // the same TypeScript 5.9 version as the plugin to avoid mixed compiler ASTs. + jit: false, + }), + ], + test: { + name: packageJson.name, + dir: './src', + watch: false, + environment: 'jsdom', + setupFiles: ['./test-setup.ts'], + coverage: { + enabled: !!process.env.CI, + provider: 'istanbul', + include: ['src/**/*'], + exclude: ['src/__tests__/**'], + }, + include: ['**/*.{test,spec}.{ts,mts,cts,tsx,js,mjs,cjs,jsx}'], + // The focused Zone.js project opts this file back in. The default project + // must not load provideZoneChangeDetection without the Zone.js setup file. + exclude: [ + '**/zoneful-integration.test.ts', + '**/*.zoneful.test.ts', + '**/*.tmp.test.ts', + ], + typecheck: { + enabled: true, + include: ['**/*.test-d.ts'], + exclude: ['**/*.tmp.test.ts'], + }, + globals: true, + restoreMocks: true, + }, +}) diff --git a/packages/angular-query/vitest.schematics.config.ts b/packages/angular-query/vitest.schematics.config.ts new file mode 100644 index 00000000000..b1f64767770 --- /dev/null +++ b/packages/angular-query/vitest.schematics.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config' +import packageJson from './package.json' with { type: 'json' } + +export default defineConfig({ + test: { + name: `${packageJson.name}:schematics`, + environment: 'node', + include: ['./schematics-build/**/*.test.ts'], + watch: false, + }, +}) diff --git a/packages/angular-query/vitest.zoneful.config.ts b/packages/angular-query/vitest.zoneful.config.ts new file mode 100644 index 00000000000..ba55e0e73d5 --- /dev/null +++ b/packages/angular-query/vitest.zoneful.config.ts @@ -0,0 +1,34 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from './vitest.config' + +/** + * Runs the Angular tests with Zone.js enabled. Tests use the change-detection + * provider from test-utils, which selects provideZoneChangeDetection here and + * provideZonelessChangeDetection in the default runner. + */ +const config = mergeConfig( + baseConfig, + defineConfig({ + test: { + setupFiles: ['./test-setup.zoneful.ts'], + }, + }), +) + +// The base project excludes the Zone.js-only integration file so an ordinary +// run remains genuinely zoneless. The Zone.js project explicitly opts it in. +if (config.test) { + // Replace, rather than merge with, the base setup so Zone.js is installed + // before Angular initializes its browser testing environment. + config.test.setupFiles = ['./test-setup.zoneful.ts'] + config.test.include = [ + '**/zoneful-integration.test.ts', + '**/pending-tasks.test.ts', + '**/pending-tasks-ssr.zoneful.test.ts', + '**/upstream-issues.test.ts', + '**/inject-external-store.test.ts', + ] + config.test.exclude = [] +} + +export default config diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e178d098bb..20b7413c879 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: + '@inquirer/external-editor': 3.0.5 chokidar: 5.0.0 '@types/react': ^19.2.7 '@types/react-dom': ^19.2.3 @@ -22,6 +23,7 @@ overrides: '@typescript-eslint/visitor-keys': 8.58.1 typescript-eslint: 8.58.1 vite: ^6.4.1 + '@angular/build@22.1.8>vite': 7.1.11 esbuild: ^0.27.2 importers: @@ -54,7 +56,7 @@ importers: version: 0.3.1(typescript@6.0.3) '@tanstack/vite-config': specifier: 0.4.3 - version: 0.4.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 0.4.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@testing-library/jest-dom': specifier: ^6.8.0 version: 6.9.1 @@ -69,10 +71,10 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitest/coverage-istanbul': specifier: 4.0.6 - version: 4.0.6(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 4.0.6(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@vitest/eslint-plugin': specifier: ^1.4.0 - version: 1.6.14(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 1.6.14(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) eslint: specifier: ^9.36.0 version: 9.39.4(jiti@2.7.0) @@ -141,28 +143,31 @@ importers: version: typescript@7.0.2 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vitest: specifier: ^4.0.18 - version: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/angular/auto-refetching: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -170,39 +175,42 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(52d17f587f491aa5bd2e2175a98e7579) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/basic: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -210,44 +218,47 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/basic-persister: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools '@tanstack/angular-query-persist-client': - specifier: ^5.103.1 + specifier: workspace:* version: link:../../../packages/angular-query-persist-client '@tanstack/query-async-storage-persister': - specifier: ^5.103.1 + specifier: ^5.100.14 version: link:../../../packages/query-async-storage-persister rxjs: specifier: ^7.8.2 @@ -256,42 +267,45 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/devtools-panel: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -299,39 +313,128 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 + + examples/angular/dynamic-devtools: + dependencies: + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: 0.16.1 + version: 0.16.1 + devDependencies: + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) + '@angular/cli': + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + typescript: + specifier: 6.0.3 + version: 6.0.3 + + examples/angular/dynamic-queries: + dependencies: + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: 0.16.1 + version: 0.16.1 + devDependencies: + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) + '@angular/cli': + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + typescript: + specifier: 6.0.3 + version: 6.0.3 examples/angular/infinite-query-with-max-pages: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -339,42 +442,45 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/optimistic-updates: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -382,39 +488,42 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/pagination: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -422,42 +531,45 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/query-options-from-a-service: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -465,42 +577,45 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/router: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -508,42 +623,45 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/rxjs: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -551,39 +669,42 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 examples/angular/simple: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -591,21 +712,183 @@ importers: specifier: ^2.8.1 version: 2.8.1 zone.js: - specifier: 0.15.0 - version: 0.15.0 + specifier: 0.16.1 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: 5.8.3 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 + + examples/angular/ssr: + dependencies: + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/ssr': + specifier: ^22.0.0 + version: 22.1.8(caf12b5b30d21bb6295944d46eee472d) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools + express: + specifier: ^5.1.0 + version: 5.2.1 + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: 0.16.1 + version: 0.16.1 + devDependencies: + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) + '@angular/cli': + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@types/express': + specifier: ^5.0.1 + version: 5.0.6 + typescript: + specifier: 6.0.3 + version: 6.0.3 + + examples/angular/ssr-persist: + dependencies: + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/ssr': + specifier: ^22.0.0 + version: 22.1.8(caf12b5b30d21bb6295944d46eee472d) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools + '@tanstack/angular-query-persist-client': + specifier: workspace:* + version: link:../../../packages/angular-query-persist-client + '@tanstack/query-async-storage-persister': + specifier: workspace:* + version: link:../../../packages/query-async-storage-persister + express: + specifier: ^5.1.0 + version: 5.2.1 + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: 0.16.1 + version: 0.16.1 + devDependencies: + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) + '@angular/cli': + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@types/express': + specifier: ^5.0.1 + version: 5.0.6 + typescript: + specifier: 6.0.3 + version: 6.0.3 + + examples/angular/to-resource-signal-forms: + dependencies: + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/forms': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../../packages/angular-query-devtools + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + tslib: + specifier: ^2.8.1 + version: 2.8.1 + zone.js: + specifier: 0.16.1 + version: 0.16.1 + devDependencies: + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) + '@angular/cli': + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + typescript: + specifier: 6.0.3 + version: 6.0.3 examples/lit/basic: dependencies: @@ -624,7 +907,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/lit/pagination: dependencies: @@ -643,7 +926,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/lit/ssr: dependencies: @@ -671,7 +954,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/preact/simple: dependencies: @@ -684,7 +967,7 @@ importers: devDependencies: '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) eslint: specifier: ^9.36.0 version: 9.39.4(jiti@2.7.0) @@ -696,7 +979,7 @@ importers: version: 5.9.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/algolia: dependencies: @@ -727,13 +1010,13 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/auto-refetching: dependencies: @@ -745,7 +1028,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -795,13 +1078,13 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/basic-graphql-request: dependencies: @@ -826,10 +1109,10 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/batching: dependencies: @@ -860,13 +1143,13 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/chat: dependencies: @@ -885,10 +1168,10 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.0.14 - version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) tailwindcss: specifier: ^4.0.14 version: 4.2.2 @@ -897,7 +1180,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/default-query-function: dependencies: @@ -916,13 +1199,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/devtools-panel: dependencies: @@ -941,13 +1224,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/eslint-legacy: dependencies: @@ -981,13 +1264,13 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/eslint-plugin-demo: dependencies: @@ -1021,7 +1304,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1049,7 +1332,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1080,7 +1363,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1108,7 +1391,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1139,7 +1422,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1170,7 +1453,7 @@ importers: version: link:../../../packages/react-query-next-experimental next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1220,13 +1503,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/pagination: dependencies: @@ -1238,7 +1521,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1273,13 +1556,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/prefetching: dependencies: @@ -1291,7 +1574,7 @@ importers: version: link:../../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -1414,13 +1697,13 @@ importers: version: 1.2.3 '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/rick-morty: dependencies: @@ -1445,10 +1728,10 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.1.13 - version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) tailwindcss: specifier: ^4.1.13 version: 4.2.2 @@ -1457,7 +1740,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/shadow-dom: dependencies: @@ -1482,13 +1765,13 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/simple: dependencies: @@ -1507,13 +1790,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/star-wars: dependencies: @@ -1538,10 +1821,10 @@ importers: devDependencies: '@tailwindcss/vite': specifier: ^4.1.13 - version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) tailwindcss: specifier: ^4.1.13 version: 4.2.2 @@ -1550,7 +1833,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/react/suspense: dependencies: @@ -1575,13 +1858,13 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/solid/astro: dependencies: @@ -1590,16 +1873,16 @@ importers: version: 0.9.8(prettier@3.8.1)(typescript@5.8.3) '@astrojs/node': specifier: ^9.1.3 - version: 9.5.5(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0)) + version: 9.5.5(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0)) '@astrojs/solid-js': specifier: ^5.0.7 - version: 5.1.3(@testing-library/jest-dom@6.9.1)(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(solid-js@1.9.12)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 5.1.3(@testing-library/jest-dom@6.9.1)(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(solid-js@1.9.12)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)) + version: 6.0.2(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0)) '@astrojs/vercel': specifier: ^8.1.3 - version: 8.2.11(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0))(react@19.2.4)(rollup@4.60.1)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3)) + version: 8.2.11(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0))(react@19.2.4)(rollup@4.60.1)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3)) '@tanstack/solid-query': specifier: ^5.103.1 version: link:../../../packages/solid-query @@ -1608,7 +1891,7 @@ importers: version: link:../../../packages/solid-query-devtools astro: specifier: ^5.5.6 - version: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) + version: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) solid-js: specifier: ^1.9.7 version: 1.9.12 @@ -1636,10 +1919,10 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/solid/basic-graphql-request: dependencies: @@ -1664,10 +1947,10 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/solid/default-query-function: dependencies: @@ -1686,10 +1969,10 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/solid/offline: dependencies: @@ -1717,10 +2000,10 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/solid/simple: dependencies: @@ -1742,10 +2025,10 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) examples/solid/solid-start-streaming: dependencies: @@ -1757,7 +2040,7 @@ importers: version: 0.15.4(solid-js@1.9.12) '@solidjs/start': specifier: ^1.1.3 - version: 1.3.2(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 1.3.2(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/solid-query': specifier: ^5.103.1 version: link:../../../packages/solid-query @@ -1769,7 +2052,7 @@ importers: version: 1.9.12 vinxi: specifier: ^0.5.3 - version: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) + version: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) examples/svelte/auto-refetching: dependencies: @@ -1782,13 +2065,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1800,7 +2083,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/basic: dependencies: @@ -1819,13 +2102,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1837,7 +2120,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/load-more-infinite-scroll: dependencies: @@ -1850,13 +2133,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1868,7 +2151,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/optimistic-updates: dependencies: @@ -1881,13 +2164,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1899,7 +2182,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/playground: dependencies: @@ -1912,13 +2195,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1930,7 +2213,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/simple: dependencies: @@ -1943,7 +2226,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tsconfig/svelte': specifier: ^5.0.4 version: 5.0.8 @@ -1958,7 +2241,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/ssr: dependencies: @@ -1971,13 +2254,13 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -1989,7 +2272,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/svelte/star-wars: dependencies: @@ -2002,16 +2285,16 @@ importers: devDependencies: '@sveltejs/adapter-auto': specifier: ^6.1.0 - version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.57.1 - version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tailwindcss/vite': specifier: ^4.1.13 - version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) svelte: specifier: ^5.39.3 version: 5.55.1 @@ -2026,7 +2309,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/vue/basic: dependencies: @@ -2042,13 +2325,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.1 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/vue/dependent-queries: dependencies: @@ -2061,13 +2344,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.1 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/vue/persister: dependencies: @@ -2092,13 +2375,13 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.1 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) examples/vue/simple: dependencies: @@ -2114,37 +2397,40 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.1 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - integrations/angular-cli-20: + integrations/angular-cli: dependencies: '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/forms': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) '@angular/router': - specifier: ^20.0.0 - version: 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@tanstack/angular-query-experimental': - specifier: ^5.103.1 - version: link:../../packages/angular-query-experimental + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../../packages/angular-query + '@tanstack/angular-query-devtools': + specifier: workspace:* + version: link:../../packages/angular-query-devtools rxjs: specifier: ~7.8.0 version: 7.8.2 @@ -2152,21 +2438,21 @@ importers: specifier: ^2.3.0 version: 2.8.1 zone.js: - specifier: ~0.15.0 - version: 0.15.0 + specifier: ~0.16.0 + version: 0.16.1 devDependencies: '@angular/build': - specifier: ^20.0.0 - version: 20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0) + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/cli': - specifier: ^20.0.0 - version: 20.3.22(@types/node@22.19.15)(chokidar@5.0.0) + specifier: ^22.0.0 + version: 22.1.8(@types/node@22.19.15)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) typescript: - specifier: ~5.8.2 - version: 5.8.3 + specifier: 6.0.3 + version: 6.0.3 integrations/lit-vite: dependencies: @@ -2181,7 +2467,7 @@ importers: version: 3.3.2 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) devDependencies: typescript: specifier: 5.8.3 @@ -2197,7 +2483,7 @@ importers: version: link:../../packages/react-query-devtools next: specifier: ^14.2.33 - version: 14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0) + version: 14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.101.0) react: specifier: ^18.2.0 version: 18.3.1 @@ -2225,7 +2511,7 @@ importers: version: link:../../packages/react-query-devtools next: specifier: ^15.4.8 - version: 15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -2256,7 +2542,7 @@ importers: version: link:../../packages/react-query-devtools next: specifier: ^16.0.7 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: specifier: ^19.2.1 version: 19.2.4 @@ -2294,7 +2580,7 @@ importers: version: link:../../packages/react-query-devtools '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) react: specifier: ^19.0.0 version: 19.2.4 @@ -2303,7 +2589,7 @@ importers: version: 19.2.4(react@19.2.4) vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) integrations/react-webpack-4: dependencies: @@ -2395,16 +2681,16 @@ importers: version: 1.9.12 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) integrations/svelte-vite: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/svelte-query': specifier: workspace:* version: link:../../packages/svelte-query @@ -2416,7 +2702,7 @@ importers: version: 5.55.1 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) integrations/vue-declaration-emit: dependencies: @@ -2442,60 +2728,145 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.1 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) typescript: specifier: 5.8.3 version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vue-tsc: specifier: ^2.2.8 version: 2.2.12(typescript@5.8.3) - packages/angular-query-experimental: + packages/angular-query: dependencies: '@tanstack/query-core': specifier: workspace:* version: link:../query-core devDependencies: + '@analogjs/vite-plugin-angular': + specifier: ^2.3.1 + version: 2.5.2(@angular/build@22.1.8(4affaef227d0997be37379ba361cd6fd))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@angular-devkit/schematics': + specifier: ^22.0.0 + version: 22.1.8(chokidar@5.0.0) + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(4affaef227d0997be37379ba361cd6fd) '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/router': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@schematics/angular': + specifier: ^22.0.0 + version: 22.1.8(chokidar@5.0.0) '@tanstack/query-test-utils': specifier: workspace:* version: link:../query-test-utils '@testing-library/angular': - specifier: ^18.0.0 - version: 18.1.1(3436010aec00f8e828978cdcb451dc36) + specifier: ^18.1.1 + version: 18.1.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2))(@testing-library/dom@10.4.1) npm-run-all2: specifier: ^5.0.0 version: 5.0.2 rxjs: specifier: ^7.8.2 version: 7.8.2 + typescript: + specifier: 6.0.3 + version: 6.0.3 vite-plugin-dts: specifier: 4.2.3 - version: 4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) vite-plugin-externalize-deps: specifier: ^0.9.0 - version: 0.9.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 0.9.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - optionalDependencies: + version: 5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + zone.js: + specifier: ^0.16.0 + version: 0.16.1 + publishDirectory: dist + + packages/angular-query-devtools: + dependencies: + '@tanstack/query-core': + specifier: workspace:* + version: link:../query-core '@tanstack/query-devtools': specifier: workspace:* version: link:../query-devtools + devDependencies: + '@analogjs/vite-plugin-angular': + specifier: ^2.3.1 + version: 2.5.2(@angular/build@22.1.8(e62ab7c722b8b89e816de2e21b02397f))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@angular/build': + specifier: ^22.0.0 + version: 22.1.8(e62ab7c722b8b89e816de2e21b02397f) + '@angular/common': + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': + specifier: ^22.0.0 + version: 22.1.6 + '@angular/compiler-cli': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@5.9.3) + '@angular/core': + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@tanstack/angular-query': + specifier: workspace:* + version: link:../angular-query + '@testing-library/jest-dom': + specifier: ^6.8.0 + version: 6.9.1 + npm-run-all2: + specifier: ^5.0.0 + version: 5.0.2 + rxjs: + specifier: ^7.8.2 + version: 7.8.2 + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite-plugin-dts: + specifier: 4.2.3 + version: 4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite-plugin-externalize-deps: + specifier: ^0.9.0 + version: 0.9.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite-tsconfig-paths: + specifier: ^5.1.4 + version: 5.1.4(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: + specifier: ^4.0.18 + version: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + zone.js: + specifier: ^0.16.0 + version: 0.16.1 publishDirectory: dist packages/angular-query-persist-client: @@ -2504,30 +2875,30 @@ importers: specifier: workspace:* version: link:../query-persist-client-core devDependencies: - '@angular/animations': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^20.0.0 - version: 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.0 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^20.0.0 - version: 20.3.18 + specifier: ^22.0.0 + version: 22.1.6 '@angular/core': - specifier: ^20.0.0 - version: 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.0 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) '@angular/platform-browser': - specifier: ^20.0.0 - version: 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@tanstack/angular-query-experimental': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/router': + specifier: ^22.0.0 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@tanstack/angular-query': specifier: workspace:* - version: link:../angular-query-experimental + version: link:../angular-query '@tanstack/query-test-utils': specifier: workspace:* version: link:../query-test-utils '@testing-library/angular': - specifier: ^18.0.0 - version: 18.1.1(3436010aec00f8e828978cdcb451dc36) + specifier: ^18.1.1 + version: 18.1.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2))(@testing-library/dom@10.4.1) '@testing-library/dom': specifier: ^10.4.0 version: 10.4.1 @@ -2537,6 +2908,12 @@ importers: npm-run-all2: specifier: ^5.0.0 version: 5.0.2 + typescript: + specifier: 5.9.3 + version: 5.9.3 + zone.js: + specifier: ^0.16.0 + version: 0.16.1 packages/eslint-plugin-query: dependencies: @@ -2596,7 +2973,7 @@ importers: devDependencies: '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.10.5(@babel/core@8.0.1)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/query-persist-client-core': specifier: workspace:* version: link:../query-persist-client-core @@ -2636,7 +3013,7 @@ importers: devDependencies: '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.10.5(@babel/core@8.0.1)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/preact-query': specifier: workspace:* version: link:../preact-query @@ -2667,7 +3044,7 @@ importers: devDependencies: '@preact/preset-vite': specifier: ^2.10.2 - version: 2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.10.5(@babel/core@8.0.1)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/preact-query': specifier: workspace:* version: link:../preact-query @@ -2726,7 +3103,7 @@ importers: version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) npm-run-all2: specifier: ^5.0.0 version: 5.0.2 @@ -2738,7 +3115,7 @@ importers: version: 17.3.0 jscodeshift: specifier: 17.3.0 - version: 17.3.0(@babel/preset-env@7.29.2(@babel/core@7.29.7)) + version: 17.3.0(@babel/preset-env@7.29.2(@babel/core@8.0.1)) packages/query-core: devDependencies: @@ -2793,10 +3170,10 @@ importers: version: 5.0.2 unplugin-solid: specifier: ^2.0.0 - version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) packages/query-persist-client-core: dependencies: @@ -2831,7 +3208,7 @@ importers: dependencies: vitest: specifier: ^4.0.18 - version: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) devDependencies: npm-run-all2: specifier: ^5.0.0 @@ -2863,7 +3240,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) cpy-cli: specifier: ^5.0.0 version: 5.0.0 @@ -2897,7 +3274,7 @@ importers: version: 19.2.14 '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) npm-run-all2: specifier: ^5.0.0 version: 5.0.2 @@ -2915,10 +3292,10 @@ importers: version: 19.2.14 '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) next: specifier: ^16.0.1 - version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + version: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) npm-run-all2: specifier: ^5.0.0 version: 5.0.2 @@ -2946,7 +3323,7 @@ importers: version: 19.2.14 '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) npm-run-all2: specifier: ^5.0.0 version: 5.0.2 @@ -2974,10 +3351,10 @@ importers: version: 1.9.12 unplugin-solid: specifier: ^2.0.0 - version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) packages/solid-query-devtools: dependencies: @@ -2999,10 +3376,10 @@ importers: version: 1.9.12 unplugin-solid: specifier: ^2.0.0 - version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) packages/solid-query-persist-client: dependencies: @@ -3027,10 +3404,10 @@ importers: version: 1.9.12 unplugin-solid: specifier: ^2.0.0 - version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + version: 2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) packages/svelte-query: dependencies: @@ -3043,13 +3420,13 @@ importers: version: 2.5.8(svelte@5.55.1)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/query-test-utils': specifier: workspace:* version: link:../query-test-utils '@testing-library/svelte': specifier: ^5.2.8 - version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@typescript-eslint/parser': specifier: 8.58.1 version: 8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) @@ -3080,13 +3457,13 @@ importers: version: 2.5.8(svelte@5.55.1)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/svelte-query': specifier: workspace:* version: link:../svelte-query '@testing-library/svelte': specifier: ^5.2.8 - version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@typescript-eslint/parser': specifier: 8.58.1 version: 8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) @@ -3114,7 +3491,7 @@ importers: version: 2.5.8(svelte@5.55.1)(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: ^5.1.1 - version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + version: 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@tanstack/query-test-utils': specifier: workspace:* version: link:../query-test-utils @@ -3123,7 +3500,7 @@ importers: version: link:../svelte-query '@testing-library/svelte': specifier: ^5.2.8 - version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) + version: 5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))) '@typescript-eslint/parser': specifier: 8.58.1 version: 8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) @@ -3160,7 +3537,7 @@ importers: version: link:../query-test-utils '@vitejs/plugin-vue': specifier: ^5.2.4 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@7.0.2)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@7.0.2)) '@vue/composition-api': specifier: 1.7.2 version: 1.7.2(vue@3.5.31(typescript@7.0.2)) @@ -3188,7 +3565,7 @@ importers: version: link:../vue-query '@vitejs/plugin-vue': specifier: ^5.2.4 - version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) + version: 5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3)) eslint-plugin-vue: specifier: ^10.5.0 version: 10.8.0(@stylistic/eslint-plugin@5.10.0(eslint@9.39.4(jiti@2.7.0)))(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.4(jiti@2.7.0))(vue-eslint-parser@10.4.0(eslint@9.39.4(jiti@2.7.0))) @@ -3197,7 +3574,7 @@ importers: version: 5.8.3 vite: specifier: ^6.4.1 - version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + version: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vue: specifier: ^3.4.27 version: 3.5.31(typescript@5.8.3) @@ -3221,78 +3598,22 @@ packages: '@adobe/css-tools@4.4.4': resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} - '@algolia/abtesting@1.1.0': - resolution: {integrity: sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-abtesting@5.35.0': - resolution: {integrity: sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.35.0': - resolution: {integrity: sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==} - engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.2.1': resolution: {integrity: sha512-f4u3TdqU8310ffGhcCjYtTBXXnVVQ9U38Gnltayxm5Hh63BxgzJzvELUcEuOaZh31CvC1FqttKx127gF90aaQg==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.35.0': - resolution: {integrity: sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.35.0': - resolution: {integrity: sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.35.0': - resolution: {integrity: sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.35.0': - resolution: {integrity: sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==} - engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.2.1': resolution: {integrity: sha512-9os21w5CmC84FZZujufLcZPnYKuTVJ3J6LF4i1/i1BKJzy1Cbb9bTWXn+e9PGdRJBzo4DRZt0D0Pd2X5dvUucg==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.35.0': - resolution: {integrity: sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==} - engines: {node: '>= 14.0.0'} - - '@algolia/ingestion@1.35.0': - resolution: {integrity: sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.35.0': - resolution: {integrity: sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.35.0': - resolution: {integrity: sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==} - engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.2.1': resolution: {integrity: sha512-KRDJRPEdIcej/KgqDqhUs/vhLwVh2oKYa+GaKIFGiyRaWusjwaUYdrWgiLxcHAWNKIeIpwjKnteU/dwCz+Nykg==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.35.0': - resolution: {integrity: sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-fetch@5.35.0': - resolution: {integrity: sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==} - engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.2.1': resolution: {integrity: sha512-Q9LE92zL8EZhe9+H7pwG6mMdU7RNtVrGtheM7lMfZoejpkR/DBTr2/5FCB5OsUk7iLSIwP43AKWX+2rTv0cLaA==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.35.0': - resolution: {integrity: sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==} - engines: {node: '>= 14.0.0'} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -3301,53 +3622,63 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + '@analogjs/vite-plugin-angular@2.5.2': + resolution: {integrity: sha512-zJdmYb6B+qwMzcv9N6fWD4uYarrulqyJSPvNg8yzmVABbRV3YkAQXFMlr6ZgwkV9jSNK5CaqiSQSB238wX7RpA==} + peerDependencies: + '@angular-devkit/build-angular': ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 + '@angular/build': ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 + vite: ^6.4.1 + peerDependenciesMeta: + '@angular-devkit/build-angular': + optional: true + '@angular/build': + optional: true + vite: + optional: true + '@andrewbranch/untar.js@1.0.3': resolution: {integrity: sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==} - '@angular-devkit/architect@0.2003.22': - resolution: {integrity: sha512-gxVOslVweD+Co6gpRVlByHus/3HVAnsl99MobS9PBh8vh2g6bJ011PBgl0TKsP/pqBGawZOkJXYrRPeMKnobYA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular-devkit/architect@0.2201.8': + resolution: {integrity: sha512-EUQo8RDS1my2Bo5FRS+gBYgz1/klfIp9XESfMpTBO04nBkjF6DkPCeOxeAf1CYjWy9nCXcWn968eSdEhV5jXiA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + hasBin: true - '@angular-devkit/core@20.3.22': - resolution: {integrity: sha512-1vZnZTAjGcCM+86v2al+2eiROiSw0uAWeVllfHSQe0KsKOP1FE8UUUiWChhxVn7vIxypphlfGunkeeIn1C/ZFw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular-devkit/core@22.1.8': + resolution: {integrity: sha512-34lsgg2FwMVBX7nR/ZqzRUcdvdYPvSB8XAEr2GudXAyZwLI9ehzfQlagAWR/gEb8p4uCFZW0r7uU0toxydq4Rw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: 5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@20.3.22': - resolution: {integrity: sha512-gN2XSXRn3eErGEJlH0iSfQZZ7NdxVZNdjSxuVEGBEFhe3cVeC21LzM3GTWW6xwtBb4pxHglFyc7BUFiYtZiYtg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - - '@angular/animations@20.3.18': - resolution: {integrity: sha512-XFxgSyjfs0SRD2vQVFJljmM4z9nTvUoI8TRqSre/+l8D2FgzD5pG67Aj2BgDgpSFAUkIcI37G48ijK7a3ZZ3WA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' - peerDependencies: - '@angular/core': 20.3.18 - - '@angular/build@20.3.22': - resolution: {integrity: sha512-sxjVZU6AZHXyKRHJUMawXOj4qMf3vm8XK6wUejr01UKj6BqW2YWaQO26RpRJssXD2ITTqn6+UBwL7pEwe2a4Jg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - '@angular/compiler': ^20.0.0 - '@angular/compiler-cli': ^20.0.0 - '@angular/core': ^20.0.0 - '@angular/localize': ^20.0.0 - '@angular/platform-browser': ^20.0.0 - '@angular/platform-server': ^20.0.0 - '@angular/service-worker': ^20.0.0 - '@angular/ssr': ^20.3.22 + '@angular-devkit/schematics@22.1.8': + resolution: {integrity: sha512-Pv3cPa/44kvEwYcqwx4Ns5dhIDmcFNSHhbpheqiEG1Z/u4e2t4zHKDtE3eHZB+8o+IcC7xJj+d+AqGR44RoZDA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + + '@angular/build@22.1.8': + resolution: {integrity: sha512-tw+Evk0EITb8p8dTu743ONoFztCEiyDkdRbtZsh7C0vBS7Q9ElsOcFeg2Z9OAAwrmtZMIBuW3jrdPRQp7OCSdw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + '@angular/compiler': ^22.0.0 + '@angular/compiler-cli': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/localize': ^22.0.0 + '@angular/platform-browser': ^22.0.0 + '@angular/platform-server': ^22.0.0 + '@angular/service-worker': ^22.0.0 + '@angular/ssr': ^22.1.8 + istanbul-lib-instrument: ^6.0.0 karma: ^6.4.0 less: ^4.2.0 - ng-packagr: ^20.0.0 + ng-packagr: ^22.0.0 postcss: ^8.4.0 + rollup: ^4.0.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 tslib: ^2.3.0 - typescript: '>=5.8 <6.0' - vitest: ^3.1.1 + typescript: '>=6.0 <6.1' + vitest: ^4.0.8 peerDependenciesMeta: '@angular/core': optional: true @@ -3361,6 +3692,8 @@ packages: optional: true '@angular/ssr': optional: true + istanbul-lib-instrument: + optional: true karma: optional: true less: @@ -3369,80 +3702,103 @@ packages: optional: true postcss: optional: true + rollup: + optional: true tailwindcss: optional: true vitest: optional: true - '@angular/cli@20.3.22': - resolution: {integrity: sha512-0uyQPF0gGuzioWJKNyOzWSQrrC5GiidR+8gz1lODoJTnJZZdsP5n3nvccbcRmhy55B1WByHvQBE+6eDBbh06/g==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular/cli@22.1.8': + resolution: {integrity: sha512-MFN/FydqDndai8vF12hDxG5RlBt1HUUheeJgJNmuHYIne4bOAfOT+xuMpIK8+gLSRwCemdqR3OMM38pnsFJ/nA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@20.3.18': - resolution: {integrity: sha512-M62oQbSTRmnGavIVCwimoadg/PDWadgNhactMm9fgH0eM9rx+iWBAYJk4VufO0bwOhysFpRZpJgXlFjOifz/Jw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/common@22.1.6': + resolution: {integrity: sha512-giuH+jJvo6YbBxbKofJCXvq6k8g1Z/xCAvh4piNFSSS6/toXTLCeZ+snr6Stw5b2wRbArM5Q5nDeuto3NqVPnQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 20.3.18 + '@angular/core': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@20.3.18': - resolution: {integrity: sha512-zsoEgLgnblmRbi47YwMghKirJ8IBKJ3+I8TxLBRIBrhx+KHFp+6oeDeLyu9H+djdyk88zexVd09wzR/YK73F0g==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler-cli@22.1.6': + resolution: {integrity: sha512-C1fQuaSLnibhfbb7Im/vurdBEcfQk+/GqPkY4+dgEKd4EPleO0xWlD+k5DwyNFX8a9w7HebWfo0Zl66HuaEwOQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 20.3.18 - typescript: '>=5.8 <6.0' + '@angular/compiler': 22.1.6 + typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@20.3.18': - resolution: {integrity: sha512-AaP/LCiDNcYmF135EEozjyR04NRBT38ZfBHQwjhgwiBBTejmvcpHwJaHSkraLpZqZzE4BQqqmgiQ1EJqxEwLVA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler@22.1.6': + resolution: {integrity: sha512-JjOUm/qD338+fGfZvxSNn/vTUiVqNwOiPzacInVUq1eVp7Jev+cnvEwXA2cFJkYZoy3Imz6wHoMvTUZNN8cbKQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@20.3.18': - resolution: {integrity: sha512-B+NQQngd/aDbcfW0zGLis3wTLDeHTeTYMl/mGKQH+HwdPaRCKI1wEtaXaOYVJXkP2FeThocPevB8gLwNlPQUUw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/core@22.1.6': + resolution: {integrity: sha512-3Ln9YYOhsaU2vPufnpcu6C4dlmX4e/nJTlggVcKMT7bGZH5KlEtw3h0uh9YfANv8YhQCEk1AcuRI7KpBnh5ing==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 20.3.18 + '@angular/compiler': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.15.0 + zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: '@angular/compiler': optional: true zone.js: optional: true - '@angular/forms@20.3.18': - resolution: {integrity: sha512-x6/99LfxolyZIFUL3Wr0OrtuXHEDwEz/rwx+WzE7NL+n35yO40t3kp0Sn5uMFwI94i91QZJmXHltMpZhrVLuYg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/forms@22.1.6': + resolution: {integrity: sha512-rfV4G4UB4l69yXSRvhaHPzTMIruvBlRO+ak9NtTcYMnsoj8O5ZCyPv0ledGIrLTBwGSI3X1j4nr9aoV+Rj/n+Q==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 - '@angular/platform-browser': 20.3.18 + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/platform-browser@20.3.18': - resolution: {integrity: sha512-q6s5rEN1yYazpHYp+k4pboXRzMsRB9auzTRBEhyXSGYxqzrnn3qHN0DqgsLC9WAdyhCgnIEMFA8kRT+W277DqQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser@22.1.6': + resolution: {integrity: sha512-jrRi6zpdz+jOle5l0OW7QL0a8xPgdnxWT5FrJabbiNKfYzQfqKcaAu02l8uJoJxtGb8fLQ8pbjkPpZEvKO2z+w==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 20.3.18 - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 + '@angular/animations': 22.1.6 + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@20.3.18': - resolution: {integrity: sha512-3CWejsEYr+ze+ktvWN/qHdyq5WLrj96QZpGYJyxh1pchIcpMPE9MmLpdjf0CUrWYB7g/85u0Geq/xsz72JrGng==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-server@22.1.6': + resolution: {integrity: sha512-5daf/BngfYLnP416Wef1BFjbTxyMaY7TfqYZ26C29ScpZdPYStBoakVBM6J3k+ycjuJWt1JeaT1WJxfqchG0kA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 20.3.18 - '@angular/core': 20.3.18 - '@angular/platform-browser': 20.3.18 + '@angular/common': 22.1.6 + '@angular/compiler': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 + '@angular/router@22.1.6': + resolution: {integrity: sha512-ex0vrkcVyJJdn7NzZXun+5ctaPPLTZJ/gE7A3g1dun7BM3g4Z6zT738mODFeJgtPtJNf4bTs2gpgvyegvzWH7w==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} + peerDependencies: + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 + rxjs: ^6.5.3 || ^7.4.0 + + '@angular/ssr@22.1.8': + resolution: {integrity: sha512-D/Is+0dXrFrYeWa3CGqN/inp73pvz+zMrje3wOAR22SBQSV4qPeY8Y4NuR8EflwMwsfiNAfGBYSDQs1WsEx+bQ==} + peerDependencies: + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/platform-server': ^22.0.0 + '@angular/router': ^22.0.0 + peerDependenciesMeta: + '@angular/platform-server': + optional: true + '@arethetypeswrong/cli@0.15.4': resolution: {integrity: sha512-YDbImAi1MGkouT7f2yAECpUMFhhA1J0EaXzIqoC5GGtK0xDgauLtcsZezm8tNq7d3wOFXH7OnY+IORYcG212rw==} engines: {node: '>=18'} @@ -3542,18 +3898,26 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@8.0.0': + resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.3': - resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} - engines: {node: '>=6.9.0'} + '@babel/compat-data@8.0.0': + resolution: {integrity: sha512-DOjnob/cXOUgDOozCDeq/aK2p5y8dUIVdf6tNhEV1HQRd6I8aQ4f4fbtHRVEvb6lP3BGomrKHiS8ICAASSVQSw==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/core@7.29.7': resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} + '@babel/core@8.0.1': + resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/eslint-parser@7.28.6': resolution: {integrity: sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} @@ -3565,14 +3929,26 @@ packages: resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0': + resolution: {integrity: sha512-NT9NrVwJsbSV6Y2FSstWa71EETOnzrjkL5/wX3D2mYHtKM+qvqB1DvR4D0Setb/gDBsHzRICifwEWMO8CnTF6g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@8.0.0': + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@8.0.0': + resolution: {integrity: sha512-JwculLABZvyPvyLBpwU/E/IbH2uM3mnxNtIJpxnIfb24y1PrdVxK5Dqjle4DpgqpGRnwgC7G8IkzPdSXZrO1Ew==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-create-class-features-plugin@7.28.6': resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} engines: {node: '>=6.9.0'} @@ -3594,6 +3970,10 @@ packages: resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@8.0.0': + resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-member-expression-to-functions@7.28.5': resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} @@ -3644,14 +4024,26 @@ packages: resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.4': + resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@8.0.0': + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-wrap-function@7.28.6': resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} engines: {node: '>=6.9.0'} @@ -3660,6 +4052,10 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} + '@babel/helpers@8.0.0': + resolution: {integrity: sha512-wfbi91pM3py96oIiJEz7qIpyXDytgr9zQC1HEWwlGNVRAEmItuU/0a41ZUKu1sJGyhhOIpc4t5vk4PYzt8wpsg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/highlight@7.25.9': resolution: {integrity: sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==} engines: {node: '>=6.9.0'} @@ -3669,6 +4065,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.4': + resolution: {integrity: sha512-srpptsAkEbbNIC/q8nT7o+m6CQe8CJUTV/t7MYc9NnWlgYVtHOb7JH6SorxMhN0kuRJjVqXbKClG6xSbPtzz+g==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -4271,14 +4672,26 @@ packages: resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} + '@babel/template@8.0.0': + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/traverse@7.29.8': resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} engines: {node: '>=6.9.0'} + '@babel/traverse@8.0.4': + resolution: {integrity: sha512-bZnmqzGG8UZneG1lLxBoWIH0G6Gr1D846Yu4/3XnY6FhCndMR49u26nTY08u/dAxWmLWF9vGQOuC+84FfIUoeg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/types@7.29.8': resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.4': + resolution: {integrity: sha512-eY+Yn3dCqTGmyiq2QRU66lA5FL8lqqqvecHt0fF3uHONIa7ToYsaCiWV8lOKqAs0Rb2SjixiKFROngnulPtt2g==} + engines: {node: ^22.18.0 || >=24.11.0} + '@callstack/react-theme-provider@3.0.9': resolution: {integrity: sha512-tTQ0uDSCL0ypeMa8T/E9wAZRGKWj8kXP7+6RYgPTfOPs9N07C9xM8P02GJ3feETap4Ux5S69D9nteq9mEj86NA==} peerDependencies: @@ -5054,10 +5467,6 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@gar/promise-retry@1.0.3': - resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} - engines: {node: ^20.17.0 || >=22.9.0} - '@gerrit0/mini-shiki@3.23.0': resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==} @@ -5066,6 +5475,9 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@harperfast/extended-iterable@1.0.3': + resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==} + '@hono/node-server@1.19.12': resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} engines: {node: '>=18.14.1'} @@ -5245,17 +5657,21 @@ packages: resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.8': + resolution: {integrity: sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/checkbox@5.2.5': + resolution: {integrity: sha512-bRt8J8m+Fot9CXv+zNQGXUq2ET0MggR1fPz7v6edN6MFYmsbfGnMmkmWZJEegMKqrAC8ej/o1sqisHZXZJMAfQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@5.1.14': - resolution: {integrity: sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==} + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} peerDependencies: '@types/node': ^22.15.3 @@ -5263,9 +5679,9 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: @@ -5281,27 +5697,45 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} + '@inquirer/core@12.0.3': + resolution: {integrity: sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} + '@inquirer/editor@5.3.3': + resolution: {integrity: sha512-YsKkS2q63IiLtaDK/9nqzdComN97SDQrmKiyNggN+ceP4ty+Z6VwyTz3FpjeUWeW1Efss2xHFKCC9sx7hnrsxg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ^22.15.3 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@5.1.5': + resolution: {integrity: sha512-uHuXLmXW+TtIfT/9vSBotypAkqn1n34Ul+CLGPos/xANyO4Ff5xZzkYhbKR4NEcfVK4a9mHQOpwVZzluSHFRGw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ^22.15.3 + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@3.0.5': + resolution: {integrity: sha512-f3QQJRIX5ZEneBHNUIuPjmbdzHnmRFJA8r2dkcb8q+OM5Uv5KtnuAttQumnrjcBVBM3mcTX1CkmtAkU58VRZxg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: @@ -5312,63 +5746,67 @@ packages: resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} + '@inquirer/figures@2.0.9': + resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.6': + resolution: {integrity: sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} + '@inquirer/number@4.2.3': + resolution: {integrity: sha512-6Yuwh1NGSbu1Lo4N1EWjXs1jKRntLg/ZCwhmeorEHde90v1XxAozdbd4Iu30eOQLW+6h1hp2O9ujNfLSbTPJnA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} + '@inquirer/password@5.2.2': + resolution: {integrity: sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/prompts@7.8.2': - resolution: {integrity: sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==} - engines: {node: '>=18'} + '@inquirer/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} + '@inquirer/rawlist@5.3.5': + resolution: {integrity: sha512-1oHky1ONfCOwNrnkQGDE1oaSij/3fI6HFMSf2H/WsGO2lEyDX9My82iggITSy9ddSZ8yk8j9v41OI0fVoSIoaA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} + '@inquirer/search@4.3.3': + resolution: {integrity: sha512-fyuIU1Nbpvwlikjg3gXwJFDI11+EFjqQ7P+iByfmivIKQ1vmaykNrD/vy5unHuUqUpsOsnvJ25//tPF7E/RBRA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} + '@inquirer/select@5.2.5': + resolution: {integrity: sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': ^22.15.3 peerDependenciesMeta: @@ -5384,6 +5822,15 @@ packages: '@types/node': optional: true + '@inquirer/type@4.1.1': + resolution: {integrity: sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': ^22.15.3 + peerDependenciesMeta: + '@types/node': + optional: true + '@internationalized/date@3.12.0': resolution: {integrity: sha512-/PyIMzK29jtXaGU23qTvNZxvBXRtKbNnGDFD+PY6CZw/Y8Ex8pFUzkuCJCG9aOqmShjqhS9mPqP6Dk5onQY8rQ==} @@ -5518,12 +5965,12 @@ packages: peerDependencies: solid-js: ^1.8.8 - '@listr2/prompt-adapter-inquirer@3.0.1': - resolution: {integrity: sha512-3XFmGwm3u6ioREG+ynAQB7FoxfajgQnMhIu8wC5eo/Lsih4aKDg0VuIMGaOsYn7hJSJagSeaD4K8yfpkEoDEmA==} - engines: {node: '>=20.0.0'} + '@listr2/prompt-adapter-inquirer@4.2.5': + resolution: {integrity: sha512-pYGy9dTdTwXdasPgyohkr0HoQ4FrkAzFnsUZl/gcnadDArbpZ8e+fgr+F9WBdNEl2y00mb9bCM4WgmoBkZJ27A==} + engines: {node: '>=22.13.0'} peerDependencies: - '@inquirer/prompts': '>= 3 < 8' - listr2: 9.0.1 + '@inquirer/prompts': '>= 3 < 9' + listr2: 11.0.0 '@lit-labs/ssr-client@1.1.8': resolution: {integrity: sha512-PjGh81oKsoI64m3IDjTqqjhC7dr2uC/o0jrllUb5gRAyp/RlAHxapgJrjq9kWz97faCHLQ8jUlTi6tGm+8fgyA==} @@ -5541,38 +5988,38 @@ packages: '@lit/reactive-element@2.1.2': resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} - '@lmdb/lmdb-darwin-arm64@3.4.2': - resolution: {integrity: sha512-NK80WwDoODyPaSazKbzd3NEJ3ygePrkERilZshxBViBARNz21rmediktGHExoj9n5t9+ChlgLlxecdFKLCuCKg==} + '@lmdb/lmdb-darwin-arm64@3.5.6': + resolution: {integrity: sha512-mY5FG4TjPAkY4P0w+OhHaUka5mDh2TX2WKYIwuKzJ1zeW3VvRgxdam/lGJTquI+bthTx5CSHDW+BAQCnNAzkEA==} cpu: [arm64] os: [darwin] - '@lmdb/lmdb-darwin-x64@3.4.2': - resolution: {integrity: sha512-zevaowQNmrp3U7Fz1s9pls5aIgpKRsKb3dZWDINtLiozh3jZI9fBrI19lYYBxqdyiIyNdlyiidPnwPShj4aK+w==} + '@lmdb/lmdb-darwin-x64@3.5.6': + resolution: {integrity: sha512-foa+pwitysO8k+xhs7psBFfTKnVgR69NlZRRTHaFVDqphh7AdGpLeyRzKw/ofatr/sN6TiHRRW6mmop0ZrrppQ==} cpu: [x64] os: [darwin] - '@lmdb/lmdb-linux-arm64@3.4.2': - resolution: {integrity: sha512-ZBEfbNZdkneebvZs98Lq30jMY8V9IJzckVeigGivV7nTHJc+89Ctomp1kAIWKlwIG0ovCDrFI448GzFPORANYg==} + '@lmdb/lmdb-linux-arm64@3.5.6': + resolution: {integrity: sha512-HmiyFFdJa38s1heCMSooSPaBSFTHJ3C+ERPp28xAPlDX1YiALJVOgbry065nXd8Y7KISWjnw05zpG1RX8IfftA==} cpu: [arm64] os: [linux] - '@lmdb/lmdb-linux-arm@3.4.2': - resolution: {integrity: sha512-OmHCULY17rkx/RoCoXlzU7LyR8xqrksgdYWwtYa14l/sseezZ8seKWXcogHcjulBddER5NnEFV4L/Jtr2nyxeg==} + '@lmdb/lmdb-linux-arm@3.5.6': + resolution: {integrity: sha512-QR4YRyR5h5Z8eGXrNQjiyo2NNDfqi3tCc9dQG5Is1blCt+qWw1ZoBWhlWAr5d+jshkifMIJjVHzHGKbkKzF8Tw==} cpu: [arm] os: [linux] - '@lmdb/lmdb-linux-x64@3.4.2': - resolution: {integrity: sha512-vL9nM17C77lohPYE4YaAQvfZCSVJSryE4fXdi8M7uWPBnU+9DJabgKVAeyDb84ZM2vcFseoBE4/AagVtJeRE7g==} + '@lmdb/lmdb-linux-x64@3.5.6': + resolution: {integrity: sha512-ADzCuCF2cTNiX9kDScqcz1fjnAkxPpQNneV3KFTdV3wWtVlI2sTGzySoMTgDpinkMMFj1NTJlxA6XR8fwc4hlA==} cpu: [x64] os: [linux] - '@lmdb/lmdb-win32-arm64@3.4.2': - resolution: {integrity: sha512-SXWjdBfNDze4ZPeLtYIzsIeDJDJ/SdsA0pEXcUBayUIMO0FQBHfVZZyHXQjjHr4cvOAzANBgIiqaXRwfMhzmLw==} + '@lmdb/lmdb-win32-arm64@3.5.6': + resolution: {integrity: sha512-J7A9aEQsQiv0TYtBGL7NDIPp2lOS8nnl+zm4sWZm1xlsTTaQ4PgD096Adzdrk27rw3UxCkDXdCUa4ax41oztBQ==} cpu: [arm64] os: [win32] - '@lmdb/lmdb-win32-x64@3.4.2': - resolution: {integrity: sha512-IY+r3bxKW6Q6sIPiMC0L533DEfRJSXibjSI3Ft/w9Q8KQBNqEIvUFXt+09wV8S5BRk0a8uSF19YWxuRwEfI90g==} + '@lmdb/lmdb-win32-x64@3.5.6': + resolution: {integrity: sha512-1g7G0knRX2iV/voDu54yxrGqw5Dk0w2oIYb7dgJq8IkOi+m7wbD8Q3QpPFjh0C01G58S88dqGn03len6UPCXsg==} cpu: [x64] os: [win32] @@ -5606,8 +6053,8 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@modelcontextprotocol/sdk@1.26.0': - resolution: {integrity: sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==} + '@modelcontextprotocol/sdk@1.30.0': + resolution: {integrity: sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -5962,47 +6409,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@npmcli/agent@4.0.0': - resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} - engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/fs@3.1.1': resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - '@npmcli/fs@5.0.0': - resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/git@7.0.2': - resolution: {integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/installed-package-contents@4.0.0': - resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - - '@npmcli/node-gyp@5.0.0': - resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/package-json@7.0.5': - resolution: {integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/promise-spawn@9.0.1': - resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/redact@4.0.0': - resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/run-script@10.0.4': - resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} - engines: {node: ^20.17.0 || >=22.9.0} - '@nx/nx-darwin-arm64@22.1.3': resolution: {integrity: sha512-4D/jXGsr3jcQ0vBo8aXXZMdfmC3n4OsZ1zjFaOXlF62Ujug+RqI/IvKxycT9r7Lr09PmW2OqBC01NfIWKoBLhg==} cpu: [arm64] @@ -6069,48 +6479,146 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@oxc-parser/binding-android-arm-eabi@0.121.0': + resolution: {integrity: sha512-n07FQcySwOlzap424/PLMtOkbS7xOu8nsJduKL8P3COGHKgKoDYXwoAHCbChfgFpHnviehrLWIPX0lKGtbEk/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm-eabi@0.142.0': + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + '@oxc-parser/binding-android-arm-eabi@0.143.0': resolution: {integrity: sha512-n9uozULWflPqBtdmI8lAabLqGKNgLVNN0ZH8HfgCwpKGNtzRzauB76jTiW/3YLkcA7N1zskpi9GdVnZuu1SAvg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] + '@oxc-parser/binding-android-arm64@0.121.0': + resolution: {integrity: sha512-/Dd1xIXboYAicw+twT2utxPD7bL8qh7d3ej0qvaYIMj3/EgIrGR+tSnjCUkiCT6g6uTC0neSS4JY8LxhdSU/sA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-android-arm64@0.142.0': + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@oxc-parser/binding-android-arm64@0.143.0': resolution: {integrity: sha512-9BbdjHETk6O3zH/DDid9IgBtF0GlpLabNKN231uraXpRDSfY+iiZxTP5bk1Z63GBownVdhdINFIeddmMz4MzpQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@oxc-parser/binding-darwin-arm64@0.121.0': + resolution: {integrity: sha512-A0jNEvv7QMtCO1yk205t3DWU9sWUjQ2KNF0hSVO5W9R9r/R1BIvzG01UQAfmtC0dQm7sCrs5puixurKSfr2bRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-arm64@0.142.0': + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@oxc-parser/binding-darwin-arm64@0.143.0': resolution: {integrity: sha512-gh+6ecoHUy4/sUcolBl/1qPXKBbYNxFY0Pk0ujgQvINTMSftJY7o4yb8gOkDJPeZeB8+a+u7xTe6umoP8N5HFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@oxc-parser/binding-darwin-x64@0.121.0': + resolution: {integrity: sha512-SsHzipdxTKUs3I9EOAPmnIimEeJOemqRlRDOp9LIj+96wtxZejF51gNibmoGq8KoqbT1ssAI5po/E3J+vEtXGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.142.0': + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@oxc-parser/binding-darwin-x64@0.143.0': resolution: {integrity: sha512-qd1hl2d+lXgHv/VQ/M9qm8TrMC5T4RqDBwtOnl+1D0QMjwcz+8AaB4JSg8STgeag0GP6a6L74XEGAsrTSJWNzQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@oxc-parser/binding-freebsd-x64@0.121.0': + resolution: {integrity: sha512-v1APOTkCp+RWOIDAHRoaeW/UoaHF15a60E8eUL6kUQXh+i4K7PBwq2Wi7jm8p0ymID5/m/oC1w3W31Z/+r7HQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-freebsd-x64@0.142.0': + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@oxc-parser/binding-freebsd-x64@0.143.0': resolution: {integrity: sha512-M5XXcNa7aOqLPKTR41msfghKu2yQ4xWvCm11/gwU0JzOzHNk5sgW//rVEjJ+LO48+VDAMzXTSzurUVxIDKwozw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@oxc-parser/binding-linux-arm-gnueabihf@0.121.0': + resolution: {integrity: sha512-PmqPQuqHZyFVWA4ycr0eu4VnTMmq9laOHZd+8R359w6kzuNZPvmmunmNJ8ybkm769A0nCoVp3TJ6dUz7B3FYIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': resolution: {integrity: sha512-T/GXusuOkPNQhCQCSBbcU/N8j0rAypuDBl1IyFK+lyYT594XsVz80clPC/OtbSSpBGyJxj8uYEfctxVuxVYoww==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.121.0': + resolution: {integrity: sha512-vF24htj+MOH+Q7y9A8NuC6pUZu8t/C2Fr/kDOi2OcNf28oogr2xadBPXAbml802E8wRAVfbta6YLDQTearz+jw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': resolution: {integrity: sha512-oKu4RcBlXSqo3OC62dp6YTnQaZIurNDpCX3BnAM3+bJxt7s8J2TJKMnC0UYer1qhlRaDCg6wkTaTw+2IlsZ12w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@oxc-parser/binding-linux-arm64-gnu@0.121.0': + resolution: {integrity: sha512-wjH8cIG2Lu/3d64iZpbYr73hREMgKAfu7fqpXjgM2S16y2zhTfDIp8EQjxO8vlDtKP5Rc7waZW72lh8nZtWrpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-arm64-gnu@0.143.0': resolution: {integrity: sha512-WJBbD186AZmMGaSIhlktC+rPl8L3peCTXAh88Ih9uEvK0en2mPojGyCGYiL6mHtV1RPV3JyfJW5t6n5hh0lXhA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6118,6 +6626,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-arm64-musl@0.121.0': + resolution: {integrity: sha512-qT663J/W8yQFw3dtscbEi9LKJevr20V7uWs2MPGTnvNZ3rm8anhhE16gXGpxDOHeg9raySaSHKhd4IGa3YZvuw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-arm64-musl@0.143.0': resolution: {integrity: sha512-t1AcYOwEzgceadT4v5e+vaCCb0AncCA3v5AyzfBAz/tMq11qzVccXKzNHtkWdjBsgvTKwRkaUF3QvT4kot8vcQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6125,6 +6647,20 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-ppc64-gnu@0.121.0': + resolution: {integrity: sha512-mYNe4NhVvDBbPkAP8JaVS8lC1dsoJZWH5WCjpw5E+sjhk1R08wt3NnXYUzum7tIiWPfgQxbCMcoxgeemFASbRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': resolution: {integrity: sha512-RsnO/NoD8376LMJq8JS8TwI0ieNaFRTuNe2GVJntQg6gwZNMENZsEbknHdVwjpOmxdGLGodcwaGSbAeRr5Bgjw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6132,6 +6668,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-riscv64-gnu@0.121.0': + resolution: {integrity: sha512-+QiFoGxhAbaI/amqX567784cDyyuZIpinBrJNxUzb+/L2aBRX67mN6Jv40pqduHf15yYByI+K5gUEygCuv0z9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': resolution: {integrity: sha512-48fSVfR9TZi5CASZFyv0VC6z6BCoeihFsX031mAD/oSH7d9PYsPgIqza7d9mjP7Z2KTEpTFyH6SIu0Ui6R1vdg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6139,6 +6689,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-riscv64-musl@0.121.0': + resolution: {integrity: sha512-9ykEgyTa5JD/Uhv2sttbKnCfl2PieUfOjyxJC/oDL2UO0qtXOtjPLl7H8Kaj5G7p3hIvFgu3YWvAxvE0sqY+hQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-riscv64-musl@0.143.0': resolution: {integrity: sha512-T8CpdD+SfE01DnIOD4HpVxu0ZJOfMJ/VhCvikKfaXAxkZ+9veyLM/D2hpi7Y2hFUyPmVQO3FNZHmYzV/WlVR4g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6146,6 +6710,20 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-linux-s390x-gnu@0.121.0': + resolution: {integrity: sha512-DB1EW5VHZdc1lIRjOI3bW/wV6R6y0xlfvdVrqj6kKi7Ayu2U3UqUBdq9KviVkcUGd5Oq+dROqvUEEFRXGAM7EQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-s390x-gnu@0.143.0': resolution: {integrity: sha512-QLdeMsCcacenPEFsfxnBUDF1y6opyz5+fmOz9bfD5Y7fiGCMupUCuB3KTPQhNwshIG1P9fPqar9MHxuBDd4bwQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6153,6 +6731,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.121.0': + resolution: {integrity: sha512-s4lfobX9p4kPTclvMiH3gcQUd88VlnkMTF6n2MTMDAyX5FPNRhhRSFZK05Ykhf8Zy5NibV4PbGR6DnK7FGNN6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@oxc-parser/binding-linux-x64-gnu@0.143.0': resolution: {integrity: sha512-659ujfqLy6k7cuH3sbzhd8b+ztSq+i6E2E9pG78Q0BmHjAExfGIdgc8cGgMdwAozDXeZFHkJ+LXYJdWsaGdgyw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6160,6 +6752,20 @@ packages: os: [linux] libc: [glibc] + '@oxc-parser/binding-linux-x64-musl@0.121.0': + resolution: {integrity: sha512-P9KlyTpuBuMi3NRGpJO8MicuGZfOoqZVRP1WjOecwx8yk4L/+mrCRNc5egSi0byhuReblBF2oVoDSMgV9Bj4Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-x64-musl@0.142.0': + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@oxc-parser/binding-linux-x64-musl@0.143.0': resolution: {integrity: sha512-/Mw/9j4TfZcnKphPrzOE6t4MMknXadcAAuVUlDRTF/ETWB5xOgQvOJV2Mh9We/bWxZdoxaGAdc+hy4GuYwQ2yQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6167,30 +6773,97 @@ packages: os: [linux] libc: [musl] + '@oxc-parser/binding-openharmony-arm64@0.121.0': + resolution: {integrity: sha512-R+4jrWOfF2OAPPhj3Eb3U5CaKNAH9/btMveMULIrcNW/hjfysFQlF8wE0GaVBr81dWz8JLgQlsxwctoL78JwXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-openharmony-arm64@0.142.0': + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@oxc-parser/binding-openharmony-arm64@0.143.0': resolution: {integrity: sha512-8rIKWR2BFuifbIK/1XB9wTaSdtuJ25dlE7ZQYDnEwj/2xH2vHsxnvIjHT3ZjSVuLLwGGlSslIG/fbOJ8TV8rTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@oxc-parser/binding-wasm32-wasi@0.121.0': + resolution: {integrity: sha512-5TFISkPTymKvsmIlKasPVTPuWxzCcrT8pM+p77+mtQbIZDd1UC8zww4CJcRI46kolmgrEX6QpKO8AvWMVZ+ifw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-parser/binding-wasm32-wasi@0.142.0': + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.121.0': + resolution: {integrity: sha512-V0pxh4mql4XTt3aiEtRNUeBAUFOw5jzZNxPABLaOKAWrVzSr9+XUaB095lY7jqMf5t8vkfh8NManGB28zanYKw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@oxc-parser/binding-win32-arm64-msvc@0.143.0': resolution: {integrity: sha512-5U9kQYMfRRI6Zq7KDxgbIP0RMnKrfn3gLepRMgJuRkPSUALTiRCk9d/uyhb4lGDjUdzwK7mBkKqhLgzBPCmLpQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@oxc-parser/binding-win32-ia32-msvc@0.121.0': + resolution: {integrity: sha512-4Ob1qvYMPnlF2N9rdmKdkQFdrq16QVcQwBsO8yiPZXof0fHKFF+LmQV501XFbi7lHyrKm8rlJRfQ/M8bZZPVLw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + '@oxc-parser/binding-win32-ia32-msvc@0.143.0': resolution: {integrity: sha512-25P7AaHk4R88Yv2XH4gToDVmh0cOu+bEURQU10CRrmvgabfRArSGAP5osmwUKeSUHj0VS50upbpbRWWW/m7mHA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] + '@oxc-parser/binding-win32-x64-msvc@0.121.0': + resolution: {integrity: sha512-BOp1KCzdboB1tPqoCPXgntgFs0jjeSyOXHzgxVFR7B/qfr3F8r4YDacHkTOUNXtDgM8YwKnkf3rE5gwALYX7NA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@oxc-parser/binding-win32-x64-msvc@0.143.0': resolution: {integrity: sha512-ORMh3JE1s6V7ySicdRK7vgaDQnn5o+UHg9ct989PlWHbel8O9ARrmWXM6kZjrBMtNucxNayQ8g69G0VfWzhANw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] + '@oxc-project/types@0.121.0': + resolution: {integrity: sha512-CGtOARQb9tyv7ECgdAlFxi0Fv7lmzvmlm2rpD/RdijOO9rfk/JvB1CjT8EnoD+tjna/IYgKKw3IV7objRb+aYw==} + + '@oxc-project/types@0.140.0': + resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} + '@oxc-project/types@0.143.0': resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} @@ -6571,36 +7244,73 @@ packages: resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==} engines: {node: '>=14.0.0'} + '@rolldown/binding-android-arm64@1.2.0': + resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-android-arm64@1.2.4': resolution: {integrity: sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] + '@rolldown/binding-darwin-arm64@1.2.0': + resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-arm64@1.2.4': resolution: {integrity: sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-x64@1.2.0': + resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.2.4': resolution: {integrity: sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-freebsd-x64@1.2.0': + resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-freebsd-x64@1.2.4': resolution: {integrity: sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.2.4': resolution: {integrity: sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.2.0': + resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.2.4': resolution: {integrity: sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6608,6 +7318,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.2.0': + resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.2.4': resolution: {integrity: sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6615,6 +7332,13 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.4': resolution: {integrity: sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6622,6 +7346,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.0': + resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.4': resolution: {integrity: sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6629,6 +7360,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.0': + resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.2.4': resolution: {integrity: sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6636,6 +7374,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.2.0': + resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-linux-x64-musl@1.2.4': resolution: {integrity: sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6643,18 +7388,41 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-openharmony-arm64@1.2.0': + resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.2.4': resolution: {integrity: sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.2.0': + resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.2.0': + resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.2.4': resolution: {integrity: sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.0': + resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/binding-win32-x64-msvc@1.2.4': resolution: {integrity: sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -6743,277 +7511,139 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.60.1': resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.60.1': resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.60.1': resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.60.1': resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.60.1': resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.1': resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.60.1': resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.60.1': resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.60.1': resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.60.1': resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-musl@4.60.1': resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.60.1': resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-musl@4.60.1': resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.60.1': resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.60.1': resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.60.1': resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.1': resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-x64-musl@4.60.1': resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - '@rollup/rollup-openbsd-x64@4.60.1': resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} - cpu: [arm64] - os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.60.1': resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.60.1': resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.1': resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.1': resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.1': resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} cpu: [x64] @@ -7041,9 +7671,9 @@ packages: '@rushstack/ts-command-line@4.22.6': resolution: {integrity: sha512-QSRqHT/IfoC5nk9zn6+fgyqOPXHME0BfchII9EUPR19pocsNp/xSbeBCbD3PIR2Lg+Q5qk7OFqk1VhWPMdKHJg==} - '@schematics/angular@20.3.22': - resolution: {integrity: sha512-wXTdFaPIBnSSNj/m0kclvPCYQOc2EGTQN1+Q3j9RIghS9gKgPxI1unSfgieJldZWKzcl8+WdB2zUuDzE7tEshQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@schematics/angular@22.1.8': + resolution: {integrity: sha512-V37T9uHOQVHyxxOqwcJ9xjSIW/mW9UuSfjOc7WJE4V8+3zj0abDJLHuoxDZKe0icYGapgOcvHyYjtNOjSeSivw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@segment/loosely-validate-event@2.0.0': resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==} @@ -7087,30 +7717,6 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@sigstore/bundle@4.0.0': - resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/core@3.2.0': - resolution: {integrity: sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/protobuf-specs@0.5.0': - resolution: {integrity: sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==} - engines: {node: ^18.17.0 || >=20.5.0} - - '@sigstore/sign@4.1.1': - resolution: {integrity: sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/tuf@4.0.2': - resolution: {integrity: sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/verify@3.1.0': - resolution: {integrity: sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==} - engines: {node: ^20.17.0 || >=22.9.0} - '@sinclair/typebox@0.27.10': resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} @@ -7544,17 +8150,12 @@ packages: vitest: optional: true + '@ts-morph/common@0.22.0': + resolution: {integrity: sha512-HqNBuV/oIlMKdkLshXd1zKBqNQCsuPEsgQOkfFQ/eUKjRlwndXW1AjN9LVkBEIukm00gGXSRmfkl0Wv5VXLnlw==} + '@tsconfig/svelte@5.0.8': resolution: {integrity: sha512-UkNnw1/oFEfecR8ypyHIQuWYdkPvHiwcQ78sh+ymIiYoF+uc5H1UBetbjyqT+vgGJ3qQN6nhucJviX6HesWtKQ==} - '@tufjs/canonical-json@2.0.0': - resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} - engines: {node: ^16.14.0 || >=18.0.0} - - '@tufjs/models@4.1.0': - resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} - engines: {node: ^20.17.0 || >=22.9.0} - '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -7579,12 +8180,18 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/braces@3.0.5': resolution: {integrity: sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -7606,6 +8213,15 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + + '@types/gensync@1.0.5': + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -7621,6 +8237,9 @@ packages: '@types/html-minifier-terser@6.1.0': resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -7633,6 +8252,9 @@ packages: '@types/jscodeshift@17.3.0': resolution: {integrity: sha512-ogvGG8VQQqAQQ096uRh+d6tBHrYuZjsumHirKtvBa5qEyTMN3IQJ7apo+sw9lxaB/iKWIhbbLlF3zmAWk9XQIg==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -7657,6 +8279,12 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -7668,6 +8296,12 @@ packages: '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/sort-by@1.2.3': resolution: {integrity: sha512-Q8Pg7o2iHWFf7pR4jIGb+ntxwwL7a/WWLFNJj8jEN14tPQdfwZLCqK68q6mo1WONqa68OysEPuFvNA3uGm0crw==} @@ -8070,8 +8704,8 @@ packages: peerDependencies: vinxi: ^0.5.5 - '@vitejs/plugin-basic-ssl@2.1.0': - resolution: {integrity: sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==} + '@vitejs/plugin-basic-ssl@2.3.0': + resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: vite: ^6.4.1 @@ -8543,10 +9177,6 @@ packages: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} engines: {node: ^18.17.0 || >=20.5.0} - abbrev@4.0.0: - resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} - engines: {node: ^20.17.0 || >=22.9.0} - abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -8598,6 +9228,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-base@9.0.0: + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + engines: {node: '>= 20'} + aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} @@ -8657,9 +9291,8 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - algoliasearch@5.35.0: - resolution: {integrity: sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==} - engines: {node: '>= 14.0.0'} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} alien-signals@1.0.13: resolution: {integrity: sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==} @@ -9092,9 +9725,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - beasties@0.3.5: - resolution: {integrity: sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==} - engines: {node: '>=14.0.0'} + beasties@0.4.3: + resolution: {integrity: sha512-fIIeLOcbAB/K1kb1HBVJoiq1alHL4RCYBSo5e7HzrNkkgMggXR1Vqt/Z9JWnkfe/qdCo66Ux3QRwZioAIBdWRA==} + engines: {node: '>=18.0.0'} better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} @@ -9275,10 +9908,6 @@ packages: resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} engines: {node: ^16.14.0 || >=18.0.0} - cacache@20.0.4: - resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} - engines: {node: ^20.17.0 || >=22.9.0} - cache-base@1.0.1: resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} engines: {node: '>=0.10.0'} @@ -9479,13 +10108,17 @@ packages: resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} engines: {node: '>=6'} + cli-spinners@3.4.0: + resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} + engines: {node: '>=18.20'} + cli-table3@0.6.5: resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} - cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} + cli-truncate@6.1.1: + resolution: {integrity: sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw==} + engines: {node: '>=22'} cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} @@ -9525,6 +10158,9 @@ packages: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} + code-block-writer@12.0.0: + resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} + collection-visit@1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} @@ -10285,14 +10921,14 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} + engines: {node: '>=20.19.0'} + env-editor@0.4.2: resolution: {integrity: sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==} engines: {node: '>=8'} - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - env-paths@4.0.0: resolution: {integrity: sha512-pxP8eL2SwwaTRi/KHYwLYXinDs7gL3jxFcBYmEdYfZmZXbaVDvdppd0XBU8qVz03rDfKZMXg1omHCbsJjZrMsw==} engines: {node: '>=20'} @@ -10306,9 +10942,6 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -10803,9 +11436,18 @@ packages: fast-loops@1.1.4: resolution: {integrity: sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} @@ -11365,6 +12007,10 @@ packages: hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + hosted-git-info@10.1.1: + resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + hosted-git-info@2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} @@ -11372,10 +12018,6 @@ packages: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} - hosted-git-info@9.0.2: - resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} - engines: {node: ^20.17.0 || >=22.9.0} - html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -11462,6 +12104,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.1.0: + resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} + engines: {node: '>= 20'} + httpxy@0.3.1: resolution: {integrity: sha512-XjG/CEoofEisMrnFr0D6U6xOZ4mRfnwcYQ9qvvnT4lvnX8BoeA3x3WofB75D+vZwpaobFVkBIHrZzoK40w8XSw==} @@ -11497,10 +12143,6 @@ packages: iferr@0.1.5: resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} - ignore-walk@8.0.0: - resolution: {integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==} - engines: {node: ^20.17.0 || >=22.9.0} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -11572,10 +12214,6 @@ packages: ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@5.0.0: - resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} - engines: {node: ^18.17.0 || >=20.5.0} - ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -11716,10 +12354,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - is-fullwidth-code-point@5.1.0: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} @@ -11861,10 +12495,6 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - is-unicode-supported@2.1.0: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} @@ -11922,10 +12552,6 @@ packages: resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} engines: {node: '>=18'} - isexe@4.0.0: - resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} - engines: {node: '>=20'} - isobject@2.1.0: resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} engines: {node: '>=0.10.0'} @@ -12061,6 +12687,9 @@ packages: jose@6.2.2: resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -12136,10 +12765,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@5.0.0: - resolution: {integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==} - engines: {node: ^20.17.0 || >=22.9.0} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -12176,10 +12801,6 @@ packages: jsonfile@6.2.0: resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -12411,9 +13032,9 @@ packages: resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} hasBin: true - listr2@9.0.1: - resolution: {integrity: sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==} - engines: {node: '>=20.0.0'} + listr2@11.0.0: + resolution: {integrity: sha512-8K88S0aSrcSXdJfiZtEy5BQMnR+TyjrCGLcgAvQs6ta0NEnIm0RJ72/Pv67Jvg07cfBhDbuN74V81lSSVYEFEw==} + engines: {node: '>=22.13.0'} lit-element@4.2.2: resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} @@ -12424,8 +13045,8 @@ packages: lit@3.3.2: resolution: {integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==} - lmdb@3.4.2: - resolution: {integrity: sha512-nwVGUfTBUwJKXd6lRV8pFNfnrCC1+l49ESJRM19t/tFb/97QfJEixe5DYRvug5JO7DSFKoKaVy7oGMt5rVqZvg==} + lmdb@3.5.6: + resolution: {integrity: sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==} hasBin: true loader-runner@2.4.0: @@ -12506,13 +13127,13 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + log-symbols@7.0.1: + resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} - log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} + log-update@8.0.0: + resolution: {integrity: sha512-lddSgOt3bPASrylL54ZSpy8nBHns+vBVSoILlVOx+dei300pnLRN958rj/EdlVLKuWlSESU3qdnDZdAI7FXYGg==} + engines: {node: '>=22'} longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -12545,12 +13166,12 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.0.0: + resolution: {integrity: sha512-CGvjzMN08iv6w1mm4/x3Gh1hLb4VnyRUA15FFpl6CsCIGGoe36k7kY5KNz9QDbSBN5I/fWHM6ZlIkUTa5xdUEA==} + magicast@0.2.11: resolution: {integrity: sha512-6saXbRDA1HMkqbsvHOU6HBjCVgZT460qheRkLhJQHWAbhXoWESI3Kn/dGGXyKs15FFKR85jsUqFx2sMK0wy/5g==} @@ -12572,10 +13193,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - make-fetch-happen@15.0.5: - resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} - engines: {node: ^20.17.0 || >=22.9.0} - makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} @@ -12967,10 +13584,6 @@ packages: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} engines: {node: '>=16 || 14 >=14.17'} - minipass-fetch@5.0.2: - resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==} - engines: {node: ^20.17.0 || >=22.9.0} - minipass-flush@1.0.7: resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} engines: {node: '>= 8'} @@ -12979,10 +13592,6 @@ packages: resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} engines: {node: '>=8'} - minipass-sized@2.0.0: - resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} - engines: {node: '>=8'} - minipass@3.3.6: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} @@ -13020,6 +13629,11 @@ packages: engines: {node: '>=10'} hasBin: true + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} @@ -13065,6 +13679,10 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -13248,11 +13866,6 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-gyp@12.2.0: - resolution: {integrity: sha512-q23WdzrQv48KozXlr0U1v9dwO/k59NHeSzn6loGcasyf0UnSrtzs8kRxM+mfwJSf0DkX0s43hcqgnSO4/VNthQ==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - node-html-parser@6.1.13: resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} @@ -13276,11 +13889,6 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} hasBin: true - nopt@9.0.0: - resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} @@ -13288,37 +13896,13 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-bundled@5.0.0: - resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-install-checks@8.0.0: - resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-normalize-package-bin@5.0.0: - resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} - engines: {node: ^20.17.0 || >=22.9.0} - npm-package-arg@11.0.3: resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} engines: {node: ^16.14.0 || >=18.0.0} - npm-package-arg@13.0.0: - resolution: {integrity: sha512-+t2etZAGcB7TbbLHfDwooV9ppB2LhhcT6A+L9cahsf9mEUAoQ6CktLEVvEnpD0N5CkX7zJqnPGaFtoQDy9EkHQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-packlist@10.0.4: - resolution: {integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-pick-manifest@11.0.3: - resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-registry-fetch@19.1.1: - resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} - engines: {node: ^20.17.0 || >=22.9.0} + npm-package-arg@14.0.0: + resolution: {integrity: sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} npm-run-all2@5.0.2: resolution: {integrity: sha512-S2G6FWZ3pNWAAKm2PFSOtEAG/N+XO/kz3+9l6V91IY+Y3XFSt7Lp7DV92KCgEboEW0hRTu0vFaMe4zXDZYaOyA==} @@ -13501,9 +14085,9 @@ packages: resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} engines: {node: '>=10'} - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} + ora@9.4.1: + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} + engines: {node: '>=20'} ordered-binary@1.6.1: resolution: {integrity: sha512-QkCdPooczexPLiXIrbVOPYkR3VO3T6v2OyKRkR1Xbhpy7/LAVXwahnRCgRp78Oe/Ehf0C/HATAxfSr6eA1oX+w==} @@ -13521,6 +14105,14 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-parser@0.121.0: + resolution: {integrity: sha512-ek9o58+SCv6AV7nchiAcUJy1DNE2CC5WRdBcO0mF+W4oRjNQfPO7b3pLjTHSFECpHkKGOZSQxx3hk8viIL5YCg==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-parser@0.142.0: + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-parser@0.143.0: resolution: {integrity: sha512-ov0NzaDCOInknS7mP1cwKdJERt3utPW8ldjtdUXQ8Ty0GEFD08wk422vCUN0d7pST6kqtV7dxoI9w1Zi0l/9TA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -13592,10 +14184,6 @@ packages: resolution: {integrity: sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==} engines: {node: '>=16'} - p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} - engines: {node: '>=18'} - p-queue@6.6.2: resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} engines: {node: '>=8'} @@ -13629,11 +14217,6 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - pacote@21.0.4: - resolution: {integrity: sha512-RplP/pDW0NNNDh3pnaoIWYPvNenS7UqMbXyvMqJczosiFWTeGGwJC2NQBLqKf4rGLFfwCOnntw1aEp9Jiqm1MA==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} @@ -13676,8 +14259,8 @@ packages: parse-statements@1.0.11: resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} - parse5-html-rewriting-stream@8.0.0: - resolution: {integrity: sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==} + parse5-html-rewriting-stream@8.0.1: + resolution: {integrity: sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==} parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} @@ -13802,10 +14385,6 @@ packages: resolution: {integrity: sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==} engines: {node: '>=10'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} @@ -13827,8 +14406,8 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - piscina@5.1.3: - resolution: {integrity: sha512-0u3N7H4+hbr40KjuVn2uNhOcthu/9usKhnw5vT3J7ply79v3D3M8naI00el9Klcy16x557VsEkkUQaHCWFXC/g==} + piscina@5.2.0: + resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==} engines: {node: '>=20.x'} pkce-challenge@5.0.1: @@ -14038,13 +14617,9 @@ packages: resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - proc-log@5.0.0: - resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - proc-log@6.1.0: - resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} - engines: {node: ^20.17.0 || >=22.9.0} + proc-log@7.0.0: + resolution: {integrity: sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -14065,10 +14640,6 @@ packages: bluebird: optional: true - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} @@ -14089,6 +14660,15 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-agent-negotiate@1.1.0: + resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} + engines: {node: '>= 20'} + peerDependencies: + kerberos: ^2.0.0 + peerDependenciesMeta: + kerberos: + optional: true + proxy-from-env@2.1.0: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} @@ -14549,11 +15129,6 @@ packages: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -14595,10 +15170,6 @@ packages: retext@9.0.0: resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - rettime@0.10.1: resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==} @@ -14606,9 +15177,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -14647,6 +15215,11 @@ packages: vue-tsc: optional: true + rolldown@1.2.0: + resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rolldown@1.2.4: resolution: {integrity: sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -14670,11 +15243,6 @@ packages: rollup: optional: true - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.60.1: resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -14738,9 +15306,9 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.90.0: - resolution: {integrity: sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==} - engines: {node: '>=14.0.0'} + sass@1.101.0: + resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} + engines: {node: '>=20.19.0'} hasBin: true sax@1.6.0: @@ -14792,13 +15360,13 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -14979,10 +15547,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sigstore@4.1.0: - resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==} - engines: {node: ^20.17.0 || >=22.9.0} - simple-code-frame@1.3.0: resolution: {integrity: sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==} @@ -15025,22 +15589,14 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} - - slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} + slice-ansi@9.0.0: + resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} + engines: {node: '>=22'} slugify@1.6.8: resolution: {integrity: sha512-HVk9X1E0gz3mSpoi60h/saazLKXKaZThMLU3u/aNwoYn8/xQyX2MGxL0ui2eaokkD7tF+Zo+cKTHUbe1mmmGzA==} engines: {node: '>=8.0.0'} - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smob@1.6.1: resolution: {integrity: sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g==} engines: {node: '>=20.0.0'} @@ -15061,14 +15617,6 @@ packages: resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} engines: {node: '>=0.10.0'} - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - solid-js@1.9.12: resolution: {integrity: sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw==} @@ -15173,10 +15721,6 @@ packages: resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ssri@13.0.1: - resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} - engines: {node: ^20.17.0 || >=22.9.0} - ssri@6.0.2: resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==} @@ -15223,8 +15767,8 @@ packages: std-env@4.0.0: resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} stop-iteration-iterator@1.1.0: @@ -15280,6 +15824,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} + engines: {node: '>=20'} + string.prototype.matchall@4.0.12: resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} engines: {node: '>= 0.4'} @@ -15610,10 +16158,6 @@ packages: resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} - tinyglobby@0.2.14: - resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -15720,6 +16264,9 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-morph@21.0.1: + resolution: {integrity: sha512-dbDtVdEAncKctzrVZ+Nr7kHpHkv+0JDJb2MjjpBaj8bFeCkePU9rHfMklmhuLFnpeq/EJZk2IhStY6NzqgjOkg==} + ts-pattern@5.9.0: resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} @@ -15783,10 +16330,6 @@ packages: tty-browserify@0.0.0: resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} - tuf-js@4.1.0: - resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} - engines: {node: ^20.17.0 || >=22.9.0} - tupleson@0.23.1: resolution: {integrity: sha512-+vPs/gj7nue5pq78/bi58sa0SohFgjXg49T6YMyWav+0xUz29rhlSXXZJBfdv8BXacG1IOqQaIOaGGHVoPTiWg==} engines: {node: '>=18'} @@ -16276,9 +16819,9 @@ packages: resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - validate-npm-package-name@6.0.2: - resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} - engines: {node: ^18.17.0 || >=20.5.0} + validate-npm-package-name@8.0.0: + resolution: {integrity: sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} @@ -16384,6 +16927,46 @@ packages: yaml: optional: true + vite@7.1.11: + resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^22.15.3 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitefu@1.1.3: resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} peerDependencies: @@ -16584,12 +17167,8 @@ packages: watchpack@1.7.5: resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} - engines: {node: '>=10.13.0'} - - watchpack@2.5.1: - resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} + watchpack@2.5.2: + resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} engines: {node: '>=10.13.0'} wcwidth@1.0.1: @@ -16759,11 +17338,6 @@ packages: engines: {node: ^16.13.0 || >=18.0.0} hasBin: true - which@6.0.1: - resolution: {integrity: sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -16786,6 +17360,10 @@ packages: worker-farm@1.7.0: resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==} + wrap-ansi@10.0.1: + resolution: {integrity: sha512-M0N4xzyzosiIok3svYlEo1sdLZts/8FPgYH/GPC3wvlmPoRvnoManGMrE54waYj3tISA8w6lsdesfVv67qSr8Q==} + engines: {node: '>=20'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -16863,6 +17441,10 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} + xhr2@0.2.1: + resolution: {integrity: sha512-sID0rrVCqkVNUn8t6xuv9+6FViXjUVXq8H5rWOH2rz9fDNQEd4g0EA2XlcEdJXRz5BMEn4O1pJFdT+z4YHhoWw==} + engines: {node: '>= 6'} + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -16956,6 +17538,10 @@ packages: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yargs@18.1.0: + resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -17018,14 +17604,11 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.1.13: - resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} - zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} - zone.js@0.15.0: - resolution: {integrity: sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==} + zone.js@0.16.1: + resolution: {integrity: sha512-dpvY17vxYIW3+bNrP0ClUlaiY0CiIRK3tnoLaGoQsQcY9/I/NpzIWQ7tQNhbV7LacQMpCII6wVzuL3tuWOyfuA==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -17040,106 +17623,22 @@ snapshots: '@adobe/css-tools@4.4.4': {} - '@algolia/abtesting@1.1.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/client-abtesting@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/client-analytics@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - '@algolia/client-common@5.2.1': {} - '@algolia/client-common@5.35.0': {} - - '@algolia/client-insights@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/client-personalization@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/client-query-suggestions@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - '@algolia/client-search@5.2.1': dependencies: '@algolia/client-common': 5.2.1 '@algolia/requester-browser-xhr': 5.2.1 '@algolia/requester-node-http': 5.2.1 - '@algolia/client-search@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/ingestion@1.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/monitoring@1.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - - '@algolia/recommend@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 - '@algolia/requester-browser-xhr@5.2.1': dependencies: '@algolia/client-common': 5.2.1 - '@algolia/requester-browser-xhr@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - - '@algolia/requester-fetch@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@algolia/requester-node-http@5.2.1': dependencies: '@algolia/client-common': 5.2.1 - '@algolia/requester-node-http@5.35.0': - dependencies: - '@algolia/client-common': 5.35.0 - '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -17147,84 +17646,226 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 + '@analogjs/vite-plugin-angular@2.5.2(@angular/build@22.1.8(4affaef227d0997be37379ba361cd6fd))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + dependencies: + magic-string: 0.30.21 + obug: 2.1.4 + oxc-parser: 0.121.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + tinyglobby: 0.2.17 + ts-morph: 21.0.1 + optionalDependencies: + '@angular/build': 22.1.8(4affaef227d0997be37379ba361cd6fd) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + '@analogjs/vite-plugin-angular@2.5.2(@angular/build@22.1.8(e62ab7c722b8b89e816de2e21b02397f))(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + dependencies: + magic-string: 0.30.21 + obug: 2.1.4 + oxc-parser: 0.121.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + tinyglobby: 0.2.17 + ts-morph: 21.0.1 + optionalDependencies: + '@angular/build': 22.1.8(e62ab7c722b8b89e816de2e21b02397f) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + '@andrewbranch/untar.js@1.0.3': {} - '@angular-devkit/architect@0.2003.22(chokidar@5.0.0)': + '@angular-devkit/architect@0.2201.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 20.3.22(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@20.3.22(chokidar@5.0.0)': + '@angular-devkit/core@22.1.8(chokidar@5.0.0)': dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) jsonc-parser: 3.3.1 - picomatch: 4.0.4 + picomatch: 4.0.5 rxjs: 7.8.2 source-map: 0.7.6 optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@20.3.22(chokidar@5.0.0)': + '@angular-devkit/schematics@22.1.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 20.3.22(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 8.2.0 + magic-string: 1.0.0 + ora: 9.4.1 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/build@22.1.8(4affaef227d0997be37379ba361cd6fd)': dependencies: - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular/compiler': 22.1.6 + '@angular/compiler-cli': 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-split-export-declaration': 7.24.7 + '@inquirer/confirm': 6.1.1(@types/node@22.19.15) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + beasties: 0.4.3 + browserslist: 4.28.2 + esbuild: 0.27.4 + https-proxy-agent: 9.1.0 + jsonc-parser: 3.3.1 + listr2: 11.0.0 + magic-string: 1.0.0 + mrmime: 2.0.1 + oxc-parser: 0.142.0 + parse5-html-rewriting-stream: 8.0.1 + picomatch: 4.0.5 + piscina: 5.2.0 + rolldown: 1.2.0 + sass: 1.101.0 + semver: 7.8.5 + source-map-support: 0.5.21 + tinyglobby: 0.2.17 tslib: 2.8.1 + typescript: 6.0.3 + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + watchpack: 2.5.2 + optionalDependencies: + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/ssr': 22.1.8(caf12b5b30d21bb6295944d46eee472d) + istanbul-lib-instrument: 6.0.3 + lmdb: 3.5.6 + postcss: 8.5.8 + rollup: 4.60.1 + tailwindcss: 4.2.2 + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - kerberos + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml - '@angular/build@20.3.22(@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3))(@angular/compiler@20.3.18)(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@types/node@22.19.15)(chokidar@5.0.0)(jiti@2.7.0)(lightningcss@1.32.0)(postcss@8.5.8)(tailwindcss@4.2.2)(terser@5.46.1)(tslib@2.8.1)(tsx@4.21.0)(typescript@5.8.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(yaml@2.9.0)': + '@angular/build@22.1.8(52d17f587f491aa5bd2e2175a98e7579)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2003.22(chokidar@5.0.0) - '@angular/compiler': 20.3.18 - '@angular/compiler-cli': 20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3) - '@babel/core': 7.28.3 - '@babel/helper-annotate-as-pure': 7.27.3 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular/compiler': 22.1.6 + '@angular/compiler-cli': 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.14(@types/node@22.19.15) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - beasties: 0.3.5 + '@inquirer/confirm': 6.1.1(@types/node@22.19.15) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + beasties: 0.4.3 browserslist: 4.28.2 esbuild: 0.27.4 - https-proxy-agent: 7.0.6 + https-proxy-agent: 9.1.0 + jsonc-parser: 3.3.1 + listr2: 11.0.0 + magic-string: 1.0.0 + mrmime: 2.0.1 + oxc-parser: 0.142.0 + parse5-html-rewriting-stream: 8.0.1 + picomatch: 4.0.5 + piscina: 5.2.0 + rolldown: 1.2.0 + sass: 1.101.0 + semver: 7.8.5 + source-map-support: 0.5.21 + tinyglobby: 0.2.17 + tslib: 2.8.1 + typescript: 6.0.3 + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + watchpack: 2.5.2 + optionalDependencies: + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/ssr': 22.1.8(caf12b5b30d21bb6295944d46eee472d) istanbul-lib-instrument: 6.0.3 + lmdb: 3.5.6 + postcss: 8.5.8 + rollup: 4.60.1 + tailwindcss: 4.2.2 + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - kerberos + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@angular/build@22.1.8(e62ab7c722b8b89e816de2e21b02397f)': + dependencies: + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular/compiler': 22.1.6 + '@angular/compiler-cli': 22.1.6(@angular/compiler@22.1.6)(typescript@5.9.3) + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-split-export-declaration': 7.24.7 + '@inquirer/confirm': 6.1.1(@types/node@22.19.15) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + beasties: 0.4.3 + browserslist: 4.28.2 + esbuild: 0.27.4 + https-proxy-agent: 9.1.0 jsonc-parser: 3.3.1 - listr2: 9.0.1 - magic-string: 0.30.17 + listr2: 11.0.0 + magic-string: 1.0.0 mrmime: 2.0.1 - parse5-html-rewriting-stream: 8.0.0 - picomatch: 4.0.4 - piscina: 5.1.3 - rollup: 4.59.0 - sass: 1.90.0 - semver: 7.7.2 + oxc-parser: 0.142.0 + parse5-html-rewriting-stream: 8.0.1 + picomatch: 4.0.5 + piscina: 5.2.0 + rolldown: 1.2.0 + sass: 1.101.0 + semver: 7.8.5 source-map-support: 0.5.21 - tinyglobby: 0.2.14 + tinyglobby: 0.2.17 tslib: 2.8.1 - typescript: 5.8.3 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - watchpack: 2.4.4 + typescript: 5.9.3 + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + watchpack: 2.5.2 optionalDependencies: - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - lmdb: 3.4.2 + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/platform-server': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@angular/ssr': 22.1.8(caf12b5b30d21bb6295944d46eee472d) + istanbul-lib-instrument: 6.0.3 + lmdb: 3.5.6 postcss: 8.5.8 + rollup: 4.60.1 tailwindcss: 4.2.2 - vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - chokidar - jiti + - kerberos - lightningcss - sass-embedded - stylus @@ -17234,42 +17875,38 @@ snapshots: - tsx - yaml - '@angular/cli@20.3.22(@types/node@22.19.15)(chokidar@5.0.0)': + '@angular/cli@22.1.8(@types/node@22.19.15)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2003.22(chokidar@5.0.0) - '@angular-devkit/core': 20.3.22(chokidar@5.0.0) - '@angular-devkit/schematics': 20.3.22(chokidar@5.0.0) - '@inquirer/prompts': 7.8.2(@types/node@22.19.15) - '@listr2/prompt-adapter-inquirer': 3.0.1(@inquirer/prompts@7.8.2(@types/node@22.19.15))(@types/node@22.19.15)(listr2@9.0.1) - '@modelcontextprotocol/sdk': 1.26.0(zod@4.1.13) - '@schematics/angular': 20.3.22(chokidar@5.0.0) - '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.35.0 - ini: 5.0.0 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) + '@inquirer/prompts': 8.5.2(@types/node@22.19.15) + '@listr2/prompt-adapter-inquirer': 4.2.5(@inquirer/prompts@8.5.2(@types/node@22.19.15))(@types/node@22.19.15)(listr2@11.0.0) + '@modelcontextprotocol/sdk': 1.30.0(zod@4.4.3) + '@schematics/angular': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 - listr2: 9.0.1 - npm-package-arg: 13.0.0 - pacote: 21.0.4 - resolve: 1.22.10 - semver: 7.7.2 - yargs: 18.0.0 - zod: 4.1.13 + listr2: 11.0.0 + npm-package-arg: 14.0.0 + parse5-html-rewriting-stream: 8.0.1 + semver: 7.8.5 + yargs: 18.1.0 + zod: 4.4.3 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@20.3.18(@angular/compiler@20.3.18)(typescript@5.8.3)': + '@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@5.9.3)': dependencies: - '@angular/compiler': 20.3.18 - '@babel/core': 7.28.3 + '@angular/compiler': 22.1.6 + '@babel/core': 8.0.1 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 @@ -17278,46 +17915,77 @@ snapshots: tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: - typescript: 5.8.3 - transitivePeerDependencies: - - supports-color + typescript: 5.9.3 - '@angular/compiler@20.3.18': + '@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3)': dependencies: + '@angular/compiler': 22.1.6 + '@babel/core': 8.0.1 + '@jridgewell/sourcemap-codec': 1.5.5 + chokidar: 5.0.0 + convert-source-map: 1.9.0 + reflect-metadata: 0.2.2 + semver: 7.7.4 tslib: 2.8.1 + yargs: 18.0.0 + optionalDependencies: + typescript: 6.0.3 - '@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/compiler@22.1.6': + dependencies: + tslib: 2.8.1 + + '@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 20.3.18 - zone.js: 0.15.0 + '@angular/compiler': 22.1.6 + zone.js: 0.16.1 - '@angular/forms@20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 + zod: 4.4.3 - '@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) tslib: 2.8.1 - optionalDependencies: - '@angular/animations': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/router@20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/platform-server@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': + dependencies: + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/compiler': 22.1.6 + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + rxjs: 7.8.2 + tslib: 2.8.1 + xhr2: 0.2.1 + + '@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) rxjs: 7.8.2 tslib: 2.8.1 + '@angular/ssr@22.1.8(caf12b5b30d21bb6295944d46eee472d)': + dependencies: + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/router': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + tslib: 2.8.1 + optionalDependencies: + '@angular/platform-server': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) + '@arethetypeswrong/cli@0.15.4': dependencies: '@arethetypeswrong/core': 0.15.1 @@ -17431,10 +18099,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/node@9.5.5(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))': + '@astrojs/node@9.5.5(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))': dependencies: '@astrojs/internal-helpers': 0.7.6 - astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) + astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) send: 1.2.1 server-destroy: 1.0.1 transitivePeerDependencies: @@ -17444,11 +18112,11 @@ snapshots: dependencies: prismjs: 1.30.0 - '@astrojs/solid-js@5.1.3(@testing-library/jest-dom@6.9.1)(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(solid-js@1.9.12)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)': + '@astrojs/solid-js@5.1.3(@testing-library/jest-dom@6.9.1)(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(solid-js@1.9.12)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)': dependencies: solid-js: 1.9.12 - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vite-plugin-solid: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite-plugin-solid: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - '@testing-library/jest-dom' - '@types/node' @@ -17464,9 +18132,9 @@ snapshots: - tsx - yaml - '@astrojs/tailwind@6.0.2(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0))': + '@astrojs/tailwind@6.0.2(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.9.0))': dependencies: - astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) + astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) autoprefixer: 10.4.27(postcss@8.5.8) postcss: 8.5.8 postcss-load-config: 4.0.2(postcss@8.5.8) @@ -17486,14 +18154,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/vercel@8.2.11(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0))(react@19.2.4)(rollup@4.60.1)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3))': + '@astrojs/vercel@8.2.11(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0))(react@19.2.4)(rollup@4.60.1)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3))': dependencies: '@astrojs/internal-helpers': 0.7.4 - '@vercel/analytics': 1.6.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0))(react@19.2.4)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3)) + '@vercel/analytics': 1.6.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0))(react@19.2.4)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3)) '@vercel/functions': 2.2.13 '@vercel/nft': 0.30.3(rollup@4.60.1) '@vercel/routing-utils': 5.3.3 - astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) + astro: 5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0) esbuild: 0.27.4 tinyglobby: 0.2.17 transitivePeerDependencies: @@ -17529,27 +18197,14 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@8.0.0': + dependencies: + '@babel/helper-validator-identifier': 8.0.4 + js-tokens: 10.0.0 + '@babel/compat-data@7.29.7': {} - '@babel/core@7.28.3': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.3) - '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.8 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 - '@babel/types': 7.29.8 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@babel/compat-data@8.0.0': {} '@babel/core@7.29.7': dependencies: @@ -17571,6 +18226,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@8.0.1': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-compilation-targets': 8.0.0 + '@babel/helpers': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/template': 8.0.0 + '@babel/traverse': 8.0.4 + '@babel/types': 8.0.4 + '@types/gensync': 1.0.5 + convert-source-map: 2.0.0 + empathic: 2.0.1 + gensync: 1.0.0-beta.2 + import-meta-resolve: 4.2.0 + json5: 2.2.3 + obug: 2.1.4 + semver: 7.8.5 + '@babel/eslint-parser@7.28.6(@babel/core@7.29.7)(eslint@9.39.4(jiti@2.7.0))': dependencies: '@babel/core': 7.29.7 @@ -17587,10 +18261,23 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0': + dependencies: + '@babel/parser': 8.0.4 + '@babel/types': 8.0.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.8 + '@babel/helper-annotate-as-pure@8.0.0': + dependencies: + '@babel/types': 8.0.4 + '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 @@ -17599,6 +18286,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@8.0.0': + dependencies: + '@babel/compat-data': 8.0.0 + '@babel/helper-validator-option': 8.0.0 + browserslist: 4.28.2 + lru-cache: 11.2.7 + semver: 7.8.5 + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17612,6 +18307,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@8.0.1) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.29.8 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17619,6 +18328,14 @@ snapshots: regexpu-core: 6.4.0 semver: 6.3.1 + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + optional: true + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17630,8 +18347,22 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/helper-globals@7.29.7': {} + '@babel/helper-globals@8.0.0': {} + '@babel/helper-member-expression-to-functions@7.28.5': dependencies: '@babel/traverse': 7.29.8 @@ -17650,23 +18381,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.28.3)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@8.0.1)': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 8.0.1 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color + optional: true '@babel/helper-optimise-call-expression@7.27.1': dependencies: @@ -17683,6 +18415,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17692,6 +18434,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-replace-supers@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: '@babel/traverse': 7.29.8 @@ -17705,10 +18457,16 @@ snapshots: '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-identifier@8.0.4': {} + '@babel/helper-validator-option@7.29.7': {} + '@babel/helper-validator-option@8.0.0': {} + '@babel/helper-wrap-function@7.28.6': dependencies: '@babel/template': 7.29.7 @@ -17722,6 +18480,11 @@ snapshots: '@babel/template': 7.29.7 '@babel/types': 7.29.8 + '@babel/helpers@8.0.0': + dependencies: + '@babel/template': 8.0.0 + '@babel/types': 8.0.4 + '@babel/highlight@7.25.9': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -17733,6 +18496,10 @@ snapshots: dependencies: '@babel/types': 7.29.8 + '@babel/parser@8.0.4': + dependencies: + '@babel/types': 8.0.4 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17741,16 +18508,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17760,6 +18548,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17768,6 +18566,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17809,6 +18616,11 @@ snapshots: dependencies: '@babel/core': 7.29.7 + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + optional: true + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17854,11 +18666,23 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17874,6 +18698,11 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17925,11 +18754,24 @@ snapshots: '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17939,6 +18781,16 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@8.0.1) + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17948,16 +18800,38 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17966,6 +18840,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17974,6 +18857,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -17986,12 +18878,32 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-classes@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@8.0.1) + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 '@babel/template': 7.29.7 + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.29.7 + optional: true + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18000,28 +18912,63 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18030,16 +18977,37 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18054,6 +19022,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-for-of@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18063,26 +19040,60 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-function-name@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-literals@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18091,6 +19102,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-transforms': 7.29.7(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18099,6 +19119,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-transforms': 7.29.7(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18109,6 +19138,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-transforms': 7.29.7(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18117,27 +19157,61 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-module-transforms': 7.29.7(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-new-target@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18149,6 +19223,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@8.0.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@8.0.1) + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18157,11 +19243,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-object-super@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18170,11 +19271,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-parameters@7.27.7(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18183,6 +19299,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18192,11 +19317,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18209,6 +19350,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18230,6 +19378,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@8.0.1) + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18241,17 +19400,36 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18269,6 +19447,12 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18277,21 +19461,48 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-spread@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18308,24 +19519,51 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.7) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@8.0.1) + '@babel/helper-plugin-utils': 7.28.6 + optional: true + '@babel/preset-env@7.29.2(@babel/core@7.29.7)': dependencies: '@babel/compat-data': 7.29.7 @@ -18402,6 +19640,83 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/preset-env@7.29.2(@babel/core@8.0.1)': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 8.0.1 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@8.0.1) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@8.0.1) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@8.0.1) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@8.0.1) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@8.0.1) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@8.0.1) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@8.0.1) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@8.0.1) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@8.0.1) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@8.0.1) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@8.0.1) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@8.0.1) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@8.0.1) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@8.0.1) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@8.0.1) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@8.0.1) + core-js-compat: 3.49.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + optional: true + '@babel/preset-flow@7.27.1(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18416,6 +19731,14 @@ snapshots: '@babel/types': 7.29.8 esutils: 2.0.3 + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@8.0.1)': + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.8 + esutils: 2.0.3 + optional: true + '@babel/preset-react@7.28.5(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -18458,6 +19781,12 @@ snapshots: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 + '@babel/template@8.0.0': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/types': 8.0.4 + '@babel/traverse@7.29.8': dependencies: '@babel/code-frame': 7.29.7 @@ -18470,11 +19799,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@8.0.4': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.0 + '@babel/helper-globals': 8.0.0 + '@babel/parser': 8.0.4 + '@babel/template': 8.0.0 + '@babel/types': 8.0.4 + obug: 2.1.4 + '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@babel/types@8.0.4': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.4 + '@callstack/react-theme-provider@3.0.9(react@19.2.4)': dependencies: deepmerge: 3.3.0 @@ -18538,7 +19882,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@22.19.15) + '@inquirer/external-editor': 3.0.5(@types/node@22.19.15) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -19521,8 +20865,6 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@gar/promise-retry@1.0.3': {} - '@gerrit0/mini-shiki@3.23.0': dependencies: '@shikijs/engine-oniguruma': 3.23.0 @@ -19535,6 +20877,9 @@ snapshots: dependencies: graphql: 16.13.2 + '@harperfast/extended-iterable@1.0.3': + optional: true + '@hono/node-server@1.19.12(hono@4.12.9)': dependencies: hono: 4.12.9 @@ -19649,27 +20994,28 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@22.19.15)': + '@inquirer/ansi@2.0.8': {} + + '@inquirer/checkbox@5.2.5(@types/node@22.19.15)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/confirm@5.1.14(@types/node@22.19.15)': + '@inquirer/confirm@5.1.21(@types/node@22.19.15)': dependencies: '@inquirer/core': 10.3.2(@types/node@22.19.15) '@inquirer/type': 3.0.10(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/confirm@5.1.21(@types/node@22.19.15)': + '@inquirer/confirm@6.1.1(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) + '@inquirer/core': 11.2.1(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 @@ -19686,23 +21032,46 @@ snapshots: optionalDependencies: '@types/node': 22.19.15 - '@inquirer/editor@4.2.23(@types/node@22.19.15)': + '@inquirer/core@11.2.1(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/external-editor': 1.0.3(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) + '@inquirer/ansi': 2.0.8 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@22.19.15) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 optionalDependencies: '@types/node': 22.19.15 - '@inquirer/expand@4.0.23(@types/node@22.19.15)': + '@inquirer/core@12.0.3(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.8 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@22.19.15) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 22.19.15 + + '@inquirer/editor@5.3.3(@types/node@22.19.15)': + dependencies: + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/external-editor': 3.0.5(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) + optionalDependencies: + '@types/node': 22.19.15 + + '@inquirer/expand@5.1.5(@types/node@22.19.15)': + dependencies: + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/external-editor@1.0.3(@types/node@22.19.15)': + '@inquirer/external-editor@3.0.5(@types/node@22.19.15)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 @@ -19711,67 +21080,66 @@ snapshots: '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@22.19.15)': + '@inquirer/figures@2.0.9': {} + + '@inquirer/input@5.1.6(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/number@3.0.23(@types/node@22.19.15)': + '@inquirer/number@4.2.3(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/password@4.0.23(@types/node@22.19.15)': + '@inquirer/password@5.2.2(@types/node@22.19.15)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/prompts@7.8.2(@types/node@22.19.15)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.19.15) - '@inquirer/confirm': 5.1.21(@types/node@22.19.15) - '@inquirer/editor': 4.2.23(@types/node@22.19.15) - '@inquirer/expand': 4.0.23(@types/node@22.19.15) - '@inquirer/input': 4.3.1(@types/node@22.19.15) - '@inquirer/number': 3.0.23(@types/node@22.19.15) - '@inquirer/password': 4.0.23(@types/node@22.19.15) - '@inquirer/rawlist': 4.1.11(@types/node@22.19.15) - '@inquirer/search': 3.2.2(@types/node@22.19.15) - '@inquirer/select': 4.4.2(@types/node@22.19.15) + '@inquirer/prompts@8.5.2(@types/node@22.19.15)': + dependencies: + '@inquirer/checkbox': 5.2.5(@types/node@22.19.15) + '@inquirer/confirm': 6.1.1(@types/node@22.19.15) + '@inquirer/editor': 5.3.3(@types/node@22.19.15) + '@inquirer/expand': 5.1.5(@types/node@22.19.15) + '@inquirer/input': 5.1.6(@types/node@22.19.15) + '@inquirer/number': 4.2.3(@types/node@22.19.15) + '@inquirer/password': 5.2.2(@types/node@22.19.15) + '@inquirer/rawlist': 5.3.5(@types/node@22.19.15) + '@inquirer/search': 4.3.3(@types/node@22.19.15) + '@inquirer/select': 5.2.5(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/rawlist@4.1.11(@types/node@22.19.15)': + '@inquirer/rawlist@5.3.5(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/search@3.2.2(@types/node@22.19.15)': + '@inquirer/search@4.3.3(@types/node@22.19.15)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 - '@inquirer/select@4.4.2(@types/node@22.19.15)': + '@inquirer/select@5.2.5(@types/node@22.19.15)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@22.19.15) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@22.19.15) optionalDependencies: '@types/node': 22.19.15 @@ -19779,6 +21147,10 @@ snapshots: optionalDependencies: '@types/node': 22.19.15 + '@inquirer/type@4.1.1(@types/node@22.19.15)': + optionalDependencies: + '@types/node': 22.19.15 + '@internationalized/date@3.12.0': dependencies: '@swc/helpers': 0.5.20 @@ -20004,11 +21376,11 @@ snapshots: '@solid-primitives/utils': 6.4.0(solid-js@1.9.12) solid-js: 1.9.12 - '@listr2/prompt-adapter-inquirer@3.0.1(@inquirer/prompts@7.8.2(@types/node@22.19.15))(@types/node@22.19.15)(listr2@9.0.1)': + '@listr2/prompt-adapter-inquirer@4.2.5(@inquirer/prompts@8.5.2(@types/node@22.19.15))(@types/node@22.19.15)(listr2@11.0.0)': dependencies: - '@inquirer/prompts': 7.8.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - listr2: 9.0.1 + '@inquirer/prompts': 8.5.2(@types/node@22.19.15) + '@inquirer/type': 4.1.1(@types/node@22.19.15) + listr2: 11.0.0 transitivePeerDependencies: - '@types/node' @@ -20042,25 +21414,25 @@ snapshots: dependencies: '@lit-labs/ssr-dom-shim': 1.5.1 - '@lmdb/lmdb-darwin-arm64@3.4.2': + '@lmdb/lmdb-darwin-arm64@3.5.6': optional: true - '@lmdb/lmdb-darwin-x64@3.4.2': + '@lmdb/lmdb-darwin-x64@3.5.6': optional: true - '@lmdb/lmdb-linux-arm64@3.4.2': + '@lmdb/lmdb-linux-arm64@3.5.6': optional: true - '@lmdb/lmdb-linux-arm@3.4.2': + '@lmdb/lmdb-linux-arm@3.5.6': optional: true - '@lmdb/lmdb-linux-x64@3.4.2': + '@lmdb/lmdb-linux-x64@3.5.6': optional: true - '@lmdb/lmdb-win32-arm64@3.4.2': + '@lmdb/lmdb-win32-arm64@3.5.6': optional: true - '@lmdb/lmdb-win32-x64@3.4.2': + '@lmdb/lmdb-win32-x64@3.5.6': optional: true '@manypkg/find-root@1.1.0': @@ -20131,7 +21503,7 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} - '@modelcontextprotocol/sdk@1.26.0(zod@4.1.13)': + '@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.12(hono@4.12.9) ajv: 8.18.0 @@ -20148,8 +21520,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.1.13 - zod-to-json-schema: 3.25.2(zod@4.1.13) + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - supports-color @@ -20369,68 +21741,10 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@npmcli/agent@4.0.0': - dependencies: - agent-base: 7.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 11.2.7 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - '@npmcli/fs@3.1.1': dependencies: semver: 7.7.4 - '@npmcli/fs@5.0.0': - dependencies: - semver: 7.7.4 - - '@npmcli/git@7.0.2': - dependencies: - '@gar/promise-retry': 1.0.3 - '@npmcli/promise-spawn': 9.0.1 - ini: 6.0.0 - lru-cache: 11.2.7 - npm-pick-manifest: 11.0.3 - proc-log: 6.1.0 - semver: 7.7.4 - which: 6.0.1 - - '@npmcli/installed-package-contents@4.0.0': - dependencies: - npm-bundled: 5.0.0 - npm-normalize-package-bin: 5.0.0 - - '@npmcli/node-gyp@5.0.0': {} - - '@npmcli/package-json@7.0.5': - dependencies: - '@npmcli/git': 7.0.2 - glob: 13.0.6 - hosted-git-info: 9.0.2 - json-parse-even-better-errors: 5.0.0 - proc-log: 6.1.0 - semver: 7.7.4 - spdx-expression-parse: 4.0.0 - - '@npmcli/promise-spawn@9.0.1': - dependencies: - which: 6.0.1 - - '@npmcli/redact@4.0.0': {} - - '@npmcli/run-script@10.0.4': - dependencies: - '@npmcli/node-gyp': 5.0.0 - '@npmcli/package-json': 7.0.5 - '@npmcli/promise-spawn': 9.0.1 - node-gyp: 12.2.0 - proc-log: 6.1.0 - transitivePeerDependencies: - - supports-color - '@nx/nx-darwin-arm64@22.1.3': optional: true @@ -20472,63 +21786,198 @@ snapshots: '@oslojs/encoding@1.1.0': {} + '@oxc-parser/binding-android-arm-eabi@0.121.0': + optional: true + + '@oxc-parser/binding-android-arm-eabi@0.142.0': + optional: true + '@oxc-parser/binding-android-arm-eabi@0.143.0': optional: true + '@oxc-parser/binding-android-arm64@0.121.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.142.0': + optional: true + '@oxc-parser/binding-android-arm64@0.143.0': optional: true + '@oxc-parser/binding-darwin-arm64@0.121.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.142.0': + optional: true + '@oxc-parser/binding-darwin-arm64@0.143.0': optional: true + '@oxc-parser/binding-darwin-x64@0.121.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.142.0': + optional: true + '@oxc-parser/binding-darwin-x64@0.143.0': optional: true + '@oxc-parser/binding-freebsd-x64@0.121.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.142.0': + optional: true + '@oxc-parser/binding-freebsd-x64@0.143.0': optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.121.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + optional: true + '@oxc-parser/binding-linux-arm-gnueabihf@0.143.0': optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.121.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + optional: true + '@oxc-parser/binding-linux-arm-musleabihf@0.143.0': optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.121.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + optional: true + '@oxc-parser/binding-linux-arm64-gnu@0.143.0': optional: true + '@oxc-parser/binding-linux-arm64-musl@0.121.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + optional: true + '@oxc-parser/binding-linux-arm64-musl@0.143.0': optional: true + '@oxc-parser/binding-linux-ppc64-gnu@0.121.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + optional: true + '@oxc-parser/binding-linux-ppc64-gnu@0.143.0': optional: true + '@oxc-parser/binding-linux-riscv64-gnu@0.121.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + optional: true + '@oxc-parser/binding-linux-riscv64-gnu@0.143.0': optional: true + '@oxc-parser/binding-linux-riscv64-musl@0.121.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + optional: true + '@oxc-parser/binding-linux-riscv64-musl@0.143.0': optional: true + '@oxc-parser/binding-linux-s390x-gnu@0.121.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + optional: true + '@oxc-parser/binding-linux-s390x-gnu@0.143.0': optional: true + '@oxc-parser/binding-linux-x64-gnu@0.121.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + optional: true + '@oxc-parser/binding-linux-x64-gnu@0.143.0': optional: true + '@oxc-parser/binding-linux-x64-musl@0.121.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.142.0': + optional: true + '@oxc-parser/binding-linux-x64-musl@0.143.0': optional: true + '@oxc-parser/binding-openharmony-arm64@0.121.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.142.0': + optional: true + '@oxc-parser/binding-openharmony-arm64@0.143.0': optional: true + '@oxc-parser/binding-wasm32-wasi@0.121.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + dependencies: + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.142.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.121.0': + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + optional: true + '@oxc-parser/binding-win32-arm64-msvc@0.143.0': optional: true + '@oxc-parser/binding-win32-ia32-msvc@0.121.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + optional: true + '@oxc-parser/binding-win32-ia32-msvc@0.143.0': optional: true + '@oxc-parser/binding-win32-x64-msvc@0.121.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + optional: true + '@oxc-parser/binding-win32-x64-msvc@0.143.0': optional: true + '@oxc-project/types@0.121.0': {} + + '@oxc-project/types@0.140.0': {} + + '@oxc-project/types@0.142.0': {} + '@oxc-project/types@0.143.0': {} '@oxc-project/types@0.144.0': {} @@ -20689,19 +22138,38 @@ snapshots: '@poppinss/exception@1.2.3': {} - '@preact/preset-vite@2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@preact/preset-vite@2.10.5(@babel/core@7.29.7)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.7) '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.7) - '@prefresh/vite': 2.4.12(preact@10.29.0)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@prefresh/vite': 2.4.12(preact@10.29.0)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@rollup/pluginutils': 5.3.0(rollup@4.60.1) babel-plugin-transform-hook-names: 1.0.2(@babel/core@7.29.7) debug: 4.4.3 magic-string: 0.30.21 picocolors: 1.1.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vite-prerender-plugin: 0.5.13(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite-prerender-plugin: 0.5.13(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + zimmerframe: 1.1.4 + transitivePeerDependencies: + - preact + - rollup + - supports-color + + '@preact/preset-vite@2.10.5(@babel/core@8.0.1)(preact@10.29.0)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + dependencies: + '@babel/core': 8.0.1 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@8.0.1) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@8.0.1) + '@prefresh/vite': 2.4.12(preact@10.29.0)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + babel-plugin-transform-hook-names: 1.0.2(@babel/core@8.0.1) + debug: 4.4.3 + magic-string: 0.30.21 + picocolors: 1.1.1 + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite-prerender-plugin: 0.5.13(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) zimmerframe: 1.1.4 transitivePeerDependencies: - preact @@ -20716,7 +22184,7 @@ snapshots: '@prefresh/utils@1.2.1': {} - '@prefresh/vite@2.4.12(preact@10.29.0)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@prefresh/vite@2.4.12(preact@10.29.0)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@prefresh/babel-plugin': 0.5.3 @@ -20724,7 +22192,7 @@ snapshots: '@prefresh/utils': 1.2.1 '@rollup/pluginutils': 4.2.1 preact: 10.29.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -20926,45 +22394,94 @@ snapshots: '@remix-run/router@1.23.2': {} + '@rolldown/binding-android-arm64@1.2.0': + optional: true + '@rolldown/binding-android-arm64@1.2.4': optional: true + '@rolldown/binding-darwin-arm64@1.2.0': + optional: true + '@rolldown/binding-darwin-arm64@1.2.4': optional: true + '@rolldown/binding-darwin-x64@1.2.0': + optional: true + '@rolldown/binding-darwin-x64@1.2.4': optional: true + '@rolldown/binding-freebsd-x64@1.2.0': + optional: true + '@rolldown/binding-freebsd-x64@1.2.4': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.2.4': optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.2.4': optional: true + '@rolldown/binding-linux-arm64-musl@1.2.0': + optional: true + '@rolldown/binding-linux-arm64-musl@1.2.4': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.4': optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.4': optional: true + '@rolldown/binding-linux-x64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-x64-gnu@1.2.4': optional: true + '@rolldown/binding-linux-x64-musl@1.2.0': + optional: true + '@rolldown/binding-linux-x64-musl@1.2.4': optional: true + '@rolldown/binding-openharmony-arm64@1.2.0': + optional: true + '@rolldown/binding-openharmony-arm64@1.2.4': optional: true + '@rolldown/binding-wasm32-wasi@1.2.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.0': + optional: true + '@rolldown/binding-win32-arm64-msvc@1.2.4': optional: true + '@rolldown/binding-win32-x64-msvc@1.2.0': + optional: true + '@rolldown/binding-win32-x64-msvc@1.2.4': optional: true @@ -21040,153 +22557,78 @@ snapshots: optionalDependencies: rollup: 4.60.1 - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - '@rollup/rollup-android-arm-eabi@4.60.1': optional: true - '@rollup/rollup-android-arm64@4.59.0': - optional: true - '@rollup/rollup-android-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - '@rollup/rollup-darwin-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - '@rollup/rollup-darwin-x64@4.60.1': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - '@rollup/rollup-freebsd-arm64@4.60.1': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - '@rollup/rollup-freebsd-x64@4.60.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-arm64-musl@4.60.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-loong64-musl@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': - optional: true - '@rollup/rollup-linux-x64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': - optional: true - '@rollup/rollup-linux-x64-musl@4.60.1': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': - optional: true - '@rollup/rollup-openbsd-x64@4.60.1': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': - optional: true - '@rollup/rollup-openharmony-arm64@4.60.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': - optional: true - '@rollup/rollup-win32-x64-gnu@4.60.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': - optional: true - '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true @@ -21224,11 +22666,12 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@schematics/angular@20.3.22(chokidar@5.0.0)': + '@schematics/angular@22.1.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 20.3.22(chokidar@5.0.0) - '@angular-devkit/schematics': 20.3.22(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 + typescript: 6.0.3 transitivePeerDependencies: - chokidar @@ -21303,38 +22746,6 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@sigstore/bundle@4.0.0': - dependencies: - '@sigstore/protobuf-specs': 0.5.0 - - '@sigstore/core@3.2.0': {} - - '@sigstore/protobuf-specs@0.5.0': {} - - '@sigstore/sign@4.1.1': - dependencies: - '@gar/promise-retry': 1.0.3 - '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 - make-fetch-happen: 15.0.5 - proc-log: 6.1.0 - transitivePeerDependencies: - - supports-color - - '@sigstore/tuf@4.0.2': - dependencies: - '@sigstore/protobuf-specs': 0.5.0 - tuf-js: 4.1.0 - transitivePeerDependencies: - - supports-color - - '@sigstore/verify@3.1.0': - dependencies: - '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 - '@sinclair/typebox@0.27.10': {} '@sinclair/typebox@0.34.49': {} @@ -21449,11 +22860,11 @@ snapshots: dependencies: solid-js: 1.9.12 - '@solidjs/start@1.3.2(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@solidjs/start@1.3.2(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@tanstack/server-functions-plugin': 1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) - '@vinxi/server-components': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) + '@tanstack/server-functions-plugin': 1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) + '@vinxi/server-components': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) cookie-es: 2.0.0 defu: 6.1.7 error-stack-parser: 2.1.4 @@ -21465,8 +22876,8 @@ snapshots: source-map-js: 1.2.1 terracotta: 1.1.0(solid-js@1.9.12) tinyglobby: 0.2.17 - vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) - vite-plugin-solid: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) + vite-plugin-solid: 2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - '@testing-library/jest-dom' - solid-js @@ -21498,15 +22909,15 @@ snapshots: dependencies: acorn: 8.16.0 - '@sveltejs/adapter-auto@6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': + '@sveltejs/adapter-auto@6.1.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': dependencies: - '@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - '@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.6.0 @@ -21518,16 +22929,16 @@ snapshots: set-cookie-parser: 3.1.0 sirv: 3.0.2 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) optionalDependencies: typescript: 5.8.3 optional: true - '@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.9(acorn@8.16.0) - '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@types/cookie': 0.6.0 acorn: 8.16.0 cookie: 0.6.0 @@ -21539,7 +22950,7 @@ snapshots: set-cookie-parser: 3.1.0 sirv: 3.0.2 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) optionalDependencies: typescript: 5.8.3 @@ -21554,49 +22965,49 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) debug: 4.4.3 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color optional: true - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) debug: 4.4.3 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) debug: 4.4.3 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.21 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color optional: true - '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) debug: 4.4.3 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.21 svelte: 5.55.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -21676,16 +23087,16 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@tailwindcss/vite@4.2.2(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.2.2 '@tailwindcss/oxide': 4.2.2 tailwindcss: 4.2.2 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) '@tanstack/devtools-event-client@0.4.4': {} - '@tanstack/directive-functions-plugin@1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@tanstack/directive-functions-plugin@1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.29.7 @@ -21694,7 +23105,7 @@ snapshots: '@tanstack/router-utils': 1.161.6 babel-dead-code-elimination: 1.0.12 tiny-invariant: 1.3.3 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color @@ -21758,7 +23169,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/server-functions-plugin@1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@tanstack/server-functions-plugin@1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/code-frame': 7.26.2 '@babel/core': 7.29.7 @@ -21767,7 +23178,7 @@ snapshots: '@babel/template': 7.29.7 '@babel/traverse': 7.29.8 '@babel/types': 7.29.8 - '@tanstack/directive-functions-plugin': 1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@tanstack/directive-functions-plugin': 1.121.21(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) babel-dead-code-elimination: 1.0.12 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -21784,25 +23195,25 @@ snapshots: transitivePeerDependencies: - typescript - '@tanstack/vite-config@0.4.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@tanstack/vite-config@0.4.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: rollup-plugin-preserve-directives: 0.4.0(rollup@4.60.1) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vite-plugin-dts: 4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - vite-plugin-externalize-deps: 0.10.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - vite-tsconfig-paths: 5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite-plugin-dts: 4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite-plugin-externalize-deps: 0.10.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite-tsconfig-paths: 5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - rollup - supports-color - typescript - '@testing-library/angular@18.1.1(3436010aec00f8e828978cdcb451dc36)': + '@testing-library/angular@18.1.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2))(@testing-library/dom@10.4.1)': dependencies: - '@angular/common': 20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/router': 20.3.18(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@20.3.18(@angular/animations@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@20.3.18(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@20.3.18(@angular/compiler@20.3.18)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1) + '@angular/platform-browser': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)) + '@angular/router': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(@angular/platform-browser@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.1)))(rxjs@7.8.2) '@testing-library/dom': 10.4.1 tslib: 2.8.1 @@ -21874,23 +23285,23 @@ snapshots: dependencies: svelte: 5.55.1 - '@testing-library/svelte@5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': + '@testing-library/svelte@5.3.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/svelte-core': 1.0.0(svelte@5.55.1) svelte: 5.55.1 optionalDependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - - '@tsconfig/svelte@5.0.8': {} + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - '@tufjs/canonical-json@2.0.0': {} - - '@tufjs/models@4.1.0': + '@ts-morph/common@0.22.0': dependencies: - '@tufjs/canonical-json': 2.0.0 - minimatch: 10.2.5 + fast-glob: 3.3.3 + minimatch: 9.0.9 + mkdirp: 3.0.1 + path-browserify: 1.0.1 + + '@tsconfig/svelte@5.0.8': {} '@tybys/wasm-util@0.10.3': dependencies: @@ -21926,6 +23337,11 @@ snapshots: dependencies: '@babel/types': 7.29.8 + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 22.19.15 + '@types/braces@3.0.5': {} '@types/chai@5.2.3': @@ -21933,6 +23349,10 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.19.15 + '@types/cookie@0.6.0': {} '@types/debug@4.1.13': @@ -21955,6 +23375,21 @@ snapshots: '@types/estree@1.0.8': {} + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 22.19.15 + '@types/qs': 6.15.1 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.6': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + + '@types/gensync@1.0.5': {} + '@types/graceful-fs@4.1.9': dependencies: '@types/node': 22.19.15 @@ -21969,6 +23404,8 @@ snapshots: '@types/html-minifier-terser@6.1.0': {} + '@types/http-errors@2.0.5': {} + '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -21984,6 +23421,8 @@ snapshots: ast-types: 0.16.1 recast: 0.23.11 + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/mdast@4.0.4': @@ -22010,6 +23449,10 @@ snapshots: '@types/normalize-package-data@2.4.4': {} + '@types/qs@6.15.1': {} + + '@types/range-parser@1.2.7': {} + '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -22020,6 +23463,15 @@ snapshots: '@types/resolve@1.20.2': {} + '@types/send@1.2.1': + dependencies: + '@types/node': 22.19.15 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.19.15 + '@types/sort-by@1.2.3': {} '@types/source-list-map@0.1.6': {} @@ -22247,7 +23699,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 @@ -22262,7 +23714,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -22277,7 +23729,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 @@ -22292,7 +23744,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@7.0.2) typescript: 7.0.2 @@ -22471,10 +23923,10 @@ snapshots: '@urql/core': 5.2.0(graphql@16.13.2) wonka: 6.3.6 - '@vercel/analytics@1.6.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0))(react@19.2.4)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3))': + '@vercel/analytics@1.6.1(@sveltejs/kit@2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0))(react@19.2.4)(svelte@5.55.1)(vue@3.5.31(typescript@5.8.3))': optionalDependencies: - '@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) - next: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0) + '@sveltejs/kit': 2.57.1(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.55.1)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))(svelte@5.55.1)(typescript@5.8.3)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + next: 16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0) react: 19.2.4 svelte: 5.55.1 vue: 3.5.31(typescript@5.8.3) @@ -22553,7 +24005,7 @@ snapshots: untun: 0.1.3 uqr: 0.1.2 - '@vinxi/plugin-directives@0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))': + '@vinxi/plugin-directives@0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))': dependencies: '@babel/parser': 7.29.8 acorn: 8.16.0 @@ -22564,24 +24016,24 @@ snapshots: magicast: 0.2.11 recast: 0.23.11 tslib: 2.8.1 - vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) + vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) - '@vinxi/server-components@0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))': + '@vinxi/server-components@0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0))': dependencies: - '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) + '@vinxi/plugin-directives': 0.5.1(vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0)) acorn: 8.16.0 acorn-loose: 8.5.2 acorn-typescript: 1.4.13(acorn@8.16.0) astring: 1.9.0 magicast: 0.2.11 recast: 0.23.11 - vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) + vinxi: 0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0) - '@vitejs/plugin-basic-ssl@2.1.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitejs/plugin-basic-ssl@2.3.0(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.7) @@ -22589,21 +24041,21 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@5.8.3))': dependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vue: 3.5.31(typescript@5.8.3) - '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@7.0.2))': + '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(vue@3.5.31(typescript@7.0.2))': dependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) vue: 3.5.31(typescript@7.0.2) - '@vitest/coverage-istanbul@4.0.6(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': + '@vitest/coverage-istanbul@4.0.6(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': dependencies: '@istanbuljs/schema': 0.1.3 debug: 4.4.3 @@ -22614,11 +24066,11 @@ snapshots: istanbul-reports: 3.2.0 magicast: 0.3.5 tinyrainbow: 3.1.0 - vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.6.14(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': + '@vitest/eslint-plugin@1.6.14(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)))': dependencies: '@typescript-eslint/scope-manager': 8.58.1 '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) @@ -22626,7 +24078,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) typescript: 6.0.3 - vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vitest: 4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -22639,33 +24091,42 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.14(@types/node@22.19.15)(typescript@5.9.3) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - optional: true + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.14(@types/node@22.19.15)(typescript@6.0.3) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.2 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.12.14(@types/node@22.19.15)(typescript@6.0.3) + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + optional: true + + '@vitest/mocker@4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: msw: 2.12.14(@types/node@22.19.15)(typescript@7.0.2) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.2': dependencies: @@ -22802,6 +24263,19 @@ snapshots: '@vue/devtools-api@6.6.4': {} + '@vue/language-core@2.1.6(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.31 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.31 + computeds: 0.0.1 + minimatch: 9.0.9 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.9.3 + '@vue/language-core@2.1.6(typescript@6.0.3)': dependencies: '@volar/language-core': 2.4.28 @@ -23149,8 +24623,6 @@ snapshots: abbrev@3.0.1: {} - abbrev@4.0.0: {} - abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -23191,6 +24663,8 @@ snapshots: agent-base@7.1.4: {} + agent-base@9.0.0: {} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 @@ -23213,9 +24687,9 @@ snapshots: dependencies: ajv: 6.14.0 - ajv-formats@2.1.1(ajv@8.18.0): + ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: - ajv: 8.18.0 + ajv: 8.20.0 ajv-formats@3.0.1(ajv@8.13.0): optionalDependencies: @@ -23225,13 +24699,17 @@ snapshots: optionalDependencies: ajv: 8.18.0 + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv-keywords@3.5.2(ajv@6.14.0): dependencies: ajv: 6.14.0 - ajv-keywords@5.1.0(ajv@8.18.0): + ajv-keywords@5.1.0(ajv@8.20.0): dependencies: - ajv: 8.18.0 + ajv: 8.20.0 fast-deep-equal: 3.1.3 ajv@6.14.0: @@ -23262,22 +24740,12 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.35.0: - dependencies: - '@algolia/abtesting': 1.1.0 - '@algolia/client-abtesting': 5.35.0 - '@algolia/client-analytics': 5.35.0 - '@algolia/client-common': 5.35.0 - '@algolia/client-insights': 5.35.0 - '@algolia/client-personalization': 5.35.0 - '@algolia/client-query-suggestions': 5.35.0 - '@algolia/client-search': 5.35.0 - '@algolia/ingestion': 1.35.0 - '@algolia/monitoring': 1.35.0 - '@algolia/recommend': 5.35.0 - '@algolia/requester-browser-xhr': 5.35.0 - '@algolia/requester-fetch': 5.35.0 - '@algolia/requester-node-http': 5.35.0 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 alien-signals@1.0.13: {} @@ -23489,7 +24957,7 @@ snapshots: astring@1.9.0: {} - astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0): + astro@5.18.1(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@1.21.7)(lightningcss@1.32.0)(rollup@4.60.1)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(typescript@5.8.3)(yaml@2.9.0): dependencies: '@astrojs/compiler': 2.13.1 '@astrojs/internal-helpers': 0.7.6 @@ -23546,8 +25014,8 @@ snapshots: unist-util-visit: 5.1.0 unstorage: 1.17.5(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1) vfile: 6.0.3 - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 21.1.1 yocto-spinner: 0.2.3 @@ -23717,6 +25185,16 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@8.0.1): + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/core': 8.0.1 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@8.0.1) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -23733,6 +25211,15 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@8.0.1): + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@8.0.1) + core-js-compat: 3.49.0 + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -23740,6 +25227,14 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@8.0.1): + dependencies: + '@babel/core': 8.0.1 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@8.0.1) + transitivePeerDependencies: + - supports-color + optional: true + babel-plugin-react-native-web@0.19.13: {} babel-plugin-syntax-hermes-parser@0.23.1: @@ -23760,6 +25255,10 @@ snapshots: dependencies: '@babel/core': 7.29.7 + babel-plugin-transform-hook-names@1.0.2(@babel/core@8.0.1): + dependencies: + '@babel/core': 8.0.1 + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -23862,7 +25361,7 @@ snapshots: baseline-browser-mapping@2.10.13: {} - beasties@0.3.5: + beasties@0.4.3: dependencies: css-select: 6.0.0 css-what: 7.0.0 @@ -23872,6 +25371,7 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.8 postcss-media-query-parser: 0.2.3 + postcss-safe-parser: 7.0.1(postcss@8.5.8) better-opn@3.0.2: dependencies: @@ -24144,19 +25644,6 @@ snapshots: tar: 6.2.1 unique-filename: 3.0.0 - cacache@20.0.4: - dependencies: - '@npmcli/fs': 5.0.0 - fs-minipass: 3.0.3 - glob: 13.0.6 - lru-cache: 11.2.7 - minipass: 7.1.3 - minipass-collect: 2.0.1 - minipass-flush: 1.0.7 - minipass-pipeline: 1.2.4 - p-map: 7.0.4 - ssri: 13.0.1 - cache-base@1.0.1: dependencies: collection-visit: 1.0.0 @@ -24368,16 +25855,18 @@ snapshots: cli-spinners@2.9.2: {} + cli-spinners@3.4.0: {} + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 - cli-truncate@4.0.0: + cli-truncate@6.1.1: dependencies: - slice-ansi: 5.0.0 - string-width: 7.2.0 + slice-ansi: 9.0.0 + string-width: 8.2.2 cli-width@4.1.0: {} @@ -24419,6 +25908,8 @@ snapshots: cluster-key-slot@1.1.2: {} + code-block-writer@12.0.0: {} + collection-visit@1.0.0: dependencies: map-visit: 1.0.0 @@ -25199,9 +26690,9 @@ snapshots: entities@7.0.1: {} - env-editor@0.4.2: {} + entities@8.1.0: {} - env-paths@2.2.1: {} + env-editor@0.4.2: {} env-paths@4.0.0: dependencies: @@ -25211,8 +26702,6 @@ snapshots: environment@1.1.0: {} - err-code@2.0.3: {} - errno@0.1.8: dependencies: prr: 1.0.1 @@ -26053,8 +27542,18 @@ snapshots: fast-loops@1.1.4: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.0: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + fastest-levenshtein@1.0.16: {} fastq@1.20.1: @@ -26717,16 +28216,16 @@ snapshots: hookable@6.1.1: {} + hosted-git-info@10.1.1: + dependencies: + lru-cache: 11.2.7 + hosted-git-info@2.8.9: {} hosted-git-info@7.0.2: dependencies: lru-cache: 10.4.3 - hosted-git-info@9.0.2: - dependencies: - lru-cache: 11.2.7 - html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -26844,6 +28343,15 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@9.1.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + proxy-agent-negotiate: 1.1.0 + transitivePeerDependencies: + - kerberos + - supports-color + httpxy@0.3.1: {} human-id@4.1.3: {} @@ -26868,10 +28376,6 @@ snapshots: iferr@0.1.5: {} - ignore-walk@8.0.0: - dependencies: - minimatch: 10.2.5 - ignore@5.3.2: {} ignore@7.0.5: {} @@ -26924,8 +28428,6 @@ snapshots: ini@1.3.8: {} - ini@5.0.0: {} - ini@6.0.0: {} inline-style-prefixer@6.0.4: @@ -27063,8 +28565,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.5.0 @@ -27186,8 +28686,6 @@ snapshots: is-unicode-supported@0.1.0: {} - is-unicode-supported@1.3.0: {} - is-unicode-supported@2.1.0: {} is-weakmap@2.0.2: {} @@ -27229,8 +28727,6 @@ snapshots: isexe@3.1.5: {} - isexe@4.0.0: {} - isobject@2.1.0: dependencies: isarray: 1.0.0 @@ -27255,7 +28751,7 @@ snapshots: '@babel/parser': 7.29.8 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -27410,7 +28906,7 @@ snapshots: jest-message-util: 30.3.0 jest-util: 30.3.0 pretty-format: 30.3.0 - semver: 7.7.4 + semver: 7.8.5 synckit: 0.11.12 transitivePeerDependencies: - supports-color @@ -27475,6 +28971,8 @@ snapshots: jose@6.2.2: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -27519,7 +29017,7 @@ snapshots: transitivePeerDependencies: - supports-color - jscodeshift@17.3.0(@babel/preset-env@7.29.2(@babel/core@7.29.7)): + jscodeshift@17.3.0(@babel/preset-env@7.29.2(@babel/core@8.0.1)): dependencies: '@babel/core': 7.29.7 '@babel/parser': 7.29.8 @@ -27540,7 +29038,7 @@ snapshots: tmp: 0.2.5 write-file-atomic: 5.0.1 optionalDependencies: - '@babel/preset-env': 7.29.2(@babel/core@7.29.7) + '@babel/preset-env': 7.29.2(@babel/core@8.0.1) transitivePeerDependencies: - supports-color @@ -27610,8 +29108,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@5.0.0: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -27642,8 +29138,6 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonparse@1.3.1: {} - jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -27844,14 +29338,11 @@ snapshots: untun: 0.1.3 uqr: 0.1.2 - listr2@9.0.1: + listr2@11.0.0: dependencies: - cli-truncate: 4.0.0 - colorette: 2.0.20 - eventemitter3: 5.0.4 - log-update: 6.1.0 - rfdc: 1.4.1 - wrap-ansi: 9.0.2 + cli-truncate: 6.1.1 + log-update: 8.0.0 + wrap-ansi: 10.0.1 lit-element@4.2.2: dependencies: @@ -27869,21 +29360,22 @@ snapshots: lit-element: 4.2.2 lit-html: 3.3.2 - lmdb@3.4.2: + lmdb@3.5.6: dependencies: + '@harperfast/extended-iterable': 1.0.3 msgpackr: 1.11.9 node-addon-api: 6.1.0 node-gyp-build-optional-packages: 5.2.2 ordered-binary: 1.6.1 weak-lru-cache: 1.2.2 optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 3.4.2 - '@lmdb/lmdb-darwin-x64': 3.4.2 - '@lmdb/lmdb-linux-arm': 3.4.2 - '@lmdb/lmdb-linux-arm64': 3.4.2 - '@lmdb/lmdb-linux-x64': 3.4.2 - '@lmdb/lmdb-win32-arm64': 3.4.2 - '@lmdb/lmdb-win32-x64': 3.4.2 + '@lmdb/lmdb-darwin-arm64': 3.5.6 + '@lmdb/lmdb-darwin-x64': 3.5.6 + '@lmdb/lmdb-linux-arm': 3.5.6 + '@lmdb/lmdb-linux-arm64': 3.5.6 + '@lmdb/lmdb-linux-x64': 3.5.6 + '@lmdb/lmdb-win32-arm64': 3.5.6 + '@lmdb/lmdb-win32-x64': 3.5.6 optional: true loader-runner@2.4.0: {} @@ -27961,18 +29453,19 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - log-symbols@6.0.0: + log-symbols@7.0.1: dependencies: - chalk: 5.6.2 - is-unicode-supported: 1.3.0 + is-unicode-supported: 2.1.0 + yoctocolors: 2.1.2 - log-update@6.1.0: + log-update@8.0.0: dependencies: ansi-escapes: 7.3.0 cli-cursor: 5.0.0 - slice-ansi: 7.1.2 + slice-ansi: 9.0.0 + string-width: 8.2.2 strip-ansi: 7.2.0 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.1 longest-streak@3.1.0: {} @@ -28000,11 +29493,11 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.30.17: + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magic-string@0.30.21: + magic-string@1.0.0: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -28039,23 +29532,6 @@ snapshots: dependencies: semver: 7.7.4 - make-fetch-happen@15.0.5: - dependencies: - '@gar/promise-retry': 1.0.3 - '@npmcli/agent': 4.0.0 - '@npmcli/redact': 4.0.0 - cacache: 20.0.4 - http-cache-semantics: 4.2.0 - minipass: 7.1.3 - minipass-fetch: 5.0.2 - minipass-flush: 1.0.7 - minipass-pipeline: 1.2.4 - negotiator: 1.0.0 - proc-log: 6.1.0 - ssri: 13.0.1 - transitivePeerDependencies: - - supports-color - makeerror@1.0.12: dependencies: tmpl: 1.0.5 @@ -28735,14 +30211,6 @@ snapshots: dependencies: minipass: 7.1.3 - minipass-fetch@5.0.2: - dependencies: - minipass: 7.1.3 - minipass-sized: 2.0.0 - minizlib: 3.1.0 - optionalDependencies: - iconv-lite: 0.7.2 - minipass-flush@1.0.7: dependencies: minipass: 3.3.6 @@ -28751,10 +30219,6 @@ snapshots: dependencies: minipass: 3.3.6 - minipass-sized@2.0.0: - dependencies: - minipass: 7.1.3 - minipass@3.3.6: dependencies: yallist: 4.0.0 @@ -28796,6 +30260,8 @@ snapshots: mkdirp@1.0.4: {} + mkdirp@3.0.1: {} + mlly@1.8.2: dependencies: acorn: 8.16.0 @@ -28944,6 +30410,8 @@ snapshots: mute-stream@2.0.0: {} + mute-stream@3.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -28992,7 +30460,7 @@ snapshots: nested-error-stacks@2.1.1: {} - next@14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.90.0): + next@14.2.35(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.101.0): dependencies: '@next/env': 14.2.35 '@swc/helpers': 0.5.5 @@ -29013,12 +30481,12 @@ snapshots: '@next/swc-win32-arm64-msvc': 14.2.33 '@next/swc-win32-ia32-msvc': 14.2.33 '@next/swc-win32-x64-msvc': 14.2.33 - sass: 1.90.0 + sass: 1.101.0 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - next@15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0): + next@15.5.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0): dependencies: '@next/env': 15.5.14 '@swc/helpers': 0.5.15 @@ -29036,13 +30504,13 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.14 '@next/swc-win32-arm64-msvc': 15.5.14 '@next/swc-win32-x64-msvc': 15.5.14 - sass: 1.90.0 + sass: 1.101.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.90.0): + next@16.2.2(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.101.0): dependencies: '@next/env': 16.2.2 '@swc/helpers': 0.5.15 @@ -29061,7 +30529,7 @@ snapshots: '@next/swc-linux-x64-musl': 16.2.2 '@next/swc-win32-arm64-msvc': 16.2.2 '@next/swc-win32-x64-msvc': 16.2.2 - sass: 1.90.0 + sass: 1.101.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -29069,7 +30537,7 @@ snapshots: nice-try@1.0.5: {} - nitropack@2.13.2(@vercel/functions@2.2.13)(idb-keyval@6.2.2)(rolldown@1.2.4)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): + nitropack@2.13.2(@vercel/functions@2.2.13)(idb-keyval@6.2.2)(rolldown@1.2.4)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.60.1) @@ -29134,7 +30602,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.5.0 unenv: 2.0.0-rc.24 - unimport: 6.0.2(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + unimport: 6.0.2(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) unplugin-utils: 0.3.1 unstorage: 1.17.5(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1) untyped: 2.0.0 @@ -29233,21 +30701,6 @@ snapshots: node-gyp-build@4.8.4: {} - node-gyp@12.2.0: - dependencies: - env-paths: 2.2.1 - exponential-backoff: 3.1.3 - graceful-fs: 4.2.11 - make-fetch-happen: 15.0.5 - nopt: 9.0.0 - proc-log: 6.1.0 - semver: 7.7.4 - tar: 7.5.13 - tinyglobby: 0.2.17 - which: 6.0.1 - transitivePeerDependencies: - - supports-color - node-html-parser@6.1.13: dependencies: css-select: 5.2.2 @@ -29291,10 +30744,6 @@ snapshots: dependencies: abbrev: 3.0.1 - nopt@9.0.0: - dependencies: - abbrev: 4.0.0 - normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 @@ -29304,16 +30753,6 @@ snapshots: normalize-path@3.0.0: {} - npm-bundled@5.0.0: - dependencies: - npm-normalize-package-bin: 5.0.0 - - npm-install-checks@8.0.0: - dependencies: - semver: 7.7.4 - - npm-normalize-package-bin@5.0.0: {} - npm-package-arg@11.0.3: dependencies: hosted-git-info: 7.0.2 @@ -29321,37 +30760,12 @@ snapshots: semver: 7.7.4 validate-npm-package-name: 5.0.1 - npm-package-arg@13.0.0: - dependencies: - hosted-git-info: 9.0.2 - proc-log: 5.0.0 - semver: 7.7.4 - validate-npm-package-name: 6.0.2 - - npm-packlist@10.0.4: - dependencies: - ignore-walk: 8.0.0 - proc-log: 6.1.0 - - npm-pick-manifest@11.0.3: + npm-package-arg@14.0.0: dependencies: - npm-install-checks: 8.0.0 - npm-normalize-package-bin: 5.0.0 - npm-package-arg: 13.0.0 + hosted-git-info: 10.1.1 + proc-log: 7.0.0 semver: 7.7.4 - - npm-registry-fetch@19.1.1: - dependencies: - '@npmcli/redact': 4.0.0 - jsonparse: 1.3.1 - make-fetch-happen: 15.0.5 - minipass: 7.1.3 - minipass-fetch: 5.0.2 - minizlib: 3.1.0 - npm-package-arg: 13.0.0 - proc-log: 6.1.0 - transitivePeerDependencies: - - supports-color + validate-npm-package-name: 8.0.0 npm-run-all2@5.0.2: dependencies: @@ -29618,17 +31032,16 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - ora@8.2.0: + ora@9.4.1: dependencies: chalk: 5.6.2 cli-cursor: 5.0.0 - cli-spinners: 2.9.2 + cli-spinners: 3.4.0 is-interactive: 2.0.0 is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.2.0 + log-symbols: 7.0.1 + stdin-discarder: 0.3.2 + string-width: 8.2.2 ordered-binary@1.6.1: optional: true @@ -29645,6 +31058,59 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-parser@0.121.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2): + dependencies: + '@oxc-project/types': 0.121.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.121.0 + '@oxc-parser/binding-android-arm64': 0.121.0 + '@oxc-parser/binding-darwin-arm64': 0.121.0 + '@oxc-parser/binding-darwin-x64': 0.121.0 + '@oxc-parser/binding-freebsd-x64': 0.121.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.121.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.121.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.121.0 + '@oxc-parser/binding-linux-arm64-musl': 0.121.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.121.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.121.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.121.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.121.0 + '@oxc-parser/binding-linux-x64-gnu': 0.121.0 + '@oxc-parser/binding-linux-x64-musl': 0.121.0 + '@oxc-parser/binding-openharmony-arm64': 0.121.0 + '@oxc-parser/binding-wasm32-wasi': 0.121.0(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@oxc-parser/binding-win32-arm64-msvc': 0.121.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.121.0 + '@oxc-parser/binding-win32-x64-msvc': 0.121.0 + transitivePeerDependencies: + - '@emnapi/core' + - '@emnapi/runtime' + + oxc-parser@0.142.0: + dependencies: + '@oxc-project/types': 0.142.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.142.0 + '@oxc-parser/binding-android-arm64': 0.142.0 + '@oxc-parser/binding-darwin-arm64': 0.142.0 + '@oxc-parser/binding-darwin-x64': 0.142.0 + '@oxc-parser/binding-freebsd-x64': 0.142.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.142.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.142.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.142.0 + '@oxc-parser/binding-linux-arm64-musl': 0.142.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.142.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-musl': 0.142.0 + '@oxc-parser/binding-openharmony-arm64': 0.142.0 + '@oxc-parser/binding-wasm32-wasi': 0.142.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.142.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.142.0 + '@oxc-parser/binding-win32-x64-msvc': 0.142.0 + oxc-parser@0.143.0: dependencies: '@oxc-project/types': 0.143.0 @@ -29749,8 +31215,6 @@ snapshots: p-map@6.0.0: {} - p-map@7.0.4: {} - p-queue@6.6.2: dependencies: eventemitter3: 4.0.7 @@ -29779,28 +31243,6 @@ snapshots: package-manager-detector@1.6.0: {} - pacote@21.0.4: - dependencies: - '@npmcli/git': 7.0.2 - '@npmcli/installed-package-contents': 4.0.0 - '@npmcli/package-json': 7.0.5 - '@npmcli/promise-spawn': 9.0.1 - '@npmcli/run-script': 10.0.4 - cacache: 20.0.4 - fs-minipass: 3.0.3 - minipass: 7.1.3 - npm-package-arg: 13.0.0 - npm-packlist: 10.0.4 - npm-pick-manifest: 11.0.3 - npm-registry-fetch: 19.1.1 - proc-log: 6.1.0 - promise-retry: 2.0.1 - sigstore: 4.1.0 - ssri: 13.0.1 - tar: 7.5.13 - transitivePeerDependencies: - - supports-color - pako@1.0.11: {} parallel-transform@1.2.0: @@ -29861,9 +31303,9 @@ snapshots: parse-statements@1.0.11: {} - parse5-html-rewriting-stream@8.0.0: + parse5-html-rewriting-stream@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.1.0 parse5: 8.0.0 parse5-sax-parser: 8.0.0 @@ -29968,8 +31410,6 @@ snapshots: picomatch@3.0.2: {} - picomatch@4.0.4: {} - picomatch@4.0.5: {} pidtree@0.5.0: {} @@ -29980,7 +31420,7 @@ snapshots: pirates@4.0.7: {} - piscina@5.1.3: + piscina@5.2.0: optionalDependencies: '@napi-rs/nice': 1.1.1 @@ -30159,9 +31599,7 @@ snapshots: proc-log@4.2.0: {} - proc-log@5.0.0: {} - - proc-log@6.1.0: {} + proc-log@7.0.0: {} process-nextick-args@2.0.1: {} @@ -30173,11 +31611,6 @@ snapshots: optionalDependencies: bluebird: 3.7.2 - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - promise@7.3.1: dependencies: asap: 2.0.6 @@ -30204,6 +31637,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-agent-negotiate@1.1.0: {} + proxy-from-env@2.1.0: {} prr@1.0.1: {} @@ -30791,12 +32226,6 @@ snapshots: resolve.exports@2.0.3: {} - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -30858,14 +32287,10 @@ snapshots: retext-stringify: 4.0.0 unified: 11.0.5 - retry@0.12.0: {} - rettime@0.10.1: {} reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@2.6.3: dependencies: glob: 7.2.3 @@ -30898,6 +32323,27 @@ snapshots: transitivePeerDependencies: - oxc-resolver + rolldown@1.2.0: + dependencies: + '@oxc-project/types': 0.140.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.0 + '@rolldown/binding-darwin-arm64': 1.2.0 + '@rolldown/binding-darwin-x64': 1.2.0 + '@rolldown/binding-freebsd-x64': 1.2.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.0 + '@rolldown/binding-linux-arm64-gnu': 1.2.0 + '@rolldown/binding-linux-arm64-musl': 1.2.0 + '@rolldown/binding-linux-ppc64-gnu': 1.2.0 + '@rolldown/binding-linux-s390x-gnu': 1.2.0 + '@rolldown/binding-linux-x64-gnu': 1.2.0 + '@rolldown/binding-linux-x64-musl': 1.2.0 + '@rolldown/binding-openharmony-arm64': 1.2.0 + '@rolldown/binding-wasm32-wasi': 1.2.0 + '@rolldown/binding-win32-arm64-msvc': 1.2.0 + '@rolldown/binding-win32-x64-msvc': 1.2.0 + rolldown@1.2.4: dependencies: '@oxc-project/types': 0.144.0 @@ -30934,37 +32380,6 @@ snapshots: rolldown: 1.2.4 rollup: 4.60.1 - rollup@4.59.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - rollup@4.60.1: dependencies: '@types/estree': 1.0.8 @@ -31066,7 +32481,7 @@ snapshots: safer-buffer@2.1.2: {} - sass@1.90.0: + sass@1.101.0: dependencies: chokidar: 5.0.0 immutable: 5.1.5 @@ -31105,9 +32520,9 @@ snapshots: schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) scule@1.3.0: {} @@ -31124,10 +32539,10 @@ snapshots: dependencies: lru-cache: 6.0.0 - semver@7.7.2: {} - semver@7.7.4: {} + semver@7.8.5: {} + send@0.19.2: dependencies: debug: 2.6.9 @@ -31382,17 +32797,6 @@ snapshots: signal-exit@4.1.0: {} - sigstore@4.1.0: - dependencies: - '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 - '@sigstore/sign': 4.1.1 - '@sigstore/tuf': 4.0.2 - '@sigstore/verify': 3.1.0 - transitivePeerDependencies: - - supports-color - simple-code-frame@1.3.0: dependencies: kolorist: 1.8.0 @@ -31435,20 +32839,13 @@ snapshots: slash@5.1.0: {} - slice-ansi@5.0.0: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 4.0.0 - - slice-ansi@7.1.2: + slice-ansi@9.0.0: dependencies: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 slugify@1.6.8: {} - smart-buffer@4.2.0: {} - smob@1.6.1: {} smol-toml@1.8.0: {} @@ -31476,19 +32873,6 @@ snapshots: transitivePeerDependencies: - supports-color - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - solid-js@1.9.12: dependencies: csstype: 3.2.3 @@ -31597,10 +32981,6 @@ snapshots: dependencies: minipass: 7.1.3 - ssri@13.0.1: - dependencies: - minipass: 7.1.3 - ssri@6.0.2: dependencies: figgy-pudding: 3.5.2 @@ -31636,7 +33016,7 @@ snapshots: std-env@4.0.0: {} - stdin-discarder@0.2.2: {} + stdin-discarder@0.3.2: {} stop-iteration-iterator@1.1.0: dependencies: @@ -31702,6 +33082,11 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string-width@8.2.2: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 @@ -32131,11 +33516,6 @@ snapshots: tinyexec@1.3.0: {} - tinyglobby@0.2.14: - dependencies: - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 - tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) @@ -32241,12 +33621,21 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-morph@21.0.1: + dependencies: + '@ts-morph/common': 0.22.0 + code-block-writer: 12.0.0 + ts-pattern@5.9.0: {} tsconfck@3.1.6(typescript@5.8.3): optionalDependencies: typescript: 5.8.3 + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + tsconfck@3.1.6(typescript@6.0.3): optionalDependencies: typescript: 6.0.3 @@ -32295,14 +33684,6 @@ snapshots: tty-browserify@0.0.0: {} - tuf-js@4.1.0: - dependencies: - '@tufjs/models': 4.1.0 - debug: 4.4.3 - make-fetch-happen: 15.0.5 - transitivePeerDependencies: - - supports-color - tupleson@0.23.1: {} type-check@0.4.0: @@ -32531,7 +33912,7 @@ snapshots: ofetch: 1.5.1 ohash: 2.0.11 - unimport@6.0.2(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): + unimport@6.0.2(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): dependencies: acorn: 8.16.0 escape-string-regexp: 5.0.0 @@ -32545,7 +33926,7 @@ snapshots: scule: 1.3.0 strip-literal: 3.1.0 tinyglobby: 0.2.17 - unplugin: 3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + unplugin: 3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) unplugin-utils: 0.3.1 transitivePeerDependencies: - '@farmfe/core' @@ -32637,18 +34018,18 @@ snapshots: unpipe@1.0.0: {} - unplugin-solid@2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): + unplugin-solid@2.0.0(@testing-library/jest-dom@6.9.1)(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): dependencies: '@babel/core': 7.29.7 babel-preset-solid: 1.9.12(@babel/core@7.29.7)(solid-js@1.9.12) merge-anything: 6.0.6 solid-js: 1.9.12 solid-refresh: 0.7.8(solid-js@1.9.12) - unplugin: 3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + unplugin: 3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) optionalDependencies: '@testing-library/jest-dom': 6.9.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - '@farmfe/core' - '@rspack/core' @@ -32672,7 +34053,7 @@ snapshots: picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 - unplugin@3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): + unplugin@3.3.0(esbuild@0.27.4)(rolldown@1.2.4)(rollup@4.60.1)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)): dependencies: '@jridgewell/remapping': 2.3.5 picomatch: 4.0.5 @@ -32681,7 +34062,7 @@ snapshots: esbuild: 0.27.4 rolldown: 1.2.4 rollup: 4.60.1 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) webpack: 5.105.4(esbuild@0.27.4) unrs-resolver@1.11.1: @@ -32813,7 +34194,7 @@ snapshots: validate-npm-package-name@5.0.1: {} - validate-npm-package-name@6.0.2: {} + validate-npm-package-name@8.0.0: {} vary@1.1.2: {} @@ -32834,7 +34215,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0): + vinxi@0.5.11(@types/node@22.19.15)(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1)(jiti@2.7.0)(lightningcss@1.32.0)(rolldown@1.2.4)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(webpack@5.105.4(esbuild@0.27.4))(yaml@2.9.0): dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.7) @@ -32855,7 +34236,7 @@ snapshots: hookable: 5.5.3 http-proxy: 1.18.1 micromatch: 4.0.8 - nitropack: 2.13.2(@vercel/functions@2.2.13)(idb-keyval@6.2.2)(rolldown@1.2.4)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) + nitropack: 2.13.2(@vercel/functions@2.2.13)(idb-keyval@6.2.2)(rolldown@1.2.4)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0))(webpack@5.105.4(esbuild@0.27.4)) node-fetch-native: 1.6.7 path-to-regexp: 6.3.0 pathe: 1.1.2 @@ -32868,7 +34249,7 @@ snapshots: unctx: 2.5.0 unenv: 1.10.0 unstorage: 1.17.5(@vercel/functions@2.2.13)(db0@0.3.4)(idb-keyval@6.2.2)(ioredis@5.10.1) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) zod: 4.4.3 transitivePeerDependencies: - '@azure/app-configuration' @@ -32921,7 +34302,26 @@ snapshots: - xml2js - yaml - vite-plugin-dts@4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-plugin-dts@4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@microsoft/api-extractor': 7.47.7(@types/node@22.19.15) + '@rollup/pluginutils': 5.3.0(rollup@4.60.1) + '@volar/typescript': 2.4.28 + '@vue/language-core': 2.1.6(typescript@5.9.3) + compare-versions: 6.1.1 + debug: 4.4.3 + kolorist: 1.8.0 + local-pkg: 0.5.1 + magic-string: 0.30.21 + typescript: 5.9.3 + optionalDependencies: + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite-plugin-dts@4.2.3(@types/node@22.19.15)(rollup@4.60.1)(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@microsoft/api-extractor': 7.47.7(@types/node@22.19.15) '@rollup/pluginutils': 5.3.0(rollup@4.60.1) @@ -32934,21 +34334,21 @@ snapshots: magic-string: 0.30.21 typescript: 6.0.3 optionalDependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-externalize-deps@0.10.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-plugin-externalize-deps@0.10.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vite-plugin-externalize-deps@0.9.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-plugin-externalize-deps@0.9.0(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vite-plugin-solid@2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-plugin-solid@2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 @@ -32956,14 +34356,14 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.12 solid-refresh: 0.6.3(solid-js@1.9.12) - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) optionalDependencies: '@testing-library/jest-dom': 6.9.1 transitivePeerDependencies: - supports-color - vite-plugin-solid@2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-plugin-solid@2.11.11(@testing-library/jest-dom@6.9.1)(solid-js@1.9.12)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 @@ -32971,14 +34371,14 @@ snapshots: merge-anything: 5.1.7 solid-js: 1.9.12 solid-refresh: 0.6.3(solid-js@1.9.12) - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) optionalDependencies: '@testing-library/jest-dom': 6.9.1 transitivePeerDependencies: - supports-color - vite-prerender-plugin@0.5.13(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-prerender-plugin@0.5.13(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: kolorist: 1.8.0 magic-string: 0.30.21 @@ -32986,20 +34386,31 @@ snapshots: simple-code-frame: 1.3.0 source-map: 0.7.6 stack-trace: 1.0.0-pre2 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + + vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + debug: 4.4.3 + globrex: 0.1.2 + tsconfck: 3.1.6(typescript@5.9.3) + optionalDependencies: + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + - typescript - vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite-tsconfig-paths@5.1.4(typescript@6.0.3)(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@6.0.3) optionalDependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): + vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.5) @@ -33012,12 +34423,12 @@ snapshots: fsevents: 2.3.3 jiti: 1.21.7 lightningcss: 1.32.0 - sass: 1.90.0 + sass: 1.101.0 terser: 5.46.1 tsx: 4.21.0 yaml: 2.9.0 - vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): + vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.5) @@ -33030,23 +34441,41 @@ snapshots: fsevents: 2.3.3 jiti: 2.7.0 lightningcss: 1.32.0 - sass: 1.90.0 + sass: 1.101.0 terser: 5.46.1 tsx: 4.21.0 yaml: 2.9.0 - vitefu@1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0): + dependencies: + esbuild: 0.27.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.8 + rollup: 4.60.1 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.19.15 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + sass: 1.101.0 + terser: 5.46.1 + tsx: 4.21.0 + yaml: 2.9.0 + + vitefu@1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): optionalDependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@1.21.7)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitefu@1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): optionalDependencies: - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) - vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.2 '@vitest/runner': 4.1.2 '@vitest/snapshot': 4.1.2 @@ -33063,19 +34492,46 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.15 + jsdom: 27.4.0 + transitivePeerDependencies: + - msw + + vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.15 jsdom: 27.4.0 transitivePeerDependencies: - msw - optional: true - vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@6.0.3))(vite@7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.2 '@vitest/runner': 4.1.2 '@vitest/snapshot': 4.1.2 @@ -33092,18 +34548,19 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 7.1.11(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.15 jsdom: 27.4.0 transitivePeerDependencies: - msw + optional: true - vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): + vitest@4.1.2(@types/node@22.19.15)(jsdom@27.4.0)(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.2(msw@2.12.14(@types/node@22.19.15)(typescript@7.0.2))(vite@6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.2 '@vitest/runner': 4.1.2 '@vitest/snapshot': 4.1.2 @@ -33120,7 +34577,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.90.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) + vite: 6.4.1(@types/node@22.19.15)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.15 @@ -33243,7 +34700,7 @@ snapshots: eslint-visitor-keys: 5.0.1 espree: 11.2.0 esquery: 1.7.0 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -33305,14 +34762,8 @@ snapshots: chokidar: 5.0.0 watchpack-chokidar2: 2.0.1 - watchpack@2.4.4: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - watchpack@2.5.1: + watchpack@2.5.2: dependencies: - glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 wcwidth@1.0.1: @@ -33437,7 +34888,7 @@ snapshots: schema-utils: 4.3.3 tapable: 2.3.2 terser-webpack-plugin: 5.4.0(esbuild@0.27.4)(webpack@5.105.4(esbuild@0.27.4)) - watchpack: 2.5.1 + watchpack: 2.5.2 webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' @@ -33470,7 +34921,7 @@ snapshots: schema-utils: 4.3.3 tapable: 2.3.2 terser-webpack-plugin: 5.4.0(webpack@5.105.4) - watchpack: 2.5.1 + watchpack: 2.5.2 webpack-sources: 3.3.4 optionalDependencies: webpack-cli: 5.1.4(webpack@5.105.4) @@ -33565,10 +35016,6 @@ snapshots: dependencies: isexe: 3.1.5 - which@6.0.1: - dependencies: - isexe: 4.0.0 - why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -33588,6 +35035,11 @@ snapshots: dependencies: errno: 0.1.8 + wrap-ansi@10.0.1: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -33650,6 +35102,8 @@ snapshots: xdg-basedir@5.1.0: {} + xhr2@0.2.1: {} + xml-name-validator@4.0.0: {} xml-name-validator@5.0.0: {} @@ -33736,6 +35190,15 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 + yargs@18.1.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 8.2.2 + y18n: 5.0.8 + yargs-parser: 22.0.0 + yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} @@ -33812,9 +35275,9 @@ snapshots: dependencies: zod: 3.25.76 - zod-to-json-schema@3.25.2(zod@4.1.13): + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: - zod: 4.1.13 + zod: 4.4.3 zod-to-ts@1.2.0(typescript@5.8.3)(zod@3.25.76): dependencies: @@ -33827,10 +35290,8 @@ snapshots: zod@3.25.76: {} - zod@4.1.13: {} - zod@4.4.3: {} - zone.js@0.15.0: {} + zone.js@0.16.1: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4e37d863ded..a2b08110a9c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -25,6 +25,7 @@ packages: - '!examples/vue/nuxt*' overrides: + '@inquirer/external-editor': '3.0.5' 'chokidar': '5.0.0' '@types/react': '^19.2.7' '@types/react-dom': '^19.2.3' @@ -42,6 +43,7 @@ overrides: '@typescript-eslint/visitor-keys': '8.58.1' typescript-eslint: '8.58.1' vite: '^6.4.1' + '@angular/build@22.1.8>vite': '7.1.11' esbuild: '^0.27.2' allowBuilds: diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index cb434c61969..3d633e39727 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -189,11 +189,11 @@ async function generatePackageReferenceDocs(pkg: PackageReferenceDocsConfig) { const packages: Array = [ { entryPoints: [ - resolve(__dirname, '../packages/angular-query-experimental/src/index.ts'), + resolve(__dirname, '../packages/angular-query/src/index.ts'), ], tsconfig: resolve( __dirname, - '../packages/angular-query-experimental/tsconfig.json', + '../packages/angular-query/tsconfig.json', ), outputDir: resolve(__dirname, '../docs/framework/angular/reference'), },