Skip to content

Conversation

@huang-julien
Copy link
Member

@huang-julien huang-julien commented Jul 3, 2025

๐Ÿ”— Linked issue

fix #32530

๐Ÿ“š Description

I don't really find this PR satisfying as we need to overwrite clear from asyncData in useFetch ๐Ÿค” should we instead add a onClear in useAsyncData ?

@huang-julien huang-julien requested a review from danielroe as a code owner July 3, 2025 20:07
@bolt-new-by-stackblitz
Copy link

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@coderabbitai
Copy link

coderabbitai bot commented Jul 3, 2025

Walkthrough

Replaces manual in-flight promise flags with AbortController/AbortSignal-based cancellation across asyncData and useFetch. AsyncData handler signature becomes (nuxtApp, { signal }) => Promise, AsyncDataOptions and execute options gain timeout and signal, and CreatedAsyncData entries (and Nuxt app _asyncData) now include an optional _abortController. useFetch types are adjusted to omit timeout from ComputedFetchOptions while exposing a public timeout option; fetch calls now accept and propagate an external signal (removing internal per-request controller/timeout scaffolding). Documentation and tests (including /api/sleep) updated to cover timeout and abort behaviours.

Estimated code review effort

๐ŸŽฏ 4 (Complex) | โฑ๏ธ ~45 minutes

Pre-merge checks and finishing touches

โŒ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check โš ๏ธ Warning Beyond fixing useFetch clear() cancellation and adding abortController support to useAsyncData, the PR also introduces timeout handling, merges multiple signals, overhauls internal asyncData execution, and updates documentation and helper types, which exceed the original objectives focused on clear() cancellation. Consider splitting the timeout enhancements and comprehensive asyncData refactoring into separate PRs so that this change focuses solely on useFetch clear() cancellation and abortController option in useAsyncData.
Docstring Coverage โš ๏ธ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
โœ… Passed checks (3 passed)
Check name Status Explanation
Title Check โœ… Passed The title refers to adding abortController option to useAsyncData, which is a genuine part of the changeset, but the primary change also targets cancellation in useFetch and broader signal handling. While it aligns with part of the work, it does not fully summarise the main focus of ensuring useFetch clear() aborts requests.
Linked Issues Check โœ… Passed The changes introduce AbortControllerโ€driven cancellation in useFetch clear() and update useAsyncData with signal support, and tests verify that clear() aborts useFetch requests as required by issue #32530.
Description Check โœ… Passed The description references the linked issue and discusses the approach to clear() override and onClear hooks, which is related to the changesetโ€™s focus on request cancellation, even if it lacks implementation detail.
โœจ Finishing touches
  • ๐Ÿ“ Generate docstrings
๐Ÿงช Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/32530

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.

โค๏ธ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a 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

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 69345c3 and b4a1341.

๐Ÿ“’ Files selected for processing (2)
  • packages/nuxt/src/app/composables/fetch.ts (1 hunks)
  • test/nuxt/composables.test.ts (3 hunks)
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (3)
`**/*.{ts,tsx}`: Follow standard TypeScript conventions and best practices

**/*.{ts,tsx}: Follow standard TypeScript conventions and best practices

๐Ÿ“„ Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • packages/nuxt/src/app/composables/fetch.ts
  • test/nuxt/composables.test.ts
`**/*.{ts,tsx,vue}`: Use clear, descriptive variable and function names Add comm...

**/*.{ts,tsx,vue}: Use clear, descriptive variable and function names
Add comments only to explain complex logic or non-obvious implementations
Keep functions focused and manageable (generally under 50 lines), and extract complex logic into separate domain-specific files
Remove code that is not used or needed
Use error handling patterns consistently

๐Ÿ“„ Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • packages/nuxt/src/app/composables/fetch.ts
  • test/nuxt/composables.test.ts
`**/*.{test,spec}.{ts,tsx}`: Write unit tests for core functionality using vitest

**/*.{test,spec}.{ts,tsx}: Write unit tests for core functionality using vitest

๐Ÿ“„ Source: CodeRabbit Inference Engine (.github/copilot-instructions.md)

List of files the instruction was applied to:

  • test/nuxt/composables.test.ts
๐Ÿง  Learnings (1)
test/nuxt/composables.test.ts (6)
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Write unit tests for core functionality using vitest
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.e2e.{ts,js} : Write end-to-end tests using Playwright and @nuxt/test-utils
Learnt from: huang-julien
PR: nuxt/nuxt#29366
File: packages/nuxt/src/app/components/nuxt-root.vue:16-19
Timestamp: 2024-12-12T12:36:34.871Z
Learning: In `packages/nuxt/src/app/components/nuxt-root.vue`, when optimizing bundle size by conditionally importing components based on route metadata, prefer using inline conditional imports like:

```js
const IsolatedPage = route?.meta?.isolate ? defineAsyncComponent(() => import('#build/isolated-page.mjs')) : null
```

