Skip to content

test(tanstackstart-react): Add source map E2E app - #25363

Open
s1gr1d wants to merge 34 commits into
sig/tanstack-start-source-mapsfrom
sig/tanstack-start-source-maps-e2e
Open

s1gr1d wants to merge 34 commits into
sig/tanstack-start-source-mapsfrom
sig/tanstack-start-source-maps-e2e

Conversation

@s1gr1d

@s1gr1d s1gr1d commented Oct 14, 2026

Copy link
Copy Markdown
Member

Adds the tanstackstart-react-sourcemaps E2E app. It builds a TanStack Start app with Nitro against a mock Sentry server and checks the upload:

  • The source maps carry the client and server source.
  • Their source paths are relative to the project root.
  • Every debug ID has an uploaded source map.
  • No source map stays in the build output.

The app is optional in CI because Nitro 3 is in beta.

Stacked on #25360.

Refs #21012

@s1gr1d
s1gr1d marked this pull request as ready for review October 14, 2026 11:59
Build a TanStack Start app with Nitro against a mock Sentry server and check the upload: the
source maps carry the client and server source, their source paths are relative to the project
root, every debug ID has an uploaded source map, and no source map stays in the build output.

The app is optional in CI because Nitro 3 is in beta.
@s1gr1d
s1gr1d force-pushed the sig/tanstack-start-source-maps-e2e branch from 8613745 to 1d9d414 Compare October 14, 2026 12:08
@s1gr1d

s1gr1d commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1d9d414. Configure here.

Both markers are in the same route file, which client and server source maps embed completely,
so one uploaded map could pass both checks. Check each marker only in the source maps of the
files in its output directory, matched by debug ID.
@s1gr1d
s1gr1d requested review from JPeer264 and nicohrubec October 14, 2026 12:58
Comment on lines +62 to +63
// TODO: The uploader passes the emitted file paths to `glob` as patterns, so it skips a file whose path has glob
// characters, like Nitro's `_libs/@tanstack/react-router+[...].mjs`. Remove this when the uploader matches paths literally.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Tracking issue for this: #25321

@s1gr1d
s1gr1d added this pull request to stack #25439 October 16, 2026 08:29
s1gr1d and others added 9 commits October 16, 2026 13:35
…yload to Sentry (#25456)

Today, `tunnelRoute` rewrites tunnel requests to Sentry as a proxy. This
PR adds an opt-in handler mode: if `app/sentry-tunnel/route.ts` (or
`src/app/sentry-tunnel/route.ts`) exists, the SDK rewrites the tunnel
route internally to it, and the handler sends only the event payload to
Sentry. Without the file, nothing changes.

```ts
// app/sentry-tunnel/route.ts
export { POST } from '@sentry/nextjs/tunnel';
```

- When the file exists, but there's no `tunnelRoute` option, a warning
is shown.
- The rewrite is in `beforeFiles`, so a public file or page at the
tunnel path cannot shadow it.
- Like TanStack Start, the handler accepts only the server SDK's DSN. We
also export `createSentryTunnelRoute({ allowedDsns })` which covers
other DSNs.
- Fixed: Tunnel request spans were sent with span streaming, in rewrite
mode too. They are now dropped.

Tested with unit tests, two new `nextjs-16-tunnel` variants
(Turbopack/Node.js, webpack/edge) that also check the forwarded headers.

Also tested with a manual Vercel deploy on Next.js 14-16.

Fixes JS-3927

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Hono is already stable since a while now. But I discovered this leftover
notice in the `package.json`, which should be removed.
)

Since #25403, E2E jobs no longer restore the root `node_modules`. pnpm
then installs all 63 workspace projects before the first `pnpm run
test:prepare`, because `verify-deps-before-run` finds the dependencies
missing. That's about 26s per E2E job, which cancels out most of what
#25403 saved. It also made the background Playwright install flaky: its
`npx` ran from the repo root and could find the hoisted
`node_modules/playwright` before pnpm had linked its binary
(`playwright: not found`). The scripts run with `pnpm run` here are
plain `node` scripts, so disabling the check is enough. It's set per
job, so local runs keep it.

