Skip to content

feat: add React Native AI support#647

Open
AlemTuzlak wants to merge 4 commits into
mainfrom
investigate/react-native-support
Open

feat: add React Native AI support#647
AlemTuzlak wants to merge 4 commits into
mainfrom
investigate/react-native-support

Conversation

@AlemTuzlak
Copy link
Copy Markdown
Contributor

@AlemTuzlak AlemTuzlak commented May 26, 2026

Summary

  • Add React Native support docs, package export compatibility, smoke fixture, and example app.
  • Add React Native smoke validation for import surface, Expo bundling, and esbuild bundling.
  • Fix post-rebase devtools build blocker by using a TypeScript 5-compatible ignoreDeprecations value in @tanstack/ai-devtools-core.

Local validation

  • pnpm install --frozen-lockfile (prior worker)
  • pnpm --filter ts-react-native-chat smoke (prior worker)
  • pnpm run test:react-native (prior worker)
  • pnpm test:docs (prior worker)
  • pnpm nx run @tanstack/ai-devtools-core:test:types
  • pnpm nx run @tanstack/ai-devtools-core:build
  • pnpm run test:pr
  • pnpm run build:all
  • pnpm --filter @tanstack/ai-e2e test:e2e (exit 0; 208 passed, 8 flaky retries reported)
  • git diff --check

Summary by CodeRabbit

  • New Features

    • React Native / Expo support: headless chat hooks, mobile-compatible connection adapters, and a fetcher option for chat clients/hooks.
    • New XHR-based streaming transports (XHR SSE and XHR HTTP) and improved client-safe streaming utilities.
    • Example React Native Expo chat app and runnable example server.
  • Documentation

    • React Native Quick Start, transport selection guidance, API docs updates, and Expo walkthrough added.
  • Tests

    • React Native smoke tests, example app tests, bundle/import-surface checks, and XHR adapter tests added.
  • Bug Fixes

    • Clearer streaming diagnostics with actionable unsupported-stream errors.

Review Change Stack

@AlemTuzlak AlemTuzlak requested a review from a team as a code owner May 26, 2026 20:39
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcd25213-e2fd-415d-a898-8229e6093cda

📥 Commits

Reviewing files that changed from the base of the PR and between d8d0d09 and 00dcf81.

📒 Files selected for processing (1)
  • examples/ts-react-native-chat/scripts/dev.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/ts-react-native-chat/scripts/dev.mjs

📝 Walkthrough

<review_stack_artifact>

</review_stack_artifact>

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch investigate/react-native-support
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch investigate/react-native-support

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

🚀 Changeset Version Preview

7 package(s) bumped directly, 23 bumped as dependents.

🟥 Major bumps

Package Version Reason
@tanstack/ai-preact 0.6.34 → 1.0.0 Changeset
@tanstack/ai-react 0.12.0 → 1.0.0 Changeset
@tanstack/ai-solid 0.10.9 → 1.0.0 Changeset
@tanstack/ai-svelte 0.10.9 → 1.0.0 Changeset
@tanstack/ai-vue 0.10.10 → 1.0.0 Changeset
@tanstack/ai-anthropic 0.11.0 → 1.0.0 Dependent
@tanstack/ai-code-mode 0.1.21 → 1.0.0 Dependent
@tanstack/ai-code-mode-skills 0.1.21 → 1.0.0 Dependent
@tanstack/ai-elevenlabs 0.2.12 → 1.0.0 Dependent
@tanstack/ai-event-client 0.3.11 → 1.0.0 Dependent
@tanstack/ai-fal 0.7.14 → 1.0.0 Dependent
@tanstack/ai-gemini 0.12.0 → 1.0.0 Dependent
@tanstack/ai-grok 0.9.0 → 1.0.0 Dependent
@tanstack/ai-groq 0.2.7 → 1.0.0 Dependent
@tanstack/ai-isolate-node 0.1.21 → 1.0.0 Dependent
@tanstack/ai-isolate-quickjs 0.1.21 → 1.0.0 Dependent
@tanstack/ai-ollama 0.6.22 → 1.0.0 Dependent
@tanstack/ai-openai 0.10.2 → 1.0.0 Dependent
@tanstack/ai-openrouter 0.9.8 → 1.0.0 Dependent
@tanstack/ai-react-ui 0.8.2 → 1.0.0 Dependent
@tanstack/ai-solid-ui 0.7.2 → 1.0.0 Dependent
@tanstack/openai-base 0.4.0 → 1.0.0 Dependent

🟨 Minor bumps

Package Version Reason
@tanstack/ai 0.22.0 → 0.23.0 Changeset
@tanstack/ai-client 0.12.0 → 0.13.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/ai-devtools-core 0.3.38 → 0.3.39 Dependent
@tanstack/ai-isolate-cloudflare 0.2.12 → 0.2.13 Dependent
@tanstack/ai-vue-ui 0.2.5 → 0.2.6 Dependent
@tanstack/preact-ai-devtools 0.1.42 → 0.1.43 Dependent
@tanstack/react-ai-devtools 0.2.42 → 0.2.43 Dependent
@tanstack/solid-ai-devtools 0.2.42 → 0.2.43 Dependent

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 26, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedexpo@​56.0.577100100100100
Addedexpo@​54.0.347710078100100
Added@​types/​react@​19.1.171001007996100
Addedreact@​19.1.01001008496100
Addedreact-native@​0.85.39110099100100
Addedreact-native@​0.81.59110099100100
Added@​hono/​node-server@​1.19.1410010010096100
Addedhono@​4.12.23991009796100