instead of wrapping the import in a computed property or importing the component unconditionally.
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.vue : Use `<script setup lang="ts">` and the composition API when creating Vue components
Learnt from: GalacticHypernova
PR: nuxt/nuxt#26468
File: packages/nuxt/src/components/plugins/loader.ts:24-24
Timestamp: 2024-11-05T15:22:54.759Z
Learning: In `packages/nuxt/src/components/plugins/loader.ts`, the references to `resolve` and `distDir` are legacy code from before Nuxt used the new unplugin VFS and will be removed.
Learnt from: CR
PR: nuxt/nuxt#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-06-30T13:48:28.134Z
Learning: Applies to **/*.{ts,tsx,vue} : Use error handling patterns consistently
๐Ÿช› GitHub Check: code
test/nuxt/composables.test.ts

[failure] 966-966:
Async arrow function has no 'await' expression

๐Ÿช› GitHub Actions: autofix.ci
test/nuxt/composables.test.ts

[error] 966-966: ESLint: Async arrow function has no 'await' expression (require-await)

โฐ Context from checks skipped due to timeout of 90000ms (20)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-off, json, 20)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (windows-latest, built, vite, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, async, manifest-on, js, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, webpack, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (windows-latest, dev, vite, async, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, vite, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, built, rspack, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-off, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, default, manifest-on, json, 20)
  • GitHub Check: test-fixtures (ubuntu-latest, dev, vite, async, manifest-on, js, 20)
  • GitHub Check: release-pr
  • GitHub Check: test-benchmark
  • GitHub Check: typecheck (windows-latest, bundler)
  • GitHub Check: typecheck (ubuntu-latest, bundler)
๐Ÿ”‡ Additional comments (2)
packages/nuxt/src/app/composables/fetch.ts (1)

179-184: LGTM! Clean implementation of abort-on-clear functionality.

The implementation correctly preserves the original clear behaviour whilst adding the requested abort functionality. The use of optional chaining and a descriptive DOMException message enhances robustness and debugging experience.

test/nuxt/composables.test.ts (1)

35-45: Good test infrastructure setup.

The new /api/sleep endpoint and global stub reset in beforeEach provide proper test isolation and simulation capabilities for the abort functionality testing.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jul 3, 2025

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@32531

nuxt

npm i https://pkg.pr.new/nuxt@32531

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@32531

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@32531

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@32531

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@32531

commit: a5a5eee

@danielroe
Copy link
Member

danielroe commented Jul 3, 2025

maybe we can add controller as an option to useAsyncData instead, or implement in useAsyncData rather than useFetch?

@codspeed-hq
Copy link

codspeed-hq bot commented Jul 3, 2025

CodSpeed Performance Report

Merging #32531 will not alter performance

Comparing fix/32530 (a5a5eee) with main (9ad3578)1

Summary

โœ… 10 untouched

Footnotes

  1. No successful run was found on main (df559ad) during the generation of this report, so 9ad3578 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. โ†ฉ

@huang-julien huang-julien marked this pull request as draft July 3, 2025 20:32
@huang-julien huang-julien marked this pull request as ready for review July 5, 2025 18:08
@huang-julien huang-julien changed the title fix(nuxt): trigger abortController on useFetch clear fix(nuxt): add abortController option to useAsyncData Jul 7, 2025
@huang-julien huang-julien changed the title fix(nuxt): add abortController option to useAsyncData feat(nuxt): add abortController option to useAsyncData Jul 7, 2025
execute: (...args) => nuxtApp._asyncData[key.value]!.execute(...args),
clear: () => clearNuxtDataByKey(nuxtApp, key.value),
clear: () => {
options.abortController?.abort?.(new DOMException('Request aborted as the async data was cleared.', 'AbortError'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if an abortController is triggered, then we should also track this and avoid doing anything with the finished result of an asyncData. so it shouldn't update the data/status, just as if it were cancelled.

@huang-julien huang-julien marked this pull request as draft August 1, 2025 09:04
@huang-julien huang-julien marked this pull request as ready for review August 1, 2025 18:10
@huang-julien huang-julien marked this pull request as draft August 2, 2025 13:35
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

โ™ป๏ธ Duplicate comments (2)
packages/nuxt/src/app/composables/asyncData.ts (2)

381-387: clear(): always abort and reset controller (donโ€™t depend on _asyncDataPromises)

Current logic skips abort when the promise reference is missing and keeps an aborted controller around.

Apply:

-    clear: () => {
-      if (nuxtApp._asyncDataPromises[key.value]) {
-        if (nuxtApp._asyncData[key.value]?._abortController) {
-          nuxtApp._asyncData[key.value]?._abortController?.abort(new DOMException('AsyncData aborted by user.', 'AbortError'))
-        }
-      }
-      clearNuxtDataByKey(nuxtApp, key.value)
-    },
+    clear: () => {
+      const entry = nuxtApp._asyncData[key.value]
+      if (entry?._abortController) {
+        try {
+          entry._abortController.abort(toAbortError('AsyncData aborted by user.'))
+        } finally {
+          entry._abortController = undefined
+        }
+      }
+      clearNuxtDataByKey(nuxtApp, key.value)
+    },

711-719: Treat any AbortError as cancellation: donโ€™t mutate error/status on cancel/timeout/external signal

Currently only internal aborts are ignored; external signal/timeout still set error/status.

Apply:

           // If the promise was replaced by another one, we do not update the asyncData
           if (nuxtApp._asyncDataPromises[key] && nuxtApp._asyncDataPromises[key] !== promise) {
             return
           }

-          // If the asyncData was explicitly aborted internally (dedupe or clear), we do not update the asyncData
-          if (asyncData._abortController?.signal.aborted) {
-            return
-          }
+          // If cancelled for any reason (internal, external or timeout), do not update
+          if (error?.name === 'AbortError' || (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === 'AbortError')) {
+            return
+          }
๐Ÿงน Nitpick comments (4)
packages/nuxt/src/app/composables/asyncData.ts (2)

103-107: Timeout API: clarify/normalise inputs

Document or coerce negative/NaN timeouts to undefined so behaviour is predictable across code paths.

You can normalise at use-site (see patch below on Lines 675โ€“683).

Also applies to: 119-125


667-671: Use a portable AbortError factory instead of new DOMException(...)

DOMException is not guaranteed everywhere. Use a helper that falls back to Error.

Apply:

-      if (asyncData._abortController) {
-        asyncData._abortController.abort(new DOMException('AsyncData request cancelled by deduplication', 'AbortError'))
-      }
+      if (asyncData._abortController) {
+        asyncData._abortController.abort(toAbortError('AsyncData request cancelled by deduplication'))
+      }
docs/3.api/2.composables/use-async-data.md (2)

21-26: Consider adding a small example that uses { signal }

Show how to forward the signal to $fetch for cancellation.

Example:

<script setup lang="ts">
const { data } = await useAsyncData('mountains', (_nuxt, { signal }) =>
  $fetch('https://api.nuxtjs.dev/mountains', { signal })
)
</script>

150-164: Clarify cancellation semantics in Return Values

Explicitly state that aborted requests donโ€™t set status: 'error' and donโ€™t mutate error/data.

Apply:

 - `clear`: a function that can be used to set `data` to `undefined` (or the value of `options.default()` if provided), set `error` to `undefined`, set `status` to `idle`, and mark any currently pending requests as cancelled.
+ `clear`: a function that can be used to set `data` to `undefined` (or the value of `options.default()` if provided), set `error` to `undefined`, set `status` to `idle`, and mark any currently pending requests as cancelled.
+
+ Cancellation semantics: if a request is aborted (via `clear`, deduplication, an external `AbortSignal`, or a timeout), `data` and `error` are left unchanged and `status` is not set to `'error'`.
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ’ก Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 03a4954 and 9e8fc34.

๐Ÿ“’ Files selected for processing (2)
  • docs/3.api/2.composables/use-async-data.md (4 hunks)
  • packages/nuxt/src/app/composables/asyncData.ts (18 hunks)
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (1)
**/*.{ts,tsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
๐Ÿช› LanguageTool
docs/3.api/2.composables/use-async-data.md

[uncategorized] ~105-~105: The preposition โ€˜ofโ€™ seems more likely in this position.
Context: ...ending request - timeout - a number in milliseconds to wait before timing out ...

(AI_HYDRA_LEO_REPLACE_IN_OF)

โฐ 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). (2)
  • GitHub Check: build
  • GitHub Check: code