Without the long install, the foreground steps start in quick
succession, which exposes a runner bug. The runner sets up the steps of
a background composite action from the job's shared `github` context
([`CompositeActionHandler.cs`](https://github.com/actions/runner/blob/main/src/Runner.Worker/Handlers/CompositeActionHandler.cs)
→ `GitHubContext.ShallowCopy()`). If a foreground step starts at that
moment, this fails with `Collection was modified; enumeration operation
may not execute`. The background install now starts right before the app
build, so nothing else starts while it sets up. The steps it no longer
overlaps with take well under a second.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… CJS build of @sentry/server-utils (#25359)

closes #25339
closes [JS-3931](https://linear.app/getsentry/issue/JS-3931)

## Description

This makes the orchestrion Vite plugin bundle the ESM build of
`@sentry/server-utils`, also when an instrumented CommonJS module (for
example Express's `router` or `mysql`) `require()`s it. Vite 5 to 7
turned the `require("@sentry/core")` of the CJS build into a default
import of the external `@sentry/core`, which crashed the server at
startup. This fixes it for every SDK that uses the plugin, not only
React Router. On Vite 8, which did not crash, it also removes a second
copy of `@sentry/core`.

Reproduction: https://github.com/JPeer264/sentry-repros/tree/issue-25339

## Resolution decisions

`resolveId` maps the resolved `build/cjs/index.js` to
`build/esm/index.js`. Resolving the `require()` with import conditions
also works, but then `@rollup/plugin-commonjs` warns
`THIS_RESOLVE_WITHOUT_OPTIONS`.

## Test decisions

`react-router-8-framework` now depends on `@sentry/core` directly, so
Vite keeps it external, as in a hoisted (npm) install. With pnpm's
isolated layout, Vite bundled it and the crash did not happen. The
alternative is a new e2e app with a hoisted install.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…d RSC responses (#24932)

closes #22876
closes
[JS-3236](https://linear.app/getsentry/issue/JS-3236/provide-a-mechanism-to-override-classifyresponsestreaming-behaviour)

## Description

This adds an `isStreamingResponse` option, so users can keep the
`http.server` span open until a streamed response has been sent, for
example for streamed SSR pages. Spans created while the body streams
then stay inside the request.

- `text/x-component` (RSC) responses without a `Content-Length` header
now count as streamed by default. The classifier is shared, so this also
applies to Bun and Deno.
- Precompressed bodies with `encodeBody: 'manual'` are no longer
compressed a second time.
- Workers with a compatibility date before 2022-11-30 send the span
again. They ignore the `TransformStream` transformer, so the SDK ends
the span at handler return there.

```ts
export default defineCloudflareOptions(env => ({
  dsn: env.SENTRY_DSN, 
  isStreamingResponse: response =>
    response.headers.get('content-type')?.startsWith('text/html') ? true : undefined,
}));
```

## Detection decisions

HTML is not streamed by default, because a buffered `new Response(html)`
has no `Content-Length` header either and would also go through the
`TransformStream`. The alternative is to treat HTML without a
`Content-Length` as streamed, which fixes streamed SSR without
configuration.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tLines (#25535)

`ContextLines` read context lines from any `filename` in a parsed stack
trace. An app that copies external input into `error.stack` before
`captureException` let that input name any file on disk, such as `.env`,
and the SDK sent its lines to Sentry.

The integration now reads only files with a JS or TS source extension,
plus `.vue`, `.svelte`, `.astro` and `.mdx`. Query strings and hashes
are stripped first so Vite style `index.js?v=abc` frames still pass. The
Deno SDK gets the same check.

An extension allowlist was chosen over `require.cache` or a project root
check: the module cache does not cover ESM, and root checks break
monorepos and symlinked installs.

@JPeer264 JPeer264 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM. Tests fail though because the pnpm-workspace is missing

s1gr1d and others added 8 commits October 19, 2026 20:19
The tunnel route handlers (`@sentry/nextjs/tunnel` and TanStack Start's
`createSentryTunnelRoute`) now check the request size before they read
the body.

A request larger than 20 MiB gets a 413, and a request without
`Content-Length` gets a 411. So one request can no longer make the
server hold more than 20 MiB in memory. SDK envelopes are much smaller
than this limit, so normal traffic does not change.

Checking the header is enough, because the HTTP server stops reading the
body at `Content-Length`.

Stacked on #25456.
Cloudflare runs on workerd with no Node APIs, but it still exports more
than Deno. The missing exports already work in Deno, because they come
from `@sentry/core` and `@sentry/server-utils`, which Deno depends on.

The manual AI client helpers matter most. Deno does not inject
diagnostics channels by default, so `instrumentOpenAiClient` and the
other `instrument*` helpers are the documented way to get AI spans
there.

Deno now re-exports `addIntegration`, `withActiveSpan`,
`getSpanDescendants`, `setConversationId`, `createConsolaReporter`,
`growthbookIntegration`, `moduleMetadataIntegration`, `trpcMiddleware`,
`prismaIntegration`, the OpenAI, Anthropic, Google GenAI, Mistral,
TypeSafe, LangChain and LangGraph helpers, and the `FlueOptions` type.
Deno also gets `initWithoutDefaultIntegrations` and
`getDefaultIntegrationsWithoutPerformance`, to match Node and Bun. The
default integration set does not change.

Some Cloudflare exports stay out. `instrumentWorkersAiClient` wraps a
binding that exists only on Cloudflare. `spotlightIntegration` and
`httpServerIntegration` live in the Cloudflare package, not in a shared
entry point, so they need more than a re-export. `parameterize` was in
the issue, but Cloudflare no longer exports it.

Fixes #23896
Fixes JS-3522

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…25493)

`Deno.serve` is the server most Deno apps use, but its integration only
took `maxRequestBodySize` and the deprecated `ignoreStatusCodes`. A user
could not skip body capture for one route, such as a health check, an
upload, or a route that receives secrets. `denoHttpIntegration`, Node
and Cloudflare all allow this.

`denoServeIntegration` now accepts `ignoreRequestBody`,
`ignoreIncomingRequests`, `ignoreStaticAssets`, `spans`, `onSpanCreated`
and `onSpanEnd`. They use the same names and behavior as
`denoHttpIntegration`, but the callbacks receive the web `Request`.

The options follow core's `node:http` server code. `urlPath` includes
the query string, and the static asset filter applies only to `GET`.
When the SDK skips a span, it still continues the incoming trace,
attaches request data and the body, and captures errors. For a streamed
response, `onSpanEnd` runs when the stream finishes. It does not run
when the handler throws, because there is no `Response` to pass.

Two defaults change behavior. `ignoreStaticAssets` defaults to `true`,
so `GET` requests for files such as `favicon.ico` or `*.js` no longer
create spans. `spans` defaults to the client's tracing configuration, so
the integration no longer starts spans when tracing is disabled.

Request sessions for `Deno.serve` and the same options for `Bun.serve`
remain separate work.

Fixes #25492
Fixes JS-3955

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`denoHttpIntegration` records a request session for each `node:http`
request, but `denoServeIntegration` recorded none. Most Deno apps use
`Deno.serve`, so release health showed no request sessions and no
crash-free rate for them.

The shared helper `recordRequestSession` waits for the `close` event of
a `node:http` response, which `Deno.serve` does not have. Core now
exports `startRequestSession`, which starts the session and returns an
`end()` function. `recordRequestSession` keeps its signature and calls
`end()` from the `close` event, so Node does not change. The helper now
defaults `sessionFlushingDelayMS` to 60 seconds. Before, only core's
`node:http` subscription code set that default, and a caller that left
the option out got a timer that fired at once.

`denoServeIntegration` accepts `sessions` (default `true`) and
`sessionFlushingDelayMS`, as `denoHttpIntegration` does. The session
ends when the response finishes. For a streamed response, that is when
the stream finishes. When the handler throws, the session ends after the
SDK captures the error, so it counts as crashed. Sessions do not depend
on tracing, so they are also recorded when the SDK skips the span. To
match Node, OPTIONS and HEAD requests also record sessions.

Fixes #25495
Fixes JS-3956

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The `@Interval` (and `@Cron`) handlers in `nestjs-basic` threw an
identical error on every tick, and the `Dedupe` integration drops
consecutive identical errors. Once earlier tests have killed the other
erroring cron jobs, nothing interleaves, so only the first interval
error is ever sent — often before the test starts listening — and
`waitForError` times out.

The handlers now include `Date.now()` in the error message so every tick
produces a distinct error, and the tests only accept an error thrown
after the test started.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Adds a check that every `package.json` declares the same exact
`@playwright/test` version as `@sentry-internal/test-utils`. That covers
the root, `packages/*`, `dev-packages/*` and every E2E app. It runs in
the Lint job's parallel group, so a drifting app fails fast instead of
silently testing against different browsers.

`@sentry-internal/test-utils` is the reference because E2E apps already
depend on it for their Playwright config. The check is an inline `git
grep` over tracked `package.json` files, so it needs no installed
dependencies and picks up new locations without a list to maintain. Only
`@playwright/test` is checked, since `playwright` and `playwright-core`
never appear without it.

Based on #25404, outside the PR stack.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The Nuxt `errors.client` and `errors.server` E2E tests sometimes time
out because they click server-rendered buttons before Vue has hydrated
and attached the handlers. The click gets dropped, so no request or
error happens. In the failed runs, the server never even logged the
`/api/server-error` request. The race exists because the Sentry client
config is loaded through a dynamic `import()` in a Nuxt plugin.
`page.goto` resolves on `load`, which doesn't wait for that chunk, and
Nuxt doesn't mount the app until its plugins finish.

The tests now wait until `#__nuxt.__vue_app__.$nuxt.isHydrating ===
false` before clicking. That is the flag Nuxt clears once the root
Suspense resolves after hydration, so it shows the handlers are
attached. `typeof window.__SENTRY__ === 'object'` only shows that the
SDK loaded, which can happen before mount. The helper replaces the one
existing `__SENTRY__` wait in `nuxt-3-dynamic-import`. I checked the
race with a minimal Nuxt app whose plugin awaits a delayed dynamic
chunk: clicking right after `goto` was a no-op, and clicking after this
wait worked.

The helper is copied into each Nuxt test app because each app is a
standalone package.

<!-- junior-request-attribution:start -->
via **Francesco Gringl-Novy**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3Aa39e9eec22d2b41c897347df8de17187 -->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/agent%3Aa39e9eec22d2b41c897347df8de17187)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Aa39e9eec22d2b41c897347df8de17187/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Francesco Gringl-Novy <francesco.novy@sentry.io>
`install-playwright` ran `npx playwright@<version>` from the repo root.
With `nodeLinker: hoisted`, npm can find `node_modules/playwright` there
before pnpm has linked its binary, so a `pnpm install` running in
parallel made it fail with `playwright: not found`. E2E jobs no longer
install the workspace since #25514, but the action also runs in other
jobs, so it now runs `npx` in `runner.temp`. It also passes the browsers
to `install-deps` as separate arguments, which only mattered for the
multi-browser default.

The original change here, starting the background install before the E2E
setup steps, is dropped. #25514 moved it right before the build, because
a background composite action crashes the runner with "Collection was
modified" when a foreground step starts while it sets up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
timfish and others added 13 commits October 20, 2026 13:01
…t startup (#25541)

The Rollup, Vite and webpack plugins added the `SENTRY_RELEASE` snippet
to every JS chunk. On apps with many lazily loaded chunks that is a lot
of repeated output, and the global only needs to exist before
`Sentry.init` runs.

The release snippet now goes only into entry chunks and the chunks they
reach through static imports. Debug ID and module metadata injection
stay per chunk. esbuild already injects per entry point and does not
change.

A plain `chunk.isEntry` check is not enough: Vite multi-page builds drop
pure-import HTML facade chunks and put the app code in non-entry shared
chunks. The Rollup plugin walks the chunk graph that `renderChunk`
receives, and injects everywhere when no graph is available. The webpack
plugin uses `chunk.canBeInitial()`.
This moves all GitHub Actions jobs to Namespace runners. It keeps
node_modules, the pnpm store and the Playwright browsers on Namespace
cache volumes instead of the GitHub cache, so every job runs `pnpm
install --frozen-lockfile` and there is no dependency cache key anymore.

## Runner decisions

Jobs use 3 profiles, sized from the CPU and memory peaks in `nsc
instance report`: `-small` (2 vCPU, 4 GB) for bots and checks, the
default (4 vCPU, 8 GB) for E2E and light jobs, `-large` (8 vCPU, 16 GB)
for build, lint and tests. One profile for all jobs is simpler, but
slower or more expensive.

## Cache decisions

All `build.yml` jobs share one cache volume through
`cache-tag=sentry-javascript`, for all branches (last write wins).
Alternative: let only `develop` write to it ("Protect caches from
updates").

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…25509)

The tunnel route is same-origin, so the browser transport includes
credentials (cookies, HTTP authentication) with each request, and the
tunnel route does not need them. With `tunnelRoute`, `@sentry/nextjs`
now sets `credentials: 'omit'` on the transport.

Apps with a layer in front that checks credentials (for example Vercel
Deployment Protection) can opt back in:

```js
// instrumentation-client.ts
Sentry.init({
  transportOptions: { fetchOptions: { credentials: 'same-origin' } },
});
```


Fixes JS-3927
#25513)

This fixes the `E2E solidstart-2` failure on `develop`. Since #25295,
the "sends a server action span on client navigation" test times out
because the `POST getPrefecture` segment is never sent.

_Root cause:_ After #25295, Nitro's h3 route handler opens a `handler`
span instead of a nested `http.server` span.
`withServerActionInstrumentation` decided whether to rename the segment
by reading the attributes of the *active* span. Now that span is the
`handler` span, which has no `url.path`, so the `/_server` check never
matched and the server action name was never applied.

The fix reads the attributes from the root span instead. That is the
request's `http.server` span, the one `setHttpServerSpanRouteAttribute`
renames, so the check and the rename now target the same span whatever
the framework nests in between.

Locally, `solidstart-2` E2E fails 1/9 (this test) without the change and
passes 9/9 with it.

<!-- junior-request-attribution:start -->
via **Francesco Gringl-Novy**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!--
junior-conversation-id:agent-dispatch%3Adispatch_6234d76cd131dcc5f1925dab1ce334ad
-->

--

[View Junior
Session](https://junior-prod.sentry.dev/conversations/agent-dispatch%3Adispatch_6234d76cd131dcc5f1925dab1ce334ad)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent-dispatch%3Adispatch_6234d76cd131dcc5f1925dab1ce334ad/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: Francesco Gringl-Novy <francesco.novy@sentry.io>
…25448)

`use cache` hits get a `cache_origin` link to their fill. The SDK
remembered fills per process. Previously, deployments on serverless
(e.g. Vercel), another instance usually did the fill, so most hits got
no link.

The SDK now also stores the fill's `sentry-trace` value on the cache
entry, so other instances find it. The per-process table stays as the
fallback for handlers that drop unknown fields.

- In a Vercel test, about 85% of `use cache: remote` hits came from
another instance
- With the locally built SDK on Vercel, all 148 logged hits linked to
their fill (also across instances)


Closes #24296
Ref #12888


[JS-3657](https://linear.app/getsentry/issue/JS-3657/decide-the-origin-transport-mechanism-cache-written-to-different)
The nuxt-4 e2e jobs keep failing because the `mongo:8` container goes
unhealthy about 10s after it starts, fixed by pinning the image to
`mongo:8.0` and printing the container logs when setup fails so the next
failure shows why.
Under `cacheComponents` the SDK did not enable
`experimental.clientTraceMetadata` (#21141). A shell that Next.js
prerenders during one request is cached and served to later visitors,
and a `sentry-trace` tag inside it would attach all of them to that one
request. ISR pages had the same problem, worked around on the client by
removing the tags on routes with `generateStaticParams` (#18192).

Next.js computes the trace tags of a document inside the `prerender
route (app)` span when it prerenders, and outside of it when it renders
for a request, including when it resumes a shell. The server SDK now
marks that span on the OpenTelemetry context, and its propagator hands
out nothing on a marked context. A prerendered shell never contains a
trace, so any tag that reaches the browser belongs to the current
request. The mark sits on the context rather than the span because an
unsampled request makes the span non-recording, with no attributes to
recognize it by, and a `sampled=0` tag in the shell would drop every
later pageload. Checked in Next.js 15.0 through 16.4.

With that, `clientTraceMetadata` is enabled again under
`cacheComponents`, and the client-side ISR workaround and the
`isrRoutes` manifest field are removed. The guard covers ISR shells too,
including `revalidate` routes the file scan never found.

The guard is applied to the tracer provider and propagator the Node SDK
registers, and `withSentry` of `@sentry/nextjs/cloudflare` applies it to
its own setup, since ISR pages revalidate inside a request of the
Worker. A custom OpenTelemetry setup does not get it automatically. If
it registers `SentryPropagator`, it can register the exported
`NextSentryPropagator` instead and pass its tracer provider to
`markPrerendersOnTracerProvider`. Setups with only the OpenTelemetry W3C
propagators, which includes `@vercel/otel` by default, write no
`sentry-trace` tag, so nothing changes for them. The docs follow in
sentry-docs.

An ISR page with `revalidate = 1` makes the e2e suite exercise the guard
at runtime; the other ISR shells come from the build, where the SDK does
not run.

On its own this PR keeps the current client behaviour: tags in `<head>`
are continued and are now always fresh. The tag of a resumed shell
arrives with the resumed part, which the client only reads in #25142.

<img width="2240" height="1332" alt="figure-shell"
src="https://github.com/user-attachments/assets/809726dd-cddb-4fab-bfee-83bcdc35dbab"
/>

Refs #24944, see #24298

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com>
Same fix as #25557, applied to nuxt-5. It failed the same way on #25552
(`container e2e-tests-nuxt-5-mongo is unhealthy`)

The floating `mongo:8` tag moved to 8.3 on 2026-10-17. On CI the
container goes unhealthy some seconds after start. That is much earlier
than the healthcheck retries allow, so the server process exits, and
Docker then reports the container as unhealthy. If setup fails again,
the container logs now print, so we can see why mongod exits.
…ackages on Vite 8 (#25523)

We put pg into ssr.noExternal and that bundles `pg` and changes its CJS
imports. In the second bundling step, Rolldown in Vite 8 emits
`__require("pg-types")` for the dependency imports of pg (see [their
migration
guide](https://vite.dev/guide/migration#require-calls-for-externalized-modules)
mentioning `esmExternalRequirePlugin` - also check the linked rolldown
docs). As `__require` comes from `module.createRequire`, the bundlers
think it's a normal function variable, and not an import. Therefore, a
sub-dependency like `pg-types` cannot get into the final bundle.

The plugin now bundles exactly these `require()` calls. The user's
`ssr.external` entries and optional dependencies stay external. We
didn't use `esmExternalRequirePlugin`, because it breaks
function-exported CommonJS on Node < 23.

Stacked on #25359.

Fixes #25470


---

**Same problem: `__require()` of an external package stays in bundled
CommonJS**

- nitrojs/nitro#4171
- rolldown/rolldown#10643
- rolldown/rolldown#9407

**Workarounds and fixes in other tools**

- nitrojs/nitro#4365
- vitejs/vite#22618
- vitejs/vite#23759
- vitejs/vite#21969

**Existing Sentry issues**

- #24775
- #25339
… the pageload (#25142)

Builds on #25141.

When a document resumes a prerendered shell, its `sentry-trace` tag sits
at the start of the resumed part, which arrives after the SDK has
started. In a local demo six of seven visits found no tag at init, so
the pageload could only link the server trace instead of joining it.

The app router pageload now continues whatever tag is in the document,
last one wins. In apps with `cacheComponents` enabled, if there is no
tag, no `Server-Timing` trace and the document is still streaming, it
waits for the tag before creating the pageload span. Other apps keep the
immediate start: without `cacheComponents` a tag is either in `<head>`
from the start or never comes, so waiting only held the pageload back.
The wait ends at `DOMContentLoaded` or after 10 seconds. The span still
starts at the time origin and gets its children from the performance
timeline, so it looks the same as before, only inside the server's
trace. No tag by the end of parsing means a fresh trace, which is the
fully static case.

A client-side navigation during the wait starts the pageload with a
fresh trace first, so the navigation ends it like any pageload it
interrupts, as today. Started later, the pageload would end the
navigation span instead. The pageload keeps the URL the document was
loaded with, even if the navigation already changed it.

Waiting was chosen over moving a started span to another trace. That
needs core changes and does not work with span streaming once children
are flushed. The cost: fetches, errors and clicks between SDK start and
the resumed part are not attached to the pageload. The `Server-Timing`
follow-ups (JS-3864, JS-3866) remove that window where the header can be
set.

<img width="2240" height="1904" alt="figure-ppr"
src="https://github.com/user-attachments/assets/c6049dff-1ceb-4a09-9467-b2436648cf6c"
/>

Fixes #24944

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com>
…ories (#25450)

When Sentry enables `'hidden'` source maps, it deleted them with
folder-name globs like `.*/**/public/**/*.map`. As discussed in
[#25362](#25362 (comment)),
a static glob cannot know where a preset writes its output.

- Reads `nitro.options.output.publicDir` and `serverDir` instead
(available since nitropack 2.6.1, Nuxt 3.7.0).
- Fixes client maps leaking in `dist` on Netlify and Cloudflare Pages,
and keeps the server maps in `dist/_worker.js`.
- Keeps server maps enabled with `nitro.rollupConfig.output.sourcemap`.

Tested with unit tests on real files, and with new `netlify` and
`cloudflare-pages` E2E variants.
…-source-maps-e2e

# Conflicts:
#	CHANGELOG.md
Install test-utils from the tarball, pin Playwright to 1.64.1 and add
the app's pnpm-workspace.yaml.
@s1gr1d
s1gr1d requested review from a team as code owners October 20, 2026 15:48
@s1gr1d
s1gr1d requested review from chargome, isaacs, logaretm and mydea and removed request for a team October 20, 2026 15:48

This branch has not been deployed

No deployments
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.

8 participants