-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
feat(angular-query): integrate with Angular v19+ PendingTasks for whenStable() support #9666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughAdds a PendingTasks compatibility token and integrates PendingTasks into query and mutation lifecycles; queries add/remove pending tasks on fetch and wrap refetch to set options; mutations track isPending to add/clear tasks; extensive tests, docs note, ESLint adjustments, and an rxjs devDependency were added. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor C as Component/Test
participant Q as createBaseQuery
participant O as QueryObserver
participant PT as PENDING_TASKS
participant AR as ApplicationRef
Note over C,Q: Query initialization and subscription
C->>Q: createBaseQuery()
Q->>O: create + subscribe
O-->>Q: state updates (fetchStatus)
rect rgb(240,248,255)
Note right of Q: PendingTasks integration (query)
Q->>PT: add() when fetchStatus == "fetching"
PT-->>Q: PendingTaskRef (dispose)
Q->>PT: dispose() when fetchStatus == "idle"
end
Note over C,Q: Refetch wrap
C->>Q: result.refetch()
Q->>O: setOptions(defaultedOptionsSignal())
Q->>O: refetch(...)
Note over AR: whenStable() waits while tasks pending
C->>AR: whenStable()
AR-->>C: resolves after PendingTaskRef disposed
sequenceDiagram
autonumber
actor C as Component/Test
participant M as injectMutation
participant MO as MutationObserver
participant PT as PENDING_TASKS
participant AR as ApplicationRef
C->>M: injectMutation()
M->>MO: observe state
rect rgb(255,250,240)
Note right of M: PendingTasks integration (mutation)
MO-->>M: state.isPending = true
M->>PT: add()
PT-->>M: PendingTaskRef
MO-->>M: state.isPending = false
M->>PT: dispose()
end
Note over C,M: Cleanup
C-->>M: destroy
M->>PT: dispose (if active)
M-->>MO: unsubscribe
C->>AR: whenStable()
AR-->>C: resolves after pending disposed
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit e2a8d8c
☁️ Nx Cloud last updated this comment at |
|
Sizes for commit e2a8d8c:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (8)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)
507-555: Simplify retry test timing to reduce flakinessYou can rely on whenStable() alone after mutate() to avoid multiple tick/advance cycles.
Apply this minimal change:
- // Synchronize pending effects for each retry attempt - TestBed.tick() - await Promise.resolve() - await vi.advanceTimersByTimeAsync(10) - - TestBed.tick() - await Promise.resolve() - await vi.advanceTimersByTimeAsync(10) - - TestBed.tick() - - const stablePromise = app.whenStable() - await Promise.resolve() - await vi.advanceTimersByTimeAsync(10) - await stablePromise + // Wait for all retries to complete via PendingTasks + await app.whenStable()
609-659: Add an assertion for the optimistic state to strengthen coverageRight after mutate(), verify the optimistic cache write took effect before final resolution.
Apply this addition:
// Start mutation mutation.mutate('test') // Synchronize pending effects TestBed.tick() + // Optimistic update should be visible immediately + expect(queryClient.getQueryData(testQueryKey)).toBe('optimistic: test') + const stablePromise = app.whenStable()
661-695: Misleading test name (no cancellation occurs)Rename to reflect behavior (successful synchronous mutation) or add a real cancellation step.
Apply either rename:
- test('should handle synchronous mutation cancellation', async () => { + test('should complete synchronous mutation', async () => {—or add a cancellation (example):
// Start mutation mutation.mutate('test') - // Synchronize pending effects + // Cancel immediately to simulate cancellation path + queryClient.cancelMutations({ mutationKey: ['cancel-sync'] }) + + // Synchronize pending effects TestBed.tick() const stablePromise = app.whenStable() @@ - // Synchronous mutations complete immediately - expect(mutation.isSuccess()).toBe(true) - expect(mutation.data()).toBe('processed: test') + // After cancellation, mutation should not report success + expect(mutation.isPending()).toBe(false) + expect(mutation.isSuccess()).toBe(false)packages/angular-query-experimental/src/__tests__/inject-query.test.ts (3)
594-637: HTTP test is well-structured; minor cleanup optionalThe scheduled flush works; verify() is correctly called. Consider using httpTestingController.expectOne/flush without setTimeout and relying purely on whenStable to reduce timer coupling.
639-680: Reduce over-synchronization in staleTime flowYou can await whenStable() after refetch() and drop extra microtask/timer steps.
Apply this diff:
- await query.refetch() - await Promise.resolve() - await vi.runAllTimersAsync() - await app.whenStable() + await query.refetch() + await app.whenStable()
723-767: Streamline invalidation syncPrefer awaiting invalidateQueries and then whenStable instead of manual ticks/advances.
Apply this diff:
- // 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() + // Invalidate and wait for PendingTasks to settle + await queryClient.invalidateQueries({ queryKey: testKey }) + await app.whenStable()packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (2)
212-276: Offline retry pause behavior validated; consider an explicit check for task count if availableCurrent assertions are strong. If the compat exposes a public API to read active task count, add it to harden the guarantee that stability remains blocked while paused.
Would you like a follow-up to probe the repository for a readable PendingTasks debug API and add an assertion?
279-334: Component destruction tests: split concerns to remove ambiguityBoth query and mutation are instantiated in TestComponent; in the mutation test, a query is also running. Prefer dedicated components per test to isolate behavior.
Proposed change outline:
- Create QueryOnlyComponent (query active, no mutation).
- Create MutationOnlyComponent (mutation active, query disabled or absent).
- Use each component in its respective test to avoid cross-effects.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md(1 hunks)packages/angular-query-experimental/package.json(1 hunks)packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts(2 hunks)packages/angular-query-experimental/src/__tests__/inject-query.test.ts(4 hunks)packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts(1 hunks)packages/angular-query-experimental/src/create-base-query.ts(4 hunks)packages/angular-query-experimental/src/inject-mutation.ts(4 hunks)packages/angular-query-experimental/src/pending-tasks-compat.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
PR: TanStack/query#9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.
Applied to files:
packages/angular-query-experimental/src/create-base-query.ts
🧬 Code graph analysis (5)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (2)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)
packages/angular-query-experimental/src/create-base-query.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/angular-query-experimental/src/signal-proxy.ts (1)
signalProxy(14-46)
packages/angular-query-experimental/src/inject-mutation.ts (1)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (2)
packages/angular-query-experimental/src/inject-query.ts (1)
injectQuery(218-226)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
🔇 Additional comments (18)
packages/angular-query-experimental/package.json (1)
100-100: LGTM! RxJS dependency addition is appropriate.The addition of RxJS as a devDependency is appropriate for testing purposes, particularly for testing asynchronous operations and observables in the PendingTasks integration.
packages/angular-query-experimental/src/pending-tasks-compat.ts (1)
1-28: Well-designed compatibility layer for Angular version differences!The implementation elegantly handles different Angular versions:
- Correctly detects PendingTasks (v19+) or ExperimentalPendingTasks (v18)
- Provides a stable no-op fallback for v17 and earlier
- Uses proper optional injection to handle missing services gracefully
This design ensures backward compatibility while enabling the new functionality where available.
packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md (1)
1-264: Excellent documentation covering all aspects of the PendingTasks integration!The documentation is comprehensive and well-structured:
- Clear problem statement and benefits
- Detailed implementation guidance with code examples
- Coverage of edge cases (offline, paused queries, destruction)
- Helpful migration notes and testing patterns
- Good contributor guidelines
The documentation effectively explains the integration's value for SSR, testing, and zoneless applications.
packages/angular-query-experimental/src/inject-mutation.ts (2)
139-148: Correct PendingTasks tracking for mutations.The implementation properly:
- Creates a pending task when
isPendingbecomes true- Clears it when the mutation completes
- Includes appropriate guards against duplicate registrations
163-170: Proper cleanup on component destruction.Good defensive programming - ensures pending tasks are cleaned up even if the component is destroyed while a mutation is in progress.
packages/angular-query-experimental/src/create-base-query.ts (3)
120-127: Excellent handling of query fetch states with PendingTasks.The implementation correctly:
- Registers a pending task when fetching starts
- Only clears when truly idle (not when paused for offline retries)
- Guards against duplicate registrations
This ensures
whenStable()remains reliable even during network interruptions.
147-153: Robust cleanup on effect teardown.Properly ensures pending tasks are cleaned up when the effect is destroyed, preventing memory leaks.
167-171: Approve — refetch wrapper maintains query semantics.It updates observer options from signals, forwards all arguments to the original refetch, and returns its result — preserving parameter/return semantics and the original 'this' binding.
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)
2-2: Good addition: using ApplicationRef for whenStable coordinationImport is appropriate for PendingTasks-backed stability checks.
471-505: whenStable assertion pattern looks solidCovers the pending→success transition and data; timing margin (60ms > 50ms) is sensible.
557-607: Concurrent synchronous mutations on same key: LGTMNice coverage for same-key, separate observers; assertions are crisp.
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (3)
2-2: Imports for whenStable, HttpClient testing, and lastValueFrom: OKAll required pieces are present for PendingTasks + HttpClient scenarios.
Also applies to: 12-16, 27-27
570-593: Core whenStable query completion: LGTMGood demonstration that PendingTasks blocks stability until async queryFn resolves.
682-721: Enabled→disabled transition with sync queryFn: LGTMCovers the PendingTasks gating while disabled and the immediate sync resolution when enabled.
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (4)
1-22: New suite provides broad PendingTasks coverageGreat scope: sync/async paths, HTTP, retries, offline pause, destruction, and concurrency.
54-79: Synchronous queryFn + whenStable: LGTMDemonstrates that even sync producers still require a stability turn before signals update.
156-182: Race during initial subscription handled wellResolving before first turn and asserting post-whenStable correctness is on point.
385-433: Concurrent mutations: LGTMCovers different durations and an effectively synchronous path; stability gating behaves as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
packages/angular-query-experimental/src/pending-tasks-compat.ts (1)
1-3: Consider using a more specific import for better tree-shaking.While the namespace import works, it could impact tree-shaking. Consider importing only what you need from Angular core.
-import { InjectionToken, inject } from '@angular/core' -import * as ng from '@angular/core' +import { InjectionToken, inject } from '@angular/core' import { noop } from '@tanstack/query-core' + +// Access Angular internals for PendingTasks compatibility +const ng = await import('@angular/core')packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
523-523: Consider adding await or removing async from synchronous mutation functions.The ESLint warnings indicate async functions without await expressions. While these are intentionally synchronous for testing, consider either:
- Removing the
asynckeyword if the function body is synchronous- Adding a comment to suppress the warning if async is required for type compatibility
For line 523:
- mutationFn: async (data: string) => { + mutationFn: (data: string) => {For line 572:
- mutationFn: async (data: string) => { + mutationFn: (data: string) => {Also applies to: 572-572
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
184-184: Consider adding "refetches" to the project dictionary.The pipeline warning indicates "refetches" is an unknown word, but it's a valid technical term in the context of TanStack Query. Consider adding it to your spell-check dictionary or ignore list.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (8)
packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md(1 hunks)packages/angular-query-experimental/package.json(1 hunks)packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts(2 hunks)packages/angular-query-experimental/src/__tests__/inject-query.test.ts(4 hunks)packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts(1 hunks)packages/angular-query-experimental/src/create-base-query.ts(4 hunks)packages/angular-query-experimental/src/inject-mutation.ts(4 hunks)packages/angular-query-experimental/src/pending-tasks-compat.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
PR: TanStack/query#9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.
Applied to files:
packages/angular-query-experimental/src/create-base-query.ts
🧬 Code graph analysis (5)
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)
packages/angular-query-experimental/src/inject-mutation.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/query-core/src/mutationObserver.ts (1)
state(145-159)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (2)
packages/angular-query-experimental/src/inject-query.ts (1)
injectQuery(218-226)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/create-base-query.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/angular-query-experimental/src/signal-proxy.ts (1)
signalProxy(14-46)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (2)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
🪛 GitHub Actions: pr
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts
[warning] 184-184: Unknown word: refetches
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts
[warning] 523-523: Async method 'mutationFn' has no 'await' expression
[warning] 572-572: Async method 'mutationFn' has no 'await' expression
🔇 Additional comments (17)
packages/angular-query-experimental/package.json (1)
100-100: LGTM! RxJS devDependency addition is appropriate.The addition of RxJS as a devDependency is reasonable for testing purposes, particularly for the
lastValueFromutility used in the HTTP client tests.packages/angular-query-experimental/src/pending-tasks-compat.ts (1)
13-28: Robust cross-version compatibility implementation.The compatibility layer elegantly handles the PendingTasks API differences across Angular v17-v19:
- v19+:
PendingTasks- v18:
ExperimentalPendingTasks- v17: graceful no-op fallback
The factory pattern with optional injection ensures the code doesn't break across versions.
packages/angular-query-experimental/src/inject-mutation.ts (2)
132-148: Excellent PendingTasks integration for mutation lifecycle.The implementation correctly tracks mutation pending states:
- Registers a pending task when
state.isPendingbecomes true- Cancels the task when the mutation completes
- Properly checks for existing
pendingTaskRefto avoid duplicate registrationsThis ensures
ApplicationRef.whenStable()will wait for in-flight mutations.
163-170: Proper cleanup on component destroy.Good defensive programming - the cleanup logic ensures any active pending task is canceled when the component is destroyed, preventing memory leaks.
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (3)
570-592: Well-structured test for PendingTasks integration.The test correctly validates that queries complete before
whenStable()resolves, which is crucial for SSR and testing scenarios.
594-637: Comprehensive HttpClient integration test.Excellent test covering real-world HTTP scenarios with the HttpTestingController. This validates that the PendingTasks integration works correctly with Angular's HTTP client.
639-680: Thorough coverage of synchronous query edge cases.Good test coverage for synchronous queries with staleTime, ensuring the queryFn isn't called unnecessarily within the stale window.
packages/angular-query-experimental/src/create-base-query.ts (4)
120-127: Well-implemented PendingTasks tracking for query fetch lifecycle.The implementation correctly:
- Registers a pending task when fetching starts
- Cancels it when fetching completes (idle state)
- Guards against duplicate registrations
This ensures
whenStable()waits for active queries to complete.
147-153: Proper cleanup handling for pending tasks.Good defensive programming with cleanup logic that cancels any active pending task when the effect is cleaned up.
162-172: Smart refetch wrapper to ensure latest options.The wrapper ensures
observer.setOptionsis called with current signal values before executing refetch. This prevents stale configuration issues when reactive options change between refetch calls.
116-144: ngZone.runOutsideAngular placement is correct — subscription callbacks re-enter Angular for signal writes and error emission.The subscription handler calls ngZone.run(...) before resultFromSubscriberSignal.set(...) and ngZone.onError.emit(...), so change detection will run for those updates; returning the unsubscribe directly is acceptable.
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)
471-505: Excellent test for mutation PendingTasks integration.The test properly validates that mutations complete before
whenStable()resolves, which is essential for SSR scenarios.
507-555: Comprehensive synchronous mutation retry test.Good coverage of retry behavior with synchronous mutations, ensuring PendingTasks correctly tracks multiple attempts.
609-659: Thorough optimistic update test.Excellent test validating that optimistic updates work correctly with PendingTasks integration, including proper sequencing of onMutate and onSuccess callbacks.
packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md (1)
1-264: Excellent documentation for the PendingTasks integration!This is comprehensive documentation that covers all the key aspects of the integration, including compatibility across Angular versions, implementation details, edge cases, and migration guidance. The code examples are clear and practical.
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (2)
1-663: Comprehensive test coverage for PendingTasks integration!The test suite thoroughly covers all critical scenarios including synchronous resolution, race conditions, component destruction, concurrent operations, HttpClient integration, and edge cases. The tests are well-structured and correctly use fake timers to control async behavior.
212-276: PendingTasks correctly remains active while query retry is paused — no change needed.create-base-query.ts adds a pendingTaskRef when state.fetchStatus === 'fetching' and only clears it when state.fetchStatus === 'idle' (pendingTaskRef logic in packages/angular-query-experimental/src/create-base-query.ts), so the test's assumption that a paused fetch still blocks ApplicationRef.whenStable() is valid.
a9ca29d to
16eb134
Compare
e8ca89d to
b02a8f4
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #9666 +/- ##
==========================================
+ Coverage 46.19% 46.38% +0.18%
==========================================
Files 213 214 +1
Lines 8453 8488 +35
Branches 1909 1930 +21
==========================================
+ Hits 3905 3937 +32
- Misses 4105 4108 +3
Partials 443 443 🚀 New features to boost your workflow:
|
b02a8f4 to
2ebb618
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
417-429: Fix: pass mutateAsync() (a Promise) to expect(...).rejects, not a thunk- await expect(() => mutateAsync()).rejects.toThrowError(err) + await expect(mutateAsync()).rejects.toThrowError(err)- await expect(() => mutateAsync()).rejects.toThrowError(err) + await expect(mutateAsync()).rejects.toThrowError(err)
🧹 Nitpick comments (6)
docs/framework/angular/zoneless.md (2)
11-12: Fix MD028: remove blank line inside blockquote.The blank line between two consecutive blockquoted lines triggers markdownlint (MD028). Remove the empty line to keep them in a single blockquote.
Apply this diff:
-> Besides Zoneless, ZoneJS change detection is also fully supported. - -> When using Zoneless, ensure you are on Angular v18 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations. +> Besides Zoneless, ZoneJS change detection is also fully supported. +> When using Zoneless, ensure you are on Angular v18 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations.
12-12: Clarify API references and add links (optional).Consider mentioning
ComponentFixture.whenStable()alongsideApplicationRef.whenStable()(common in tests) and link to Angular docs forPendingTasksandApplicationRefto aid readers. Both APIs exist in v18+. (angular.dev)Apply this diff:
-> When using Zoneless, ensure you are on Angular v18 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations. +> When using Zoneless, ensure you are on Angular v18 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` (and `ComponentFixture.whenStable()`) in sync with ongoing queries and mutations. See Angular docs for `PendingTasks` and `ApplicationRef`.If you prefer inline links:
- ... `PendingTasks` integration that keeps `ApplicationRef.whenStable()` ... + ... [`PendingTasks`](https://angular.dev/api/core/PendingTasks) integration that keeps [`ApplicationRef.whenStable()`](https://angular.dev/api/core/ApplicationRef) ...packages/angular-query-experimental/src/inject-mutation.ts (1)
163-170: Prefer effect cleanup over DestroyRef to avoid subtle lifecycle drift.If this effect ever re-runs (e.g., future refactor), the
destroyRef.onDestroycleanup won’t fire until parent destroy, risking duplicate subscriptions. Use effect cleanup for symmetry withcreate-base-query.ts.Apply:
- effect( - () => { + 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 } … resultFromSubscriberSignal.set(state) }) }), ), ) - destroyRef.onDestroy(() => { + onCleanup(() => { // Clean up any pending task on destroy if (pendingTaskRef) { pendingTaskRef() pendingTaskRef = null } unsubscribe() }) }) }, { injector, }, )packages/angular-query-experimental/src/create-base-query.ts (1)
162-172: Wrap all callable methods, not just refetch.You update observer options before
refetch, but infinite queries also exposefetchNextPage/fetchPreviousPage. Wrap them too to ensure fresh options on call.- return { - ...result, - refetch: (...args: Array<any>) => { - // Update options with current signal values before refetch - observer.setOptions(defaultedOptionsSignal()) - return result.refetch(...args) - }, - } + const wrap = <F extends (...a: any[]) => any>(fn: F | undefined) => + fn + ? ((...args: Parameters<F>): ReturnType<F> => { + observer.setOptions(defaultedOptionsSignal()) + return fn(...args) + }) + : undefined + + return { + ...result, + refetch: wrap(result.refetch)!, + // Present on InfiniteQuery results; undefined on plain Query. + fetchNextPage: wrap((result as any).fetchNextPage), + fetchPreviousPage: wrap((result as any).fetchPreviousPage), + } as typeof resultPlease run the infinite query tests to ensure types remain sound and calls still compile.
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
661-694: Rename test or implement actual cancellation.The test titled “synchronous mutation cancellation” never cancels. Either:
- rename to “should complete synchronous mutation”; or
- actually cancel/reset mid-flight and assert final state.
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
558-588: Cancellation post-start leaves query idle/pending – clarify intent.Asserting
status: 'pending'withfetchStatus: 'idle'is subtle. Consider a brief comment explaining why status remains pending after cancellation to future readers.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
docs/framework/angular/zoneless.md(1 hunks)eslint.config.js(1 hunks)packages/angular-query-experimental/eslint.config.js(1 hunks)packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md(1 hunks)packages/angular-query-experimental/package.json(1 hunks)packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts(2 hunks)packages/angular-query-experimental/src/__tests__/inject-query.test.ts(4 hunks)packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts(1 hunks)packages/angular-query-experimental/src/create-base-query.ts(4 hunks)packages/angular-query-experimental/src/inject-is-fetching.ts(0 hunks)packages/angular-query-experimental/src/inject-mutation.ts(4 hunks)packages/angular-query-experimental/src/inject-queries.ts(0 hunks)packages/angular-query-experimental/src/pending-tasks-compat.ts(1 hunks)
💤 Files with no reviewable changes (2)
- packages/angular-query-experimental/src/inject-is-fetching.ts
- packages/angular-query-experimental/src/inject-queries.ts
✅ Files skipped from review due to trivial changes (2)
- packages/angular-query-experimental/eslint.config.js
- eslint.config.js
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/angular-query-experimental/package.json
- packages/angular-query-experimental/src/pending-tasks-compat.ts
- packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
PR: TanStack/query#9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.
Applied to files:
packages/angular-query-experimental/src/create-base-query.ts
🧬 Code graph analysis (5)
packages/angular-query-experimental/src/create-base-query.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/angular-query-experimental/src/signal-proxy.ts (1)
signalProxy(14-46)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (2)
packages/angular-query-experimental/src/inject-query.ts (1)
injectQuery(218-226)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)
packages/angular-query-experimental/src/inject-mutation.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/query-core/src/mutationObserver.ts (1)
state(145-159)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(46-197)packages/angular-query-experimental/src/index.ts (2)
injectMutation(37-37)provideTanStackQuery(58-58)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
🪛 markdownlint-cli2 (0.18.1)
docs/framework/angular/zoneless.md
11-11: Blank line inside blockquote
(MD028, no-blanks-blockquote)
🔇 Additional comments (16)
packages/angular-query-experimental/src/inject-mutation.ts (3)
20-21: Good: PendingTasks compat hooks are correctly imported.The imports match the compat API surface and keep the production bundle lean via type‑only import for the ref.
64-64: Good: Resolve compat service via Injector once.Fetching
PENDING_TASKShere centralizes compat logic and keeps the hot path clean.
132-149: Correct pending-task lifecycle for mutations.
- Adds exactly one task per in‑flight mutation.
- Keeps the task open across retries; closes when not pending.
This aligns with Angular PendingTasks expectations and SSR/test stability.If you want to double‑check edge cases (rapid re‑mutate, error → retry → success), run the mutation test suite with fake timers enabled.
packages/angular-query-experimental/src/create-base-query.ts (2)
48-49: Good: Compat PendingTasks injection is localized.Keeps compat boundaries in the Angular adapter and avoids app‑level plumbing.
111-128: Solid: Query PendingTasks lifecycle covers fetching/idle with pause semantics.
- Starts a task on
fetching, holds it throughpaused(offline), and closes onidle.- Cleans up in effect
onCleanup.
Matches the contract validated by tests (e.g., offline pause keeps stability blocked).Also applies to: 147-153
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (6)
570-592: Nice: Stability gating for async query.This validates PendingTasks →
whenStable()integration in a minimal, non-HTTP scenario.
594-637: Good coverage for HttpClient flow.Uses
HttpTestingController+whenStable()to assert completion, preventing false positives with timers alone.
639-680: Covers synchronous query with staleTime.Asserts single call within stale window and correctness on subsequent refetch.
682-721: Enabled/disabled transition is exercised correctly.Confirms initial pending with
enabled=falseand proper resolution after enabling.
723-767: Invalidate + sync data path looks good.Verifies refetch after invalidation is reflected before stability resolves.
27-27: Ensure rxjs is available to testspackages/angular-query-experimental/src/tests/inject-query.test.ts imports lastValueFrom from 'rxjs' — add rxjs to devDependencies of packages/angular-query-experimental/package.json (or to the repo root devDependencies) so tests can run. Sandbox couldn't confirm package.json presence; manually verify rxjs is declared.
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
471-505: Good: Stability gating validated for mutations.Ensures PendingTasks covers mutation lifecycles and
whenStable()resolves only after completion.packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (4)
54-79: Good: sync query paths covered againstwhenStable().Validates that even synchronous producers participate in stability properly.
212-276: Strong offline/paused retry coverage.Holding the PendingTask through
pausedensures SSR/tests don’t move past incomplete work.
300-331: Cleanup on component destruction is verified.Prevents “stuck” tasks after teardown. Nice regression barrier.
525-554: HTTP cancellation path looks correct.Asserts error state and
verify()cleanup; reduces risk of leaked tasks.
e5d028d to
65df1d8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (5)
docs/framework/angular/zoneless.md (2)
11-12: Fix MD028 and clarify whenStable targets (ApplicationRef + fixture).Remove the blank line inside the blockquote to satisfy markdownlint MD028 and mention
fixture.whenStable()alongsideApplicationRef.whenStable()to align with the PR intent.Apply this diff:
- -> When using Zoneless, ensure you are on Angular v18 or later to take advantage of the `PendingTasks` integration that keeps `ApplicationRef.whenStable()` in sync with ongoing queries and mutations. +> When using Zoneless, use Angular v18+ to take advantage of `PendingTasks`, which keeps `ApplicationRef.whenStable()` and `fixture.whenStable()` in sync with ongoing queries and mutations.
12-12: Optional: Add brief version nuance/link.Consider adding a short follow-up sentence to set expectations for earlier Angular versions and link to the new PendingTasks integration doc.
Proposed text to append after this line:
- “On Angular v17 or earlier, Zoneless still works, but whenStable won’t track queries/mutations via PendingTasks.”
- Link to your new integration page once it’s published (e.g., “See PendingTasks integration for details.”).
packages/angular-query-experimental/src/inject-mutation.ts (1)
62-68: PendingTasks lifecycle tracking looks correct; consider a small helper to DRY it up.
- The add/clear logic keyed on
state.isPendingis sound and cleanup is handled viaonCleanup. Nice use ofrunOutsideAngular/runto minimize zone churn.- Minor: this pattern is duplicated with queries. Consider extracting a tiny helper (e.g.,
trackPendingTask(pendingTasks, () => state.isPending)) to keep behavior consistent across modules.Please confirm this also behaves as expected when multiple mutation updates occur in quick succession (e.g., retries), i.e., you never accumulate more than one open pending task for a single observer.
Also applies to: 127-169
packages/angular-query-experimental/src/create-base-query.ts (1)
111-128: PendingTasks integration for queries looks correct; clarify paused semantics and consider infinite-query methods.
- Add/clear on
fetchStatus === 'fetching' | 'idle'is reasonable. Shouldpausedbe treated as non-pending (current behavior) or pending? Please confirm expected behavior for offline/paused fetches.- Optional: apply the same “update options before call” wrapper to infinite-query methods (
fetchNextPage,fetchPreviousPage) when present to keep behavior consistent.Also applies to: 147-153, 156-173
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (1)
661-694: Test name is misleading; no cancellation occurs.Rename to reflect behavior (e.g., “should complete synchronous mutation”). This avoids confusion.
- test('should handle synchronous mutation cancellation', async () => { + test('should complete synchronous mutation', async () => {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (13)
docs/framework/angular/zoneless.md(1 hunks)eslint.config.js(1 hunks)packages/angular-query-experimental/eslint.config.js(1 hunks)packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md(1 hunks)packages/angular-query-experimental/package.json(1 hunks)packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts(2 hunks)packages/angular-query-experimental/src/__tests__/inject-query.test.ts(4 hunks)packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts(1 hunks)packages/angular-query-experimental/src/create-base-query.ts(4 hunks)packages/angular-query-experimental/src/inject-is-fetching.ts(0 hunks)packages/angular-query-experimental/src/inject-mutation.ts(4 hunks)packages/angular-query-experimental/src/inject-queries.ts(0 hunks)packages/angular-query-experimental/src/pending-tasks-compat.ts(1 hunks)
💤 Files with no reviewable changes (2)
- packages/angular-query-experimental/src/inject-is-fetching.ts
- packages/angular-query-experimental/src/inject-queries.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/angular-query-experimental/eslint.config.js
- packages/angular-query-experimental/package.json
- packages/angular-query-experimental/src/tests/pending-tasks.test.ts
- packages/angular-query-experimental/implementation-docs/pending-tasks-integration.md
- packages/angular-query-experimental/src/pending-tasks-compat.ts
- eslint.config.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
PR: TanStack/query#9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.
Applied to files:
packages/angular-query-experimental/src/create-base-query.ts
🧬 Code graph analysis (4)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (2)
packages/angular-query-experimental/src/inject-query.ts (1)
injectQuery(218-226)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (2)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(45-195)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/inject-mutation.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-32)PendingTaskRef(7-7)packages/query-core/src/mutationObserver.ts (1)
state(145-159)
packages/angular-query-experimental/src/create-base-query.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-32)PendingTaskRef(7-7)packages/angular-query-experimental/src/signal-proxy.ts (1)
signalProxy(14-46)
🪛 markdownlint-cli2 (0.18.1)
docs/framework/angular/zoneless.md
11-11: Blank line inside blockquote
(MD028, no-blanks-blockquote)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Test
🔇 Additional comments (6)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (3)
570-592: Good coverage for whenStable() with async query.This validates the PendingTasks integration for queries; assertions look solid.
If Angular v17 must remain green, please confirm
ApplicationRef.whenStable()exists there under your test setup; otherwise gate/skip this test on older majors.
594-637: HttpClient path test is well structured.
- Using
HttpTestingController+lastValueFromis the right shape.- Timers +
whenStable()sequencing should make this reliable.
639-680: Synchronous/stale/enabled/invalidation cases covered nicely.These scenarios tend to regress with reactive options; the tests are concise and effective.
Also applies to: 682-721, 723-767
packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts (3)
471-505: Mutation whenStable() test LGTM.Confirms PendingTasks covers mutation flows; assertions are clear.
507-555: Retry path covered well.Good to see synchronous-throw retries validated alongside whenStable().
609-660: Optimistic-update flow is well asserted.Covers onMutate/onSuccess plus query cache effects.
c235065 to
37258cf
Compare
…Stable() support Integrates TanStack Query for Angular with Angular's PendingTasks API to properly track asynchronous operations, ensuring ApplicationRef.whenStable() waits for queries and mutations to complete before resolving. Features: - Cross-version compatibility (Angular v16+ with graceful degradation) - Tracks query fetchStatus and mutation isPending state - Automatic cleanup on component destruction - Comprehensive test coverage including edge cases Benefits: - Improved SSR: Server waits for data fetching before rendering - Better testing: fixture.whenStable() properly waits for async operations - Zoneless support: Correct change detection timing
37258cf to
e2a8d8c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
docs/framework/angular/zoneless.md (1)
11-12: Fix markdownlint MD028: remove blank line inside the blockquote.There’s an empty line between two consecutive quoted lines. Remove it to satisfy no-blanks-blockquote.
- > 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.packages/angular-query-experimental/src/create-base-query.ts (1)
111-128: PendingTasks lifecycle wiring looks right; consider also covering initial “paused” states.You add a task on
fetchingand clear onidle, which keeps tasks active duringpaused(offline) — exactly what's needed. To also handle offline-first queries that enterpausedbefore an observedfetching, add a guard forpausedwhen no task is present.- if (state.fetchStatus === 'fetching' && !pendingTaskRef) { + // Start tracking when a fetch begins… + if (state.fetchStatus === 'fetching' && !pendingTaskRef) { pendingTaskRef = pendingTasks.add() } + // …or if we observe a paused fetch before we ever saw 'fetching' (e.g., offline-first). + if (state.fetchStatus === 'paused' && !pendingTaskRef) { + pendingTaskRef = pendingTasks.add() + } + if (state.fetchStatus === 'idle' && pendingTaskRef) { pendingTaskRef() pendingTaskRef = null }packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
212-277: Optional: add an offline-first test variant.You cover going offline during retry; consider a case where the app is offline before the initial subscribe so the first state is
paused. This ensures the PendingTasks guard holds stability in that path too.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (12)
docs/framework/angular/zoneless.md(1 hunks)eslint.config.js(1 hunks)packages/angular-query-experimental/eslint.config.js(1 hunks)packages/angular-query-experimental/package.json(1 hunks)packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts(2 hunks)packages/angular-query-experimental/src/__tests__/inject-query.test.ts(4 hunks)packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts(1 hunks)packages/angular-query-experimental/src/create-base-query.ts(4 hunks)packages/angular-query-experimental/src/inject-is-fetching.ts(0 hunks)packages/angular-query-experimental/src/inject-mutation.ts(4 hunks)packages/angular-query-experimental/src/inject-queries.ts(0 hunks)packages/angular-query-experimental/src/pending-tasks-compat.ts(1 hunks)
💤 Files with no reviewable changes (2)
- packages/angular-query-experimental/src/inject-is-fetching.ts
- packages/angular-query-experimental/src/inject-queries.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/angular-query-experimental/src/inject-mutation.ts
- packages/angular-query-experimental/eslint.config.js
- packages/angular-query-experimental/src/pending-tasks-compat.ts
- packages/angular-query-experimental/package.json
- packages/angular-query-experimental/src/tests/inject-mutation.test.ts
- eslint.config.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T17:57:33.184Z
Learnt from: TkDodo
PR: TanStack/query#9612
File: packages/query-async-storage-persister/src/asyncThrottle.ts:0-0
Timestamp: 2025-09-02T17:57:33.184Z
Learning: When importing from tanstack/query-core in other TanStack Query packages like query-async-storage-persister, a workspace dependency "tanstack/query-core": "workspace:*" needs to be added to the package.json.
Applied to files:
packages/angular-query-experimental/src/create-base-query.ts
🧬 Code graph analysis (3)
packages/angular-query-experimental/src/create-base-query.ts (2)
packages/angular-query-experimental/src/pending-tasks-compat.ts (2)
PENDING_TASKS(9-28)PendingTaskRef(7-7)packages/angular-query-experimental/src/signal-proxy.ts (1)
signalProxy(14-46)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (2)
packages/angular-query-experimental/src/inject-query.ts (1)
injectQuery(218-226)packages/angular-query-experimental/src/providers.ts (1)
provideTanStackQuery(105-113)
packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
packages/angular-query-experimental/src/inject-mutation.ts (1)
injectMutation(45-195)
🪛 markdownlint-cli2 (0.18.1)
docs/framework/angular/zoneless.md
11-11: Blank line inside blockquote
(MD028, no-blanks-blockquote)
🔇 Additional comments (12)
packages/angular-query-experimental/src/__tests__/inject-query.test.ts (7)
2-2: Appropriate dependencies added for PendingTasks integration testing.The new imports (
ApplicationRef,HttpClient,provideHttpClient,HttpTestingController,provideHttpClientTesting, andlastValueFrom) are all necessary for the comprehensive testing of the PendingTasks integration functionality being added.Also applies to: 12-16, 27-27
570-592: Excellent test for basic PendingTasks integration.This test verifies that queries properly integrate with Angular's PendingTasks system by ensuring
whenStable()waits for query completion. The test structure is clean and validates both the initial pending state and final success state.
594-637: Comprehensive HttpClient integration test for PendingTasks.This test effectively validates that HttpClient-based queries work correctly with the PendingTasks integration. The use of
HttpTestingControllerto simulate HTTP responses and the verification thatwhenStable()waits for completion demonstrates proper integration.
639-680: Thorough testing of synchronous queryFn with staleTime.This test covers an important edge case where synchronous queries with staleTime should still work correctly with PendingTasks. The verification of call counts ensures that the refetch behavior is working as expected.
682-721: Good coverage of enabled/disabled query transitions.This test validates that queries transitioning from disabled to enabled state properly integrate with PendingTasks. The test structure ensures that the query doesn't execute when disabled and properly executes when enabled.
723-767: Effective test for query invalidation scenarios.This test covers query invalidation with synchronous data, ensuring that the PendingTasks integration works correctly when queries are invalidated and refetched. The call count verification confirms that invalidation triggers the expected refetch.
594-767: PendingTasks integration verified — no action required.create-base-query calls pendingTasks.add() when fetchStatus becomes "fetching" and clears it when idle; mutations mirror this; the PENDING_TASKS token/factory exists; tests cover these flows (create-base-query.ts, inject-mutation.ts, pending-tasks-compat.ts, tests/pending-tasks.test.ts / inject-query.test.ts).
packages/angular-query-experimental/src/create-base-query.ts (4)
17-18: Good: Compat import is typed and isolated.Importing the compat token and its ref type keeps Angular <19 builds no-op while enabling v19+. Looks correct.
48-48: Injection site is correct.
inject(PENDING_TASKS)matches the compat factory semantics; safe on all supported Angular versions.
147-153: Cleanup is comprehensive.You dispose any outstanding task before unsubscribing — prevents leaks and accidental stability locks.
165-172: Refetch wrapper now preserves generics.The wrapper keeps the original
refetchsignature viaParameters/ReturnTypeand updates options pre-call. This resolves the earlier type-widening concern.packages/angular-query-experimental/src/__tests__/pending-tasks.test.ts (1)
118-118: TestBed.tick() is a valid TestBed API — do not remove these calls.TestBed.tick() is a TestBed method (it supersedes flushEffects) and synchronizes Angular (change detection/effects); the standalone tick() helper is the fakeAsync-only timer function — they are different. Keep TestBed.tick() here and continue using vi.advanceTimersByTimeAsync / Promise.resolve only to advance timers/microtasks; only replace TestBed.tick() if you target an Angular version that lacks it. (angular.dev)
Likely an incorrect or invalid review comment.
…nStable() support (TanStack#9666) Integrates TanStack Query for Angular with Angular's PendingTasks API to properly track asynchronous operations, ensuring ApplicationRef.whenStable() waits for queries and mutations to complete before resolving. Features: - Cross-version compatibility (Angular v16+ with graceful degradation) - Tracks query fetchStatus and mutation isPending state - Automatic cleanup on component destruction - Comprehensive test coverage including edge cases Benefits: - Improved SSR: Server waits for data fetching before rendering - Better testing: fixture.whenStable() properly waits for async operations - Zoneless support: Correct change detection timing
Benefits
Summary by CodeRabbit
New Features
Documentation
Tests
Chores