๐Ÿ”‡ Additional comments (6)
packages/nuxt/src/app/composables/asyncData.ts (4)

603-603: Per-entry _abortController shape looks good

This enables precise cancellation per key and aligns with the new API.


616-624: SSR prerender cache: options propagation preserved

Wrapping _handler with runWithContext while forwarding { signal } is correct.


772-779: Hash inputs remain correct

Including getCachedData in the hash is appropriate to enforce option consistency.


74-75: Remove review comment โ€” no custom getCachedData providers to update
The library itself only defines the default getCachedData and its internal uses; there are no existing overrides or custom providers within this repo that need signature changes.

Likely an incorrect or invalid review comment.

docs/3.api/2.composables/use-async-data.md (2)

105-105: Timeout option doc looks good

Clear and aligned with the code changes.


172-215: Types section updates are consistent with runtime changes

Handler overloads, timeout, and signal are correctly reflected.

Comment on lines +21 to +22
export type AsyncDataHandler<ResT> = (nuxtApp: NuxtApp, options: { signal: AbortSignal }) => Promise<ResT>

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๐Ÿ’ก Verification agent

๐Ÿงฉ Analysis chain

Breaking change: handler now receives { signal } โ€” verify all call sites and wrappers

Ensure every internal wrapper and userland example that passes a handler compiles with the new (nuxtApp, { signal }) signature. Consider a brief migration note.

Run to find potential offenders (handlers with 0โ€“1 params):

Also applies to: 153-166, 181-195, 425-437, 453-466


๐Ÿ Script executed:

#!/bin/bash
rg -nP --type=ts -C2 '\buse(Lazy)?AsyncData\s*\(\s*(?:[^,]+,)?\s*\(([^,)]+)\)\s*=>'

Length of output: 85


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

# 1. List all useAsyncData and useLazyAsyncData call sites with surrounding context
rg -n --type=ts 'use(Lazy)?AsyncData' -C2

# 2. Highlight handlers defined with a single parameter (potentially missing { signal })
rg -nP --type=ts -C2 'use(Lazy)?AsyncData\(\s*[^,]+,\s*\(\s*[^,)]+\s*\)\s*=>'

Length of output: 65342


๐Ÿ Script executed:

#!/bin/bash
set -euo pipefail

# Zero-arg handler call sites for useAsyncData and useLazyAsyncData
rg -nP --type=ts -C2 'use(Lazy)?AsyncData\(\s*(?:[^,]+,\s*)?\(\)\s*=>' || true

# Single-arg handler call sites (potentially missing { signal })
rg -nP --type=ts -C2 'use(Lazy)?AsyncData\(\s*(?:[^,]+,\s*)?\(\s*[^,)]+\s*\)\s*=>' || true

Length of output: 25717