View full report

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 26, 2026

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Publisher changed: npm @expo/expo-modules-macros-plugin is now published by tsapeta

Author: tsapeta

From: pnpm-lock.yamlnpm/expo@56.0.5npm/@expo/expo-modules-macros-plugin@0.0.9

ℹ Read more on: This package | This alert | What is unstable ownership?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Try to reduce the number of authors you depend on to reduce the risk to malicious actors gaining access to your supply chain. Packages should remove inactive collaborators with publishing rights from packages on npm.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@expo/expo-modules-macros-plugin@0.0.9. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Publisher changed: npm @expo/spawn-async is now published by philpl

Author: philpl

From: pnpm-lock.yamlnpm/expo@54.0.34npm/expo@56.0.5npm/@expo/spawn-async@1.8.0

ℹ Read more on: This package | This alert | What is unstable ownership?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Try to reduce the number of authors you depend on to reduce the risk to malicious actors gaining access to your supply chain. Packages should remove inactive collaborators with publishing rights from packages on npm.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@expo/spawn-async@1.8.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @react-native/debugger-frontend is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: pnpm-lock.yamlnpm/expo@54.0.34npm/react-native@0.81.5npm/@react-native/debugger-frontend@0.81.5

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@react-native/debugger-frontend@0.81.5. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @react-native/debugger-frontend is 96.0% likely obfuscated

Confidence: 0.96

Location: Package overview

From: pnpm-lock.yamlnpm/react-native@0.85.3npm/expo@56.0.5npm/@react-native/debugger-frontend@0.85.3

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@react-native/debugger-frontend@0.85.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Low adoption: npm fetch-nodeshim

Location: Package overview

From: pnpm-lock.yamlnpm/expo@56.0.5npm/fetch-nodeshim@0.4.10

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/fetch-nodeshim@0.4.10. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Deprecated by its maintainer: npm inflight

Reason: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.

From: pnpm-lock.yamlnpm/expo@54.0.34npm/react-native@0.81.5npm/inflight@1.0.6

ℹ Read more on: This package | This alert | What is a deprecated package?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Research the state of the package and determine if there are non-deprecated versions that can be used, or if it should be replaced with a new, supported solution.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/inflight@1.0.6. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Deprecated by its maintainer: npm rimraf

Reason: Rimraf versions prior to v4 are no longer supported

From: pnpm-lock.yamlnpm/expo@54.0.34npm/react-native@0.81.5npm/rimraf@3.0.2

ℹ Read more on: This package | This alert | What is a deprecated package?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Research the state of the package and determine if there are non-deprecated versions that can be used, or if it should be replaced with a new, supported solution.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/rimraf@3.0.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Deprecated by its maintainer: npm uuid

Reason: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).

From: pnpm-lock.yamlnpm/expo@54.0.34npm/expo@56.0.5npm/uuid@7.0.3

ℹ Read more on: This package | This alert | What is a deprecated package?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Research the state of the package and determine if there are non-deprecated versions that can be used, or if it should be replaced with a new, supported solution.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/uuid@7.0.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 26, 2026

View your CI Pipeline Execution ↗ for commit d8d0d09

Command Status Duration Result
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-26 21:23:40 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 26, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/@tanstack/ai@647

@tanstack/ai-anthropic

npm i https://pkg.pr.new/@tanstack/ai-anthropic@647

@tanstack/ai-client

npm i https://pkg.pr.new/@tanstack/ai-client@647

@tanstack/ai-code-mode

npm i https://pkg.pr.new/@tanstack/ai-code-mode@647

@tanstack/ai-code-mode-skills

npm i https://pkg.pr.new/@tanstack/ai-code-mode-skills@647

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/@tanstack/ai-devtools-core@647

@tanstack/ai-elevenlabs

npm i https://pkg.pr.new/@tanstack/ai-elevenlabs@647

@tanstack/ai-event-client

npm i https://pkg.pr.new/@tanstack/ai-event-client@647

@tanstack/ai-fal

npm i https://pkg.pr.new/@tanstack/ai-fal@647

@tanstack/ai-gemini

npm i https://pkg.pr.new/@tanstack/ai-gemini@647

@tanstack/ai-grok

npm i https://pkg.pr.new/@tanstack/ai-grok@647

@tanstack/ai-groq

npm i https://pkg.pr.new/@tanstack/ai-groq@647

@tanstack/ai-isolate-cloudflare

npm i https://pkg.pr.new/@tanstack/ai-isolate-cloudflare@647

@tanstack/ai-isolate-node

npm i https://pkg.pr.new/@tanstack/ai-isolate-node@647