Update AsyncData handlers to accept signal
All calls to useAsyncData/useLazyAsyncData must now pass a handler with two parametersโ€”(nuxtApp, { signal })โ€”instead of a zero- or single-arg function.

  • packages/nuxt/src/app/composables/component.ts:30 โ€“ change () => โ€ฆ to (_, { signal }) => โ€ฆ.
  • test/nuxt/use-async-data.test.ts and fixtures (e.g. test/fixtures/basic/app/composables/asyncDataTests.ts) include numerous () => โ€ฆ or (arg) => โ€ฆ handlers; update them to (_, { signal }) => โ€ฆ.
    Add a brief migration note in the docs/release notes.
๐Ÿค– Prompt for AI Agents
packages/nuxt/src/app/composables/asyncData.ts around lines 21-22, the AsyncData
handler signature must require the second options parameter with a signal;
ensure the exported type is exactly (nuxtApp: NuxtApp, options: { signal:
AbortSignal }) => Promise<ResT> (or make the options parameter non-optional if
currently optional), then update all call sites to match: change component
handler at packages/nuxt/src/app/composables/component.ts line ~30 from () =>
... to (_, { signal }) => ... and update tests/fixtures
(test/nuxt/use-async-data.test.ts and test/fixtures/**/asyncDataTests.ts) to use
(_, { signal }) => ... for every useAsyncData/useLazyAsyncData handler; finally
add a short migration note to docs/release notes describing the new handler
signature and example usage.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

โ™ป๏ธ Duplicate comments (2)
packages/nuxt/src/app/composables/asyncData.ts (2)

673-681: Use a ponyfill to merge signals, short-circuit alreadyโ€‘aborted, and listen once

-            const timeout = opts.timeout ?? options.timeout
-            const mergedSignal = AbortSignal.any([asyncData._abortController?.signal, opts?.signal, typeof timeout === 'number' ? AbortSignal.timeout(timeout) : undefined].filter((s): s is NonNullable<typeof s> => Boolean(s)))
-            mergedSignal.addEventListener('abort', (event) => {
-              const reason = (event.target as any)?.reason ?? mergedSignal.reason
-              reject(reason instanceof Error ? reason : new DOMException(reason, 'AbortError'))
-            })
-
-            return Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject)
+            const rawTimeout = opts.timeout ?? options.timeout
+            const timeout = (typeof rawTimeout === 'number' && isFinite(rawTimeout) && rawTimeout >= 0) ? rawTimeout : undefined
+            const mergedSignal = mergeAbortSignals(
+              [asyncData._abortController?.signal, opts?.signal],
+              timeout,
+            )
+            if (mergedSignal.aborted) {
+              reject(toAbortError(mergedSignal.reason))
+              return
+            }
+            mergedSignal.addEventListener('abort', () => {
+              reject(toAbortError(mergedSignal.reason))
+            }, { once: true })
+
+            Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject)

Add these helpers in this module (outside the selected range):

function toAbortError (reason?: unknown): Error {
  try { return reason instanceof Error ? reason : new DOMException(String(reason ?? 'Aborted'), 'AbortError') }
  catch { return reason instanceof Error ? reason : new Error(String(reason ?? 'Aborted')) }
}

function mergeAbortSignals (signals: Array<AbortSignal | null | undefined>, timeout?: number): AbortSignal {
  const list: AbortSignal[] = signals.filter(Boolean) as AbortSignal[]
  if (typeof timeout === 'number') {
    const t = (AbortSignal as any).timeout?.(timeout)
    if (t) list.push(t)
  }
  const AS: any = AbortSignal as any
  if (AS.any) return AS.any(list)
  const controller = new AbortController()
  const onAbort = (sig: AbortSignal) => () => {
    controller.abort(sig.reason ?? toAbortError())
    list.forEach(s => s.removeEventListener?.('abort', onAbort(s)))
  }
  for (const s of list) {
    if (s.aborted) return s
    s.addEventListener?.('abort', onAbort(s), { once: true })
  }
  return controller.signal
}

709-723: Treat any AbortError (internal/external/timeout) as cancellation; donโ€™t mutate data/error

-          // If the asyncData was explicitly aborted internally (dedupe or clear), we do not update the asyncData
-          if (asyncData._abortController?.signal.aborted) {
-            return
-          }
-
-          // if the asyncData was explicitly aborted by user, we set it back to idle state
-          if (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === 'AbortError') {
-            asyncData.status.value = 'idle'
-            return
-          }
+          // Treat any AbortError (internal, external, timeout) as cancellation: do nothing except reset status.
+          if (
+            (error && typeof error === 'object' && (error as any).name === 'AbortError') ||
+            (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === 'AbortError')
+          ) {
+            asyncData.status.value = 'idle'
+            return
+          }
๐Ÿงน Nitpick comments (1)
packages/nuxt/src/app/composables/asyncData.ts (1)

103-107: Timeout/signal options added โ€” validate inputs and define precedence

Guard against negative/NaN timeouts and clarify in JSDoc that opts.{signal,timeout} override options.{timeout}. The input validation is addressed in my abort-merge suggestion below.

Also applies to: 121-125

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ’ก Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 9e8fc34 and fa122ed.

๐Ÿ“’ Files selected for processing (2)
  • packages/nuxt/src/app/composables/asyncData.ts (18 hunks)
  • test/nuxt/use-async-data.test.ts (2 hunks)
๐Ÿšง Files skipped from review as they are similar to previous changes (1)
  • test/nuxt/use-async-data.test.ts
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (1)
**/*.{ts,tsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
โฐ 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). (3)
  • GitHub Check: build
  • GitHub Check: link-checker
  • GitHub Check: code
๐Ÿ”‡ Additional comments (3)
packages/nuxt/src/app/composables/asyncData.ts (3)

21-21: Breaking: AsyncDataHandler now requires { signal } โ€” verify all call sites/tests/docs

Ensure every useAsyncData/useLazyAsyncData handler uses (nuxtApp, { signal }) => โ€ฆ and docs/migration notes reflect this.

Also applies to: 153-165, 181-194, 206-206, 425-437, 453-466, 477-477


615-626: Prerender cache + per-call cancellation โ€” confirm intended semantics

Shared prerender promise ignores later callersโ€™ distinct signal/timeout. If a later call times out/aborts, it cannot cancel a cached in-flight promise. Confirm this trade-off is acceptable for prerender.


776-784: Hashing update to new handler type โ€” LGTM

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

๐Ÿงน Nitpick comments (1)
packages/nuxt/src/app/composables/asyncData.ts (1)

668-671: Consider safe DOMException construction.

DOMException may not be available in all environments (older Node versions, some workers). Consider wrapping in a try-catch or checking availability.

       if (asyncData._abortController) {
-        asyncData._abortController.abort(new DOMException('AsyncData request cancelled by deduplication', 'AbortError'))
+        try {
+          asyncData._abortController.abort(new DOMException('AsyncData request cancelled by deduplication', 'AbortError'))
+        } catch {
+          const err = new Error('AsyncData request cancelled by deduplication')
+          err.name = 'AbortError'
+          asyncData._abortController.abort(err)
+        }
       }
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between fa122ed and cba987e.

๐Ÿ“’ Files selected for processing (5)
  • docs/3.api/2.composables/use-async-data.md (4 hunks)
  • docs/3.api/2.composables/use-fetch.md (3 hunks)
  • packages/nuxt/src/app/composables/asyncData.ts (18 hunks)
  • packages/nuxt/src/app/composables/fetch.ts (5 hunks)
  • packages/nuxt/src/app/nuxt.ts (1 hunks)
๐Ÿšง Files skipped from review as they are similar to previous changes (3)
  • packages/nuxt/src/app/nuxt.ts
  • docs/3.api/2.composables/use-fetch.md
  • packages/nuxt/src/app/composables/fetch.ts
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (1)
**/*.{ts,tsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
๐Ÿงฌ Code graph analysis (1)
packages/nuxt/src/app/composables/asyncData.ts (1)
packages/nuxt/src/app/composables/error.ts (1)
  • NuxtError (15-17)
โฐ 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). (2)
  • GitHub Check: build
  • GitHub Check: code
๐Ÿ”‡ Additional comments (7)
packages/nuxt/src/app/composables/asyncData.ts (3)

20-20: Handler signature breaking change is correctly enforced.

The new AsyncDataHandler<ResT> type properly requires the { signal: AbortSignal } parameter. This aligns with the PR objectives for abort controller support.


102-105: Timeout option correctly added to AsyncDataOptions.

The timeout option is well-documented and integrates cleanly with the abort signal mechanism.


711-725: Abort handling logic is correct.

The error handler properly distinguishes between superseded requests, internal aborts, and user aborts. Setting status to 'idle' for AbortError is the right semantic choice.

docs/3.api/2.composables/use-async-data.md (4)

105-105: Timeout option is well documented.

Clear explanation of the timeout behaviour with appropriate defaults.


173-181: Handler signature correctly documents the new { signal } parameter.

The type signatures accurately reflect the breaking change requiring the options parameter with signal.


194-195: AsyncDataOptions type signature is accurate.

The timeout option is correctly shown in the type definition.


213-215: AsyncDataExecuteOptions correctly documents signal and timeout.

Both new options are properly typed and documented for the execute method.

@danielroe danielroe added enhancement and removed bug labels Oct 6, 2025
Copy link

@coderabbitai coderabbitai bot left a 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

โ™ป๏ธ Duplicate comments (2)
packages/nuxt/src/app/composables/asyncData.ts (2)

413-420: Reset _abortController after aborting to prevent immediate cancellation of subsequent requests.

Currently, the aborted controller remains attached to the asyncData entry. If a new request is initiated after calling clear(), it would be immediately aborted by the stale signal.

Based on learnings

Apply this diff:

     clear: () => {
-      if (nuxtApp._asyncDataPromises[key.value]) {
-        if (nuxtApp._asyncData[key.value]?._abortController) {
-          nuxtApp._asyncData[key.value]?._abortController?.abort(new DOMException('AsyncData aborted by user.', 'AbortError'))
-        }
-      }
+      const entry = nuxtApp._asyncData[key.value]
+      if (entry?._abortController) {
+        try {
+          entry._abortController.abort(new DOMException('AsyncData aborted by user.', 'AbortError'))
+        } finally {
+          entry._abortController = undefined
+        }
+      }
       clearNuxtDataByKey(nuxtApp, key.value)
     },

703-713: Add polyfill for AbortSignal.any/timeout, fix type mismatch, and use mergedSignal.reason.

Multiple issues require attention:

  1. Type mismatch (line 703): Variable typed as Promise<ResT | void> but constructor uses Promise<ResT>
  2. Missing polyfills (line 707): AbortSignal.any and AbortSignal.timeout are not available in Node < 19.7 and older browsers
  3. Unreliable reason access (line 709): event.target?.reason is not type-safe; use mergedSignal.reason instead
  4. No early abort check: Should reject immediately if the merged signal is already aborted

Based on learnings

Apply this diff:

-      const promise: Promise<ResT | void> = new Promise<ResT>(
+      const promise: Promise<ResT | void> = new Promise<ResT | void>(
         (resolve, reject) => {
           try {
             const timeout = opts.timeout ?? options.timeout
-            const mergedSignal = AbortSignal.any([asyncData._abortController?.signal, opts?.signal, typeof timeout === 'number' ? AbortSignal.timeout(timeout) : undefined].filter((s): s is NonNullable<typeof s> => Boolean(s)))
-            mergedSignal.addEventListener('abort', (event) => {
-              const reason = (event.target as any)?.reason ?? mergedSignal.reason
+            const mergedSignal = mergeAbortSignals(
+              [asyncData._abortController?.signal, opts?.signal],
+              typeof timeout === 'number' && timeout >= 0 ? timeout : undefined
+            )
+            
+            if (mergedSignal.aborted) {
+              const reason = mergedSignal.reason
+              reject(reason instanceof Error ? reason : toAbortError(reason))
+              return
+            }
+            
+            mergedSignal.addEventListener('abort', () => {
+              const reason = mergedSignal.reason
               reject(reason instanceof Error ? reason : new DOMException(reason, 'AbortError'))
-            })
+            }, { once: true })
 
-            return Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject)
+            Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject)
           } catch (err) {
             reject(err)
           }

Add these helper functions at module scope (after imports):

function toAbortError(reason?: unknown): Error {
  try {
    return reason instanceof Error ? reason : new DOMException(String(reason ?? 'Aborted'), 'AbortError')
  } catch {
    return reason instanceof Error ? reason : new Error(String(reason ?? 'Aborted'))
  }
}

function mergeAbortSignals(signals: Array<AbortSignal | null | undefined>, timeout?: number): AbortSignal {
  const list: AbortSignal[] = signals.filter(Boolean) as AbortSignal[]
  if (typeof timeout === 'number' && timeout >= 0) {
    const timeoutSignal = (AbortSignal as any).timeout?.(timeout)
    if (timeoutSignal) list.push(timeoutSignal)
  }
  
  // Use native if available
  if ((AbortSignal as any).any) {
    return (AbortSignal as any).any(list)
  }
  
  // Polyfill
  const controller = new AbortController()
  const abort = (sig: AbortSignal) => () => {
    controller.abort(sig.reason ?? toAbortError())
    list.forEach(s => s.removeEventListener?.('abort', abort(s)))
  }
  
  for (const sig of list) {
    if (sig.aborted) return sig
    sig.addEventListener?.('abort', abort(sig), { once: true })
  }
  
  return controller.signal
}
๐Ÿงน Nitpick comments (1)
packages/nuxt/src/app/composables/asyncData.ts (1)

741-755: Consider checking error.name === 'AbortError' instead of internal controller state.

The current implementation checks asyncData._abortController?.signal.aborted to detect internal aborts. However, a cleaner approach would be to check if the error is an AbortError regardless of source (internal, external, or timeout), as suggested in previous reviews.

Apply this diff:

           // If the promise was replaced by another one, we do not update the asyncData
           if (nuxtApp._asyncDataPromises[key] && nuxtApp._asyncDataPromises[key] !== promise) {
             return
           }
-
-          // If the asyncData was explicitly aborted internally (dedupe or clear), we do not update the asyncData
-          if (asyncData._abortController?.signal.aborted) {
-            return
-          }
-
-          // if the asyncData was explicitly aborted by user, we set it back to idle state
-          if (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === 'AbortError') {
+          
+          // If cancelled for any reason (internal, external, or timeout), reset to idle
+          if (error?.name === 'AbortError' || (typeof DOMException !== 'undefined' && error instanceof DOMException && error.name === 'AbortError')) {
             asyncData.status.value = 'idle'
             return
           }
๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 0f9d9ca and 648fe05.

๐Ÿ“’ Files selected for processing (2)
  • packages/nuxt/src/app/composables/asyncData.ts (18 hunks)
  • test/nuxt/use-async-data.test.ts (2 hunks)
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (2)
**/*.{ts,tsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • packages/nuxt/src/app/composables/asyncData.ts
  • test/nuxt/use-async-data.test.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/nuxt/use-async-data.test.ts
๐Ÿงฌ Code graph analysis (1)
test/nuxt/use-async-data.test.ts (1)
packages/nuxt/src/app/composables/asyncData.ts (1)
  • useAsyncData (194-428)
๐Ÿ”‡ Additional comments (6)
test/nuxt/use-async-data.test.ts (3)

19-25: LGTM!

The /api/sleep endpoint is well-suited for testing cancellation and timeout scenarios with a predictable 100ms delay.


27-29: LGTM!

Unstubbing all globals before each test ensures proper test isolation and prevents mock pollution across tests.


838-894: LGTM! Comprehensive cancellation test coverage.

The tests properly validate:

  • External signal cancellation via execute({ signal })
  • Internal cancellation via clear()
  • Handler signal propagation
  • Timeout behaviour

The vi.waitFor workaround in the timeout test (line 890) is acceptable given the note about advanceTimersToNextTimer not working as expected.

packages/nuxt/src/app/composables/asyncData.ts (3)

20-20: LGTM! Type definitions correctly reflect the new cancellation API.

The AsyncDataHandler signature now requires a second parameter with signal, and timeout options are properly typed in both AsyncDataOptions and AsyncDataExecuteOptions.

Also applies to: 102-105, 121-123


648-658: LGTM! Prerender wrapper correctly propagates signal.

The handler wrapper now passes the options parameter containing the signal to the underlying _handler, ensuring cancellation works in prerender scenarios.


698-701: LGTM! AbortController lifecycle management is correct.

Creating a new AbortController for each execution and aborting any existing controller ensures proper cancellation semantics. Using DOMException with name 'AbortError' is the standard way to create abort errors.

Copy link

@coderabbitai coderabbitai bot left a 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 (3)
test/nuxt/use-async-data.test.ts (1)

19-25: Great coverage for abort/timeout semantics; minor stability nit

  • New endpoint, global unstub, and abort/timeout scenarios are well covered. LGTM.
  • Optional: in the timeout test (Lines 969-978), prefer advancing fake timers deterministically (e.g. vi.advanceTimersByTime(2)) before waitFor to reduce flakiness.

Also applies to: 27-29, 901-920, 922-937, 939-951, 953-967, 969-978, 980-999, 1000-1025, 1027-1058, 1060-1080, 1082-1102, 1104-1119, 1121-1132, 1134-1153

packages/nuxt/src/app/composables/asyncData.ts (2)

706-722: Normalise abort reason and align Promise generic

  • Use the same generic in Promise construction to avoid confusion.
  • Prefer a helper to normalise abort reasons rather than constructing DOMException inline.

Apply:

-      const promise: Promise<ResT | void> = new Promise<ResT>(
+      const promise: Promise<ResT | void> = new Promise<ResT | void>(
         (resolve, reject) => {
           try {
             const timeout = opts.timeout ?? options.timeout
-            const mergedSignal = mergeAbortSignals([asyncData._abortController?.signal, opts?.signal], timeout)
+            const mergedSignal = mergeAbortSignals([asyncData._abortController?.signal, opts?.signal], timeout)
             if (mergedSignal.aborted) {
-              const reason = mergedSignal.reason
-              reject(reason instanceof Error ? reason : new DOMException(String(reason ?? 'Aborted'), 'AbortError'))
+              reject(mergedSignal.reason instanceof Error ? mergedSignal.reason : toAbortError(mergedSignal.reason))
               return
             }
-            mergedSignal.addEventListener('abort', () => {
-              const reason = mergedSignal.reason
-              reject(reason instanceof Error ? reason : new DOMException(String(reason ?? 'Aborted'), 'AbortError'))
-            }, { once: true })
+            mergedSignal.addEventListener('abort', () => {
+              const r = mergedSignal.reason
+              reject(r instanceof Error ? r : toAbortError(r))
+            }, { once: true })
 
             return Promise.resolve(handler(nuxtApp, { signal: mergedSignal })).then(resolve, reject)

701-705: Avoid direct DOMException construction in aborts

Constructing DOMException directly is brittle in some runtimes. Use a helper to produce an AbortError.

Apply:

-      if (asyncData._abortController) {
-        asyncData._abortController.abort(new DOMException('AsyncData request cancelled by deduplication', 'AbortError'))
-      }
+      if (asyncData._abortController) {
+        asyncData._abortController.abort(toAbortError('AsyncData request cancelled by deduplication'))
+      }
-      if (entry?._abortController) {
+      if (entry?._abortController) {
         try {
-          entry._abortController.abort(new DOMException('AsyncData aborted by user.', 'AbortError'))
+          entry._abortController.abort(toAbortError('AsyncData aborted by user.'))
         } finally {
           entry._abortController = undefined
         }
       }

Add the toAbortError helper once per module (see previous comment).

Also applies to: 413-423

๐Ÿ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

๐Ÿ“ฅ Commits

Reviewing files that changed from the base of the PR and between 648fe05 and a5a5eee.

๐Ÿ“’ Files selected for processing (2)
  • packages/nuxt/src/app/composables/asyncData.ts (18 hunks)
  • test/nuxt/use-async-data.test.ts (2 hunks)
๐Ÿงฐ Additional context used
๐Ÿ““ Path-based instructions (2)
**/*.{ts,tsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Follow standard TypeScript conventions and best practices

Files:

  • test/nuxt/use-async-data.test.ts
  • packages/nuxt/src/app/composables/asyncData.ts
**/*.{test,spec}.{ts,tsx,js,jsx}

๐Ÿ“„ CodeRabbit inference engine (.github/copilot-instructions.md)

Write unit tests for core functionality using vitest

Files:

  • test/nuxt/use-async-data.test.ts
๐Ÿงฌ Code graph analysis (2)
test/nuxt/use-async-data.test.ts (1)
packages/nuxt/src/app/composables/asyncData.ts (1)
  • useAsyncData (194-431)
packages/nuxt/src/app/composables/asyncData.ts (2)
packages/nuxt/src/app/nuxt.ts (1)
  • NuxtApp (199-199)
packages/nuxt/src/app/composables/error.ts (1)
  • NuxtError (15-17)
โฐ 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: code

Comment on lines +824 to +866
function mergeAbortSignals (signals: Array<AbortSignal | null | undefined>, timeout?: number): AbortSignal {
const list = signals.filter(s => !!s)
if (typeof timeout === 'number' && timeout >= 0) {
const timeoutSignal = AbortSignal.timeout?.(timeout)
if (timeoutSignal) { list.push(timeoutSignal) }
}

// Use native if available
if (AbortSignal.any) {
return AbortSignal.any(list)
}

// Polyfill
const controller = new AbortController()

for (const sig of list) {
if (sig.aborted) {
const reason = sig.reason ?? new DOMException('Aborted', 'AbortError')
try {
controller.abort(reason)
} catch {
controller.abort()
}
return controller.signal
}
}

const onAbort = () => {
const abortedSignal = list.find(s => s.aborted)
const reason = abortedSignal?.reason ?? new DOMException('Aborted', 'AbortError')
try {
controller.abort(reason)
} catch {
controller.abort()
}
}

for (const sig of list) {
sig.addEventListener?.('abort', onAbort, { once: true })
}

return controller.signal
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

โš ๏ธ Potential issue | ๐ŸŸ  Major

Type-safety and portability: avoid direct AbortSignal.any/timeout; add type guards

Current implementation directly references AbortSignal.any/timeout (not in all TS lib targets) and filters signals without a type guard, risking TS errors (โ€œProperty 'any'/'timeout' does not existโ€ฆโ€, โ€œObject is possibly 'undefined'โ€). Use a small ponyfill and a type-guarded list.

Apply:

-function mergeAbortSignals (signals: Array<AbortSignal | null | undefined>, timeout?: number): AbortSignal {
-  const list = signals.filter(s => !!s)
-  if (typeof timeout === 'number' && timeout >= 0) {
-    const timeoutSignal = AbortSignal.timeout?.(timeout)
-    if (timeoutSignal) { list.push(timeoutSignal) }
-  }
-
-  // Use native if available
-  if (AbortSignal.any) {
-    return AbortSignal.any(list)
-  }
-
-  // Polyfill
-  const controller = new AbortController()
-
-  for (const sig of list) {
-    if (sig.aborted) {
-      const reason = sig.reason ?? new DOMException('Aborted', 'AbortError')
-      try {
-        controller.abort(reason)
-      } catch {
-        controller.abort()
-      }
-      return controller.signal
-    }
-  }
-
-  const onAbort = () => {
-    const abortedSignal = list.find(s => s.aborted)
-    const reason = abortedSignal?.reason ?? new DOMException('Aborted', 'AbortError')
-    try {
-      controller.abort(reason)
-    } catch {
-      controller.abort()
-    }
-  }
-
-  for (const sig of list) {
-    sig.addEventListener?.('abort', onAbort, { once: true })
-  }
-
-  return controller.signal
-}
+function mergeAbortSignals (signals: Array<AbortSignal | null | undefined>, timeout?: number): AbortSignal {
+  const list: AbortSignal[] = signals.filter((s): s is AbortSignal => Boolean(s))
+  const AS: any = AbortSignal as any
+  if (typeof timeout === 'number' && timeout >= 0) {
+    const t: AbortSignal | undefined = AS.timeout?.(timeout)
+    if (t) list.push(t)
+  }
+  if (AS.any) {
+    return AS.any(list)
+  }
+  const controller = new AbortController()
+  for (const sig of list) {
+    if (sig.aborted) {
+      const reason = sig.reason instanceof Error ? sig.reason : toAbortError(sig.reason)
+      try { controller.abort(reason) } catch { controller.abort() }
+      return controller.signal
+    }
+  }
+  const onAbort = () => {
+    const aborted = list.find(s => s.aborted)
+    const reason = aborted?.reason instanceof Error ? aborted.reason : toAbortError(aborted?.reason)
+    try { controller.abort(reason) } catch { controller.abort() }
+  }
+  for (const sig of list) {
+    sig.addEventListener?.('abort', onAbort, { once: true })
+  }
+  return controller.signal
+}

Add helper (module scope):

function toAbortError (reason?: unknown): Error {
  try {
    return reason instanceof Error ? reason : new DOMException(String(reason ?? 'Aborted'), 'AbortError')
  } catch {
    return reason instanceof Error ? reason : Object.assign(new Error(String(reason ?? 'Aborted')), { name: 'AbortError' })
  }
}
๐Ÿค– Prompt for AI Agents
In packages/nuxt/src/app/composables/asyncData.ts around lines 824 to 866, the
code directly references AbortSignal.any and AbortSignal.timeout and filters
signals without type guards which can cause TS/lib-target errors and unsafe
undefined access; instead add the suggested toAbortError helper at module scope,
use explicit type guards when building the list (e.g., filter for non-null
AbortSignal instances), check existence of AbortSignal.any and
AbortSignal.timeout via typeof before calling them and provide a
ponyfill/fallback implementation for `any` behavior, convert signal.reason to a
proper Error using toAbortError when aborting, and guard addEventListener calls
so they only run on defined signals; ensure all abort calls wrap with try/catch
as currently done and return controller.signal for the fallback.

@danielroe danielroe merged commit 6f9cb0d into main Oct 7, 2025
85 of 87 checks passed
@danielroe danielroe deleted the fix/32530 branch October 7, 2025 21:43
This was referenced Oct 7, 2025
@github-actions github-actions bot mentioned this pull request Oct 23, 2025
danielroe pushed a commit that referenced this pull request Oct 25, 2025
Co-authored-by: Robin <robin.kehl@singular-it.de>
@github-actions github-actions bot mentioned this pull request Oct 25, 2025
@bwshnaftjmal2025
Copy link

ุชุญูŠุงุชูŠ ู„ู„ุฌู…ูŠุน ุจุงู„ุชูˆููŠู‚ ุงู† ุดุงุก ุงู„ู„ู‡

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

clear does not cancel useFetch requests

5 participants