@tanstack/ai-isolate-quickjs

npm i https://pkg.pr.new/@tanstack/ai-isolate-quickjs@647

@tanstack/ai-ollama

npm i https://pkg.pr.new/@tanstack/ai-ollama@647

@tanstack/ai-openai

npm i https://pkg.pr.new/@tanstack/ai-openai@647

@tanstack/ai-openrouter

npm i https://pkg.pr.new/@tanstack/ai-openrouter@647

@tanstack/ai-preact

npm i https://pkg.pr.new/@tanstack/ai-preact@647

@tanstack/ai-react

npm i https://pkg.pr.new/@tanstack/ai-react@647

@tanstack/ai-react-ui

npm i https://pkg.pr.new/@tanstack/ai-react-ui@647

@tanstack/ai-solid

npm i https://pkg.pr.new/@tanstack/ai-solid@647

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/@tanstack/ai-solid-ui@647

@tanstack/ai-svelte

npm i https://pkg.pr.new/@tanstack/ai-svelte@647

@tanstack/ai-utils

npm i https://pkg.pr.new/@tanstack/ai-utils@647

@tanstack/ai-vue

npm i https://pkg.pr.new/@tanstack/ai-vue@647

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/@tanstack/ai-vue-ui@647

@tanstack/openai-base

npm i https://pkg.pr.new/@tanstack/openai-base@647

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/@tanstack/preact-ai-devtools@647

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/@tanstack/react-ai-devtools@647

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/@tanstack/solid-ai-devtools@647

commit: d8d0d09

Copy link
Copy Markdown
Contributor

@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: 7

🧹 Nitpick comments (1)
testing/react-native-smoke/scripts/esbuild-smoke.ts (1)

14-20: ⚡ Quick win

Consider removing the @tanstack/ai root alias.

Line 15 aliases @tanstack/ai to the root package entry, but the import surface script (assert-import-surface.ts lines 245-248) explicitly forbids importing from @tanstack/ai root, requiring @tanstack/ai/client instead.

Since the import surface validation would fail if any code imports @tanstack/ai, this alias should never be used. Including it may cause confusion or suggest the import is permitted.

Proposed removal
   alias: {
-    '`@tanstack/ai`': resolve(repoRoot, 'packages/ai/src/index.ts'),
     '`@tanstack/ai/client`': resolve(repoRoot, 'packages/ai/src/client.ts'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@testing/react-native-smoke/scripts/esbuild-smoke.ts` around lines 14 - 20,
Remove the forbidden root alias entry from the esbuild alias map: delete the
'`@tanstack/ai`': resolve(repoRoot, 'packages/ai/src/index.ts') line in the alias
object in esbuild-smoke.ts so imports must use the allowed scoped entry
('`@tanstack/ai/client`'); this aligns with the import-surface rule that disallows
importing from the '`@tanstack/ai`' root.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/getting-started/quick-start.md`:
- Around line 21-25: The two adjacent blockquotes ("React Native or Expo app?
Use the headless React hooks..." and the "**Tip:** If you'd prefer not to sign
up...") are separated by a blank line which triggers markdownlint MD028; remove
the empty line between them or merge them into one contiguous blockquote so they
form a single continuous blockquote (locate the callout text starting with
"React Native or Expo app?" and the following "**Tip:**" paragraph and either
delete the blank line or combine the content into one blockquote).

In `@examples/ts-react-native-chat/package.json`:
- Around line 1-44: Add the missing packageManager field to the top-level
package.json object in examples/ts-react-native-chat by adding packageManager:
"pnpm@10.17.0" (same JSON object that contains "name", "private", "type", etc.);
ensure the property is a sibling to those keys so the package manifest follows
the **/package.json guideline.

In `@examples/ts-react-native-chat/scripts/dev.mjs`:
- Around line 338-344: The exit handler for child.on('exit') incorrectly treats
any signal-based exit as success; update the callback (the child.on('exit',
(code, signal) => { ... }) block) so that if signal is non-null you call
shutdown with a non-zero code (e.g., shutdown(1) or shutdown with a code derived
from the signal) instead of shutdown(0), and include the signal in the shutdown
invocation or log for context; keep the existing shuttingDown guard and the
existing success path for code === 0 unchanged.

In `@examples/ts-react-native-chat/scripts/smoke-server.ts`:
- Line 169: The content-type assertion in smoke-server.ts compares the entire
header string exactly, which fails when parameters like "; charset=utf-8" are
present; update the assertion that calls response.headers.get('content-type')
(the expression used in the failing assert.equal) to instead check only the
media type portion or to test startsWith/includes (e.g., trim and split on ';'
or use startsWith) and use an appropriate assertion (assert.ok/assert.match) so
the test accepts "application/x-ndjson" with optional parameters.
- Line 145: The assertion constructs a RegExp from LIVE_RECIPE_SERVER_ERROR
which can misinterpret regex metacharacters; change the check to a plain
substring match instead. Replace the call to assert.match(error.message, new
RegExp(LIVE_RECIPE_SERVER_ERROR)) with a string-based assertion such as
assert.include(error.message, LIVE_RECIPE_SERVER_ERROR) or
assert.ok(error.message.includes(LIVE_RECIPE_SERVER_ERROR)) so the test matches
the literal message text; keep references to LIVE_RECIPE_SERVER_ERROR and
error.message in the updated assertion.

In `@examples/ts-react-native-chat/src/server/index.ts`:
- Around line 11-17: The PORT env value is parsed into port using
Number.parseInt and may be NaN, which can break the call to serve; update the
initialization and before calling serve to validate the parsed port (e.g., parse
with Number.parseInt or Number(), then check Number.isFinite/Number.isInteger
and that port > 0 and within valid TCP port range), and if invalid fall back to
the default 8787 (or exit with a clear error); make this change around the
existing port variable and the serve({ fetch: app.fetch, hostname: '0.0.0.0',
port }) invocation so serve always receives a valid numeric port.

In `@pnpm-workspace.yaml`:
- Around line 6-7: Add an inline comment above the trustPolicyExclude entry in
pnpm-workspace.yaml explaining why semver@5.7.2 || 6.3.1 are excluded: state
which transitive packages pull them in (e.g., make-dir@2.1.0, `@babel/`*,
istanbul-lib-instrument), why they must bypass trustPolicy: 'no-downgrade'
(e.g., blocking build or incompatible with newer semver semantics), and note
whether upgrading the parent dependencies to eliminate these versions was
evaluated and is feasible (or why it is not), so the exclusion rationale is
recorded next to the trustPolicyExclude entry.

---

Nitpick comments:
In `@testing/react-native-smoke/scripts/esbuild-smoke.ts`:
- Around line 14-20: Remove the forbidden root alias entry from the esbuild
alias map: delete the '`@tanstack/ai`': resolve(repoRoot,
'packages/ai/src/index.ts') line in the alias object in esbuild-smoke.ts so
imports must use the allowed scoped entry ('`@tanstack/ai/client`'); this aligns
with the import-surface rule that disallows importing from the '`@tanstack/ai`'
root.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 60c0cd63-ed29-4403-a4c5-45d65705658a

📥 Commits

Reviewing files that changed from the base of the PR and between ef029a0 and be4108d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (78)
  • .changeset/react-native-ai-support.md
  • docs/advanced/tree-shaking.md
  • docs/api/ai-client.md
  • docs/api/ai-react.md
  • docs/chat/connection-adapters.md
  • docs/config.json
  • docs/getting-started/overview.md
  • docs/getting-started/quick-start-react-native.md
  • docs/getting-started/quick-start.md
  • docs/structured-outputs/multi-turn.md
  • examples/ts-react-native-chat/.gitignore
  • examples/ts-react-native-chat/README.md
  • examples/ts-react-native-chat/app.json
  • examples/ts-react-native-chat/index.ts
  • examples/ts-react-native-chat/metro.config.cjs
  • examples/ts-react-native-chat/package.json
  • examples/ts-react-native-chat/scripts/dev.mjs
  • examples/ts-react-native-chat/scripts/dev.test.mjs
  • examples/ts-react-native-chat/scripts/smoke-server.ts
  • examples/ts-react-native-chat/scripts/verify-react-resolution.mjs
  • examples/ts-react-native-chat/src/App.tsx
  • examples/ts-react-native-chat/src/server/app.ts
  • examples/ts-react-native-chat/src/server/index.ts
  • examples/ts-react-native-chat/tsconfig.json
  • package.json
  • packages/ai-client/src/chat-client.ts
  • packages/ai-client/src/connection-adapters.ts
  • packages/ai-client/src/events.ts
  • packages/ai-client/src/generation-client.ts
  • packages/ai-client/src/generation-types.ts
  • packages/ai-client/src/index.ts
  • packages/ai-client/src/realtime-client.ts
  • packages/ai-client/src/realtime-types.ts
  • packages/ai-client/src/response-stream.ts
  • packages/ai-client/src/sse-parser.ts
  • packages/ai-client/src/sse-utils.ts
  • packages/ai-client/src/tool-types.ts
  • packages/ai-client/src/types.ts
  • packages/ai-client/src/video-generation-client.ts
  • packages/ai-client/tests/chat-client-abort.test.ts
  • packages/ai-client/tests/chat-client.test.ts
  • packages/ai-client/tests/chat-fetcher.test.ts
  • packages/ai-client/tests/connection-adapters-abort.test.ts
  • packages/ai-client/tests/connection-adapters-xhr.test.ts
  • packages/ai-client/tests/connection-adapters.test.ts
  • packages/ai-client/tests/generation-client.test.ts
  • packages/ai-client/tests/infer-chat-messages.test.ts
  • packages/ai-client/tests/test-utils.ts
  • packages/ai-client/tests/tool-types.test.ts
  • packages/ai-client/tests/video-generation-client.test.ts
  • packages/ai-devtools/src/env.d.ts
  • packages/ai-devtools/tsconfig.json
  • packages/ai-preact/src/index.ts
  • packages/ai-react/src/index.ts
  • packages/ai-react/src/types.ts
  • packages/ai-react/src/use-chat.ts
  • packages/ai-solid/src/index.ts
  • packages/ai-svelte/src/index.ts
  • packages/ai-vue/src/index.ts
  • packages/ai/package.json
  • packages/ai/src/client.ts
  • packages/ai/vite.config.ts
  • packages/preact-ai-devtools/src/env.d.ts
  • packages/react-ai-devtools/src/env.d.ts
  • packages/solid-ai-devtools/src/env.d.ts
  • pnpm-workspace.yaml
  • testing/react-native-smoke/.gitignore
  • testing/react-native-smoke/README.md
  • testing/react-native-smoke/app.json
  • testing/react-native-smoke/index.ts
  • testing/react-native-smoke/metro.config.cjs
  • testing/react-native-smoke/package.json
  • testing/react-native-smoke/scripts/assert-bundle-output.ts
  • testing/react-native-smoke/scripts/assert-import-surface.ts
  • testing/react-native-smoke/scripts/esbuild-smoke.ts
  • testing/react-native-smoke/scripts/react-native-runtime-stub.tsx
  • testing/react-native-smoke/src/App.tsx
  • testing/react-native-smoke/tsconfig.json

Comment on lines +21 to 25
> **React Native or Expo app?** Use the headless React hooks with an absolute
> server URL and a mobile-compatible transport. See
> [Quick Start: React Native](./quick-start-react-native).

> **Tip:** If you'd prefer not to sign up with individual AI providers, [OpenRouter](../adapters/openrouter) gives you access to 300+ models with a single API key and is the easiest way to get started.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix adjacent blockquote formatting to satisfy markdownlint (MD028).

The new React Native callout is separated from the next blockquote by a blank line, which triggers no-blanks-blockquote. Remove the blank line or merge the two callouts into one contiguous blockquote.

Suggested diff
 > **React Native or Expo app?** Use the headless React hooks with an absolute
 > server URL and a mobile-compatible transport. See
 > [Quick Start: React Native](./quick-start-react-native).
-
 > **Tip:** If you'd prefer not to sign up with individual AI providers, [OpenRouter](../adapters/openrouter) gives you access to 300+ models with a single API key and is the easiest way to get started.

As per coding guidelines: "**/*.{ts,tsx,js,jsx,json,md}: Use Prettier for code formatting".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> **React Native or Expo app?** Use the headless React hooks with an absolute
> server URL and a mobile-compatible transport. See
> [Quick Start: React Native](./quick-start-react-native).
> **Tip:** If you'd prefer not to sign up with individual AI providers, [OpenRouter](../adapters/openrouter) gives you access to 300+ models with a single API key and is the easiest way to get started.
> **React Native or Expo app?** Use the headless React hooks with an absolute
> server URL and a mobile-compatible transport. See
> [Quick Start: React Native](./quick-start-react-native).
> **Tip:** If you'd prefer not to sign up with individual AI providers, [OpenRouter](../adapters/openrouter) gives you access to 300+ models with a single API key and is the easiest way to get started.
🧰 Tools
🪛 LanguageTool

[style] ~25-~25: Try using a synonym here to strengthen your writing.
Context: ...s, OpenRouter gives you access to 300+ models with a single API...

(GIVE_PROVIDE)

🪛 markdownlint-cli2 (0.22.1)

[warning] 24-24: Blank line inside blockquote

(MD028, no-blanks-blockquote)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/getting-started/quick-start.md` around lines 21 - 25, The two adjacent
blockquotes ("React Native or Expo app? Use the headless React hooks..." and the
"**Tip:** If you'd prefer not to sign up...") are separated by a blank line
which triggers markdownlint MD028; remove the empty line between them or merge
them into one contiguous blockquote so they form a single continuous blockquote
(locate the callout text starting with "React Native or Expo app?" and the
following "**Tip:**" paragraph and either delete the blank line or combine the
content into one blockquote).

Comment on lines +1 to +44
{
"name": "ts-react-native-chat",
"private": true,
"type": "module",
"main": "index.ts",
"expo": {
"install": {
"exclude": [
"@types/react",
"react",
"typescript"
]
}
},
"scripts": {
"dev": "node scripts/dev.mjs",
"dev:server": "tsx src/server/index.ts",
"dev:app": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'start', '--lan', '--clear']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"test:dev-script": "node --test scripts/dev.test.mjs",
"typecheck": "tsc --noEmit",
"smoke:server": "tsx scripts/smoke-server.ts",
"smoke:expo": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'export', '--platform', 'ios', '--no-bytecode', '--output-dir', '.expo-example-dist', '--max-workers', '0']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"verify:react-resolution": "node scripts/verify-react-resolution.mjs",
"smoke": "pnpm smoke:server && pnpm smoke:expo"
},
"dependencies": {
"@hono/node-server": "^1.19.6",
"@tanstack/ai": "workspace:*",
"@tanstack/ai-openai": "workspace:*",
"@tanstack/ai-react": "workspace:*",
"concurrently": "^9.1.2",
"dotenv": "^17.2.3",
"expo": "~54.0.34",
"hono": "^4.10.6",
"react": "19.1.0",
"react-native": "0.81.5"
},
"devDependencies": {
"@types/node": "^24.10.1",
"@types/react": "19.1.17",
"tsx": "^4.21.0",
"typescript": "5.9.3"
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import json, pathlib
p = pathlib.Path("examples/ts-react-native-chat/package.json")
data = json.loads(p.read_text())
print("packageManager:", data.get("packageManager"))
PY

Repository: TanStack/ai

Length of output: 76


Add packageManager to this example manifest.

examples/ts-react-native-chat/package.json is missing packageManager: "pnpm@10.17.0" per the **/package.json coding guideline.

Proposed fix
 {
   "name": "ts-react-native-chat",
   "private": true,
+  "packageManager": "pnpm@10.17.0",
   "type": "module",
   "main": "index.ts",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"name": "ts-react-native-chat",
"private": true,
"type": "module",
"main": "index.ts",
"expo": {
"install": {
"exclude": [
"@types/react",
"react",
"typescript"
]
}
},
"scripts": {
"dev": "node scripts/dev.mjs",
"dev:server": "tsx src/server/index.ts",
"dev:app": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'start', '--lan', '--clear']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"test:dev-script": "node --test scripts/dev.test.mjs",
"typecheck": "tsc --noEmit",
"smoke:server": "tsx scripts/smoke-server.ts",
"smoke:expo": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'export', '--platform', 'ios', '--no-bytecode', '--output-dir', '.expo-example-dist', '--max-workers', '0']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"verify:react-resolution": "node scripts/verify-react-resolution.mjs",
"smoke": "pnpm smoke:server && pnpm smoke:expo"
},
"dependencies": {
"@hono/node-server": "^1.19.6",
"@tanstack/ai": "workspace:*",
"@tanstack/ai-openai": "workspace:*",
"@tanstack/ai-react": "workspace:*",
"concurrently": "^9.1.2",
"dotenv": "^17.2.3",
"expo": "~54.0.34",
"hono": "^4.10.6",
"react": "19.1.0",
"react-native": "0.81.5"
},
"devDependencies": {
"@types/node": "^24.10.1",
"@types/react": "19.1.17",
"tsx": "^4.21.0",
"typescript": "5.9.3"
}
}
{
"name": "ts-react-native-chat",
"private": true,
"packageManager": "pnpm@10.17.0",
"type": "module",
"main": "index.ts",
"expo": {
"install": {
"exclude": [
"`@types/react`",
"react",
"typescript"
]
}
},
"scripts": {
"dev": "node scripts/dev.mjs",
"dev:server": "tsx src/server/index.ts",
"dev:app": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'start', '--lan', '--clear']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"test:dev-script": "node --test scripts/dev.test.mjs",
"typecheck": "tsc --noEmit",
"smoke:server": "tsx scripts/smoke-server.ts",
"smoke:expo": "node -e \"const { spawnSync } = require('node:child_process'); const env = { ...process.env, EXPO_NO_DOTENV: '1' }; delete env.OPENAI_API_KEY; delete env.OPENAI_MODEL; const args = ['exec', 'expo', 'export', '--platform', 'ios', '--no-bytecode', '--output-dir', '.expo-example-dist', '--max-workers', '0']; const result = process.platform === 'win32' ? spawnSync(env.ComSpec ?? 'cmd.exe', ['/d', '/s', '/c', 'pnpm.cmd', ...args], { stdio: 'inherit', env }) : spawnSync('pnpm', args, { stdio: 'inherit', env }); process.exit(result.status ?? 1)\"",
"verify:react-resolution": "node scripts/verify-react-resolution.mjs",
"smoke": "pnpm smoke:server && pnpm smoke:expo"
},
"dependencies": {
"`@hono/node-server`": "^1.19.6",
"`@tanstack/ai`": "workspace:*",
"`@tanstack/ai-openai`": "workspace:*",
"`@tanstack/ai-react`": "workspace:*",
"concurrently": "^9.1.2",
"dotenv": "^17.2.3",
"expo": "~54.0.34",
"hono": "^4.10.6",
"react": "19.1.0",
"react-native": "0.81.5"
},
"devDependencies": {
"`@types/node`": "^24.10.1",
"`@types/react`": "19.1.17",
"tsx": "^4.21.0",
"typescript": "5.9.3"
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-native-chat/package.json` around lines 1 - 44, Add the
missing packageManager field to the top-level package.json object in
examples/ts-react-native-chat by adding packageManager: "pnpm@10.17.0" (same
JSON object that contains "name", "private", "type", etc.); ensure the property
is a sibling to those keys so the package manifest follows the **/package.json
guideline.

Comment on lines +338 to +344
child.on('exit', (code, signal) => {
if (shuttingDown) return
if (code === 0 || signal) {
shutdown(0)
} else {
shutdown(code ?? 1)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Don’t treat all signal-based child exits as success.

Line 340 currently maps any signal exit to code 0. If a child is terminated unexpectedly (for example SIGKILL), the runner still exits successfully and masks failure.

Suggested fix
     child.on('exit', (code, signal) => {
       if (shuttingDown) return
-      if (code === 0 || signal) {
+      if (code === 0) {
         shutdown(0)
+      } else if (signal === 'SIGINT' || signal === 'SIGTERM') {
+        shutdown(0)
       } else {
-        shutdown(code ?? 1)
+        shutdown(code ?? 1)
       }
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-native-chat/scripts/dev.mjs` around lines 338 - 344, The
exit handler for child.on('exit') incorrectly treats any signal-based exit as
success; update the callback (the child.on('exit', (code, signal) => { ... })
block) so that if signal is non-null you call shutdown with a non-zero code
(e.g., shutdown(1) or shutdown with a code derived from the signal) instead of
shutdown(0), and include the signal in the shutdown invocation or log for
context; keep the existing shuttingDown guard and the existing success path for
code === 0 unchanged.


assert.equal(response.status, 200)
assert.ok(error)
assert.match(error.message, new RegExp(LIVE_RECIPE_SERVER_ERROR))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use string matching instead of constructing a regex from message text.

Line 145 builds a RegExp from LIVE_RECIPE_SERVER_ERROR; metacharacters in the message can make this assertion flaky.

Suggested fix
-    assert.match(error.message, new RegExp(LIVE_RECIPE_SERVER_ERROR))
+    assert.ok(error.message.includes(LIVE_RECIPE_SERVER_ERROR))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert.match(error.message, new RegExp(LIVE_RECIPE_SERVER_ERROR))
assert.ok(error.message.includes(LIVE_RECIPE_SERVER_ERROR))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-native-chat/scripts/smoke-server.ts` at line 145, The
assertion constructs a RegExp from LIVE_RECIPE_SERVER_ERROR which can
misinterpret regex metacharacters; change the check to a plain substring match
instead. Replace the call to assert.match(error.message, new
RegExp(LIVE_RECIPE_SERVER_ERROR)) with a string-based assertion such as
assert.include(error.message, LIVE_RECIPE_SERVER_ERROR) or
assert.ok(error.message.includes(LIVE_RECIPE_SERVER_ERROR)) so the test matches
the literal message text; keep references to LIVE_RECIPE_SERVER_ERROR and
error.message in the updated assertion.

})

assert.equal(response.status, 200)
assert.equal(response.headers.get('content-type'), 'application/x-ndjson')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make the content-type assertion tolerant of parameters.

Line 169 requires an exact value. Responses commonly include ; charset=utf-8, which would fail this smoke check unnecessarily.

Suggested fix
-  assert.equal(response.headers.get('content-type'), 'application/x-ndjson')
+  assert.ok(
+    response.headers.get('content-type')?.startsWith('application/x-ndjson'),
+  )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert.equal(response.headers.get('content-type'), 'application/x-ndjson')
assert.ok(
response.headers.get('content-type')?.startsWith('application/x-ndjson'),
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-native-chat/scripts/smoke-server.ts` at line 169, The
content-type assertion in smoke-server.ts compares the entire header string
exactly, which fails when parameters like "; charset=utf-8" are present; update
the assertion that calls response.headers.get('content-type') (the expression
used in the failing assert.equal) to instead check only the media type portion
or to test startsWith/includes (e.g., trim and split on ';' or use startsWith)
and use an appropriate assertion (assert.ok/assert.match) so the test accepts
"application/x-ndjson" with optional parameters.

Comment on lines +11 to +17
const port = Number.parseInt(process.env.PORT ?? '8787', 10)

serve({
fetch: app.fetch,
hostname: '0.0.0.0',
port,
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate PORT before passing it to serve.

Number.parseInt can return NaN for malformed env input, and passing that through can fail startup.

Proposed fix
-const port = Number.parseInt(process.env.PORT ?? '8787', 10)
+const parsedPort = Number.parseInt(process.env.PORT ?? '8787', 10)
+const port =
+  Number.isInteger(parsedPort) && parsedPort > 0 && parsedPort <= 65535
+    ? parsedPort
+    : 8787
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/ts-react-native-chat/src/server/index.ts` around lines 11 - 17, The
PORT env value is parsed into port using Number.parseInt and may be NaN, which
can break the call to serve; update the initialization and before calling serve
to validate the parsed port (e.g., parse with Number.parseInt or Number(), then
check Number.isFinite/Number.isInteger and that port > 0 and within valid TCP
port range), and if invalid fall back to the default 8787 (or exit with a clear
error); make this change around the existing port variable and the serve({
fetch: app.fetch, hostname: '0.0.0.0', port }) invocation so serve always
receives a valid numeric port.

Comment thread pnpm-workspace.yaml
Comment on lines +6 to +7
trustPolicyExclude:
- 'semver@5.7.2 || 6.3.1'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Analyze semver dependency tree to understand version conflicts

echo "=== Checking semver dependency tree ==="
pnpm why semver

echo -e "\n=== Checking for packages that depend on old semver versions ==="
pnpm list semver --depth=Infinity --long

echo -e "\n=== Checking if React Native packages are pulling in old semver ==="
rg -n 'semver.*5\.7\.2|semver.*6\.3\.1' --glob '**/package.json' -C2

Repository: TanStack/ai

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== pnpm-workspace.yaml (lines around trustPolicy) ==="
nl -ba pnpm-workspace.yaml | sed -n '1,40p'

echo -e "\n=== pnpm why semver@5.7.2 (limit output) ==="
pnpm why semver@5.7.2 --depth 1 || true

echo -e "\n=== pnpm why semver@6.3.1 (limit output) ==="
pnpm why semver@6.3.1 --depth 1 || true

echo -e "\n=== Top-level dependents for semver@5.7.2 ==="
pnpm list --depth 0 semver@5.7.2 || true

echo -e "\n=== Top-level dependents for semver@6.3.1 ==="
pnpm list --depth 0 semver@6.3.1 || true

echo -e "\n=== Find exact entries of trustPolicyExclude in repo ==="
rg -n "trustPolicyExclude|trustPolicy:" -S pnpm-workspace.yaml **/pnpm-workspace.yaml **/*pnpm-workspace*.yaml || true

Repository: TanStack/ai

Length of output: 171


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== pnpm-workspace.yaml (lines around trustPolicy) ==="
cat -n pnpm-workspace.yaml | sed -n '1,80p'

echo -e "\n=== pnpm-workspace.yaml (grep trustPolicy*) ==="
rg -n "trustPolicy" pnpm-workspace.yaml || true

echo -e "\n=== pnpm why semver@5.7.2 (limit output) ==="
pnpm why semver@5.7.2 --depth 1 || true

echo -e "\n=== pnpm why semver@6.3.1 (limit output) ==="
pnpm why semver@6.3.1 --depth 1 || true

echo -e "\n=== Find exact entries of trustPolicyExclude in repo ==="
rg -n "trustPolicyExclude|trustPolicy:" -S pnpm-workspace.yaml **/pnpm-workspace*.yaml **/*pnpm*.yaml || true

Repository: TanStack/ai

Length of output: 2821


🌐 Web query:

pnpm trustPolicy no-downgrade trustPolicyExclude semantics

💡 Result:

The trustPolicy and trustPolicyExclude settings are supply chain security features in pnpm (introduced in v10.21.0 and v10.22.0, respectively) designed to mitigate risks from potentially compromised packages [1][2]. trustPolicy: no-downgrade When set to no-downgrade (default is off), pnpm checks the "trust level" of a package version against its history [1][2]. The trust levels are ordered: Trusted Publisher > Provenance > None [3]. If a package version has a lower trust level than any previously published version, pnpm will fail the installation [1][3]. These checks are based on the publication date, not semantic versioning [1]. Starting in v10.24.0, prerelease versions are ignored during this evaluation to prevent trusted prereleases from blocking stable releases that lack trust evidence [1]. trustPolicyExclude This setting (type: string[]) allows you to provide a list of package selectors to bypass the trustPolicy check [1][2]. This is useful for allowing specific, known-safe versions that do not meet the stricter trust requirements [1][2]. You can specify exact versions (e.g., 'chokidar@4.0.3'), version ranges (e.g., 'webpack@4.47.0 || 5.102.1'), or even exclude all versions of a package by omitting the version [1][4]. In summary, trustPolicy: no-downgrade enforces that trust evidence for a package never regresses, while trustPolicyExclude provides a necessary escape hatch for legitimate cases where downgrading the trust evidence is expected [1][3][2].

Citations:


Document and justify the trustPolicyExclude exception for semver

pnpm-workspace.yaml sets trustPolicy: 'no-downgrade' and excludes semver@5.7.2 || 6.3.1. Those semver versions come in transitively (semver@5.7.2 via make-dir@2.1.0; semver@6.3.1 via several @babel/* packages and istanbul-lib-instrument). Because no-downgrade is enforced on pnpm’s supply-chain “trust level” regression (not semver ordering), an exclusion can be a legitimate escape hatch—but it materially weakens the policy unless the rationale is recorded.

Add a short comment explaining why these specific versions must bypass no-downgrade (and whether upgrading the pulling deps to avoid them is feasible).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pnpm-workspace.yaml` around lines 6 - 7, Add an inline comment above the
trustPolicyExclude entry in pnpm-workspace.yaml explaining why semver@5.7.2 ||
6.3.1 are excluded: state which transitive packages pull them in (e.g.,
make-dir@2.1.0, `@babel/`*, istanbul-lib-instrument), why they must bypass
trustPolicy: 'no-downgrade' (e.g., blocking build or incompatible with newer
semver semantics), and note whether upgrading the parent dependencies to
eliminate these versions was evaluated and is feasible (or why it is not), so
the exclusion rationale is recorded next to the trustPolicyExclude entry.

AlemTuzlak and others added 3 commits May 26, 2026 23:01
…property

Node 24.8 rejects non-enumerable data descriptors on process.env with
ERR_INVALID_OBJECT_DEFINE_PROPERTY, breaking `pnpm dev`. Track loaded
.env keys via a module-scoped WeakMap keyed by the env object instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants