Skip to content

feat: admin framework adapter pattern and tanstack support#16139

Draft
r1tsuu wants to merge 136 commits intomainfrom
experiment/framework-adapter-pattern
Draft

feat: admin framework adapter pattern and tanstack support#16139
r1tsuu wants to merge 136 commits intomainfrom
experiment/framework-adapter-pattern

Conversation

@r1tsuu
Copy link
Copy Markdown
Member

@r1tsuu r1tsuu commented Apr 2, 2026

This is an experiment for now

Framework Adapter Pattern + TanStack Start Adapter

Decouples Payload's admin panel from Next.js, making it renderable on any SSR framework. Ships @payloadcms/tanstack-start as the first non-Next adapter — a proof that the abstraction works.

Core Idea

packages/ui becomes framework-agnostic. Framework-specific concerns (routing, request handling, server functions, HMR) are pushed behind typed contracts in packages/payload. Each framework implements its own adapter package.

Two modes of rendering:

Next.js TanStack Start
Server rendering RSC (flight payloads) SSR + route loaders
Server functions 'use server' actions returning JSX createServerFn returning JSON
Request init next/headers @tanstack/react-start/server
HMR Next.js built-in Vite vite:beforeFullReload
Build tool Webpack / Turbopack Vite

Dependency Graph

graph TD
    payload["payload<br/><i>adapter contracts (types only)</i>"]
    ui["@payloadcms/ui<br/><i>framework-agnostic components + data fetchers</i>"]
    next["@payloadcms/next<br/><i>RSC · server actions · next/headers</i>"]
    tanstack["@payloadcms/tanstack-start<br/><i>SSR · createServerFn · @tanstack/react-start</i>"]
    app_next["Next.js App"]
    app_tanstack["TanStack Start App"]

    ui -- "peer" --> payload
    next -- "peer" --> payload
    next --> ui
    tanstack -- "peer" --> payload
    tanstack --> ui
    app_next --> next
    app_tanstack --> tanstack
Loading

What Changed

packages/payload — adapter contract types: RouterAdapterComponent, ServerAdapter, ComponentRenderer, DevReloadStrategy, ServerFunctionMode ('rsc' | 'data-only').

packages/ui — zero next/* imports. Shared server function registry, data-only handlers, RenderClientComponent, injectable RootProvider props (router, server function, reload strategy). View data fetchers extracted (getRootViewData, getListViewData, getDocumentViewData, etc.).

packages/next — refactored to use extracted data fetchers and re-exports from ui. Unchanged runtime behavior.

packages/tanstack-start — new package: router adapter, server adapter (initReq via @tanstack/react-start/server), handleServerFunctions (data-only mode), Vite HMR strategy, admin views, auth helpers (login/logout/refresh via createServerFn).

tanstack-app/ — working example app: TanStack Start + TanStack Router file routes, Vite config, import map. The build stack is purely Vite + @tanstack/react-start/plugin/vite (which uses H3 under the hood for the server layer).

@jmbockhorst
Copy link
Copy Markdown
Contributor

Hey, I am excited about this work! I had experimented with a similar approach a few months ago. I'm sure you might be aware that TanStack Start is planning to add RSC support soon (there is an draft blog post here, not sure how up to date it is with their current plans). I was wondering if this would make a difference in the approach to the framework adapter pattern, and if RSCs in Start make this significantly easier. If you haven't already, it might be good to have some communication with the TanStack team about this. Thanks for your work on this!

@r1tsuu
Copy link
Copy Markdown
Member Author

r1tsuu commented Apr 3, 2026

Hey, I am excited about this work! I had experimented with a similar approach a few months ago. I'm sure you might be aware that TanStack Start is planning to add RSC support soon (there is an draft blog post here, not sure how up to date it is with their current plans). I was wondering if this would make a difference in the approach to the framework adapter pattern, and if RSCs in Start make this significantly easier. If you haven't already, it might be good to have some communication with the TanStack team about this. Thanks for your work on this!

I'm aware, currently I want to see if it is possible so we don't rely on whether a framework supports RSC or not (while still maintaining 100% the same approach in Next.js). This is a bit more complex indeed but would allow room for any React framework (or even a custom one on top of Vite), not just Next/Tanstack. In this case - when Tanstack will add RSC support and if we want to use it - the only place we'd have modify is the adapter itself.

r1tsuu added 27 commits April 4, 2026 00:39
Add RouterAdapter, ServerAdapter, ComponentRenderer, and DevReloadStrategy
type contracts in packages/payload/src/admin/adapters.ts. These types form
the foundation for decoupling the admin panel from Next.js.
…imports

- Create RouterAdapter pattern: adapter is a React component that wraps
  children and populates RouterAdapterContext with framework-specific values
- Replace all 41 files importing from next/navigation.js, next/link.js,
  and next/dist/* with framework-agnostic RouterAdapter equivalents
- Replace AppRouterInstance type with RouterAdapterRouter from payload
- Replace ReadonlyRequestCookies with CookieStore from payload
- Replace LinkProps from next/link with LinkAdapterProps from payload
- Remove next from packages/ui peerDependencies
- Wire RouterAdapter component into RootProvider
- Export RouterAdapterContext from client entrypoint
- Create NextRouterAdapter component that calls Next.js hooks (useRouter,
  usePathname, useSearchParams, useParams) and populates the framework-agnostic
  RouterAdapterContext
- Wire NextRouterAdapter into RootLayout as the RouterAdapter prop
- Export NextRouterAdapter from @payloadcms/next/client
Move pure routing utilities from packages/next/src/views/Root/ to
packages/ui/src/utilities/routeResolution/:
- isPathMatchingRoute, getDocumentViewInfo, attachViewActions
- getCustomViewByKey, getCustomViewByRoute
- Shared ViewFromConfig type

Original files in packages/next re-export from @payloadcms/ui for
backward compatibility. getRouteData.ts updated to import from shared.
Move framework-agnostic presentational components from packages/next:
- MinimalTemplate (template + styles) → packages/ui/src/templates/Minimal/
- FormHeader (element + styles) → packages/ui/src/elements/FormHeader/

Original locations in packages/next now re-export for backward compat.
Create serverFunctionRegistry in packages/ui with framework-agnostic
handlers (form-state, table-state, copy-data-from-locale, etc.).
packages/next handleServerFunctions now spreads the shared registry
and adds RSC-specific handlers (render-document, render-list, etc.).
Create a client-only component renderer that treats all components as
client components and never passes serverProps. This is the alternative
to RenderServerComponent for frameworks without RSC support.
Add candidateDirectories parameter to resolveImportMapFilePath, allowing
framework adapters to specify their own directory patterns instead of
defaulting to Next.js app/(payload) convention. The default behavior
is unchanged for backward compatibility.
Remove import of Metadata from 'next' in packages/payload config types.
Define AdminMeta type that covers the commonly-used metadata subset
(title, description, openGraph, icons, twitter, keywords). MetaConfig
now intersects with AdminMeta instead of Next.js Metadata.

The Next.js adapter can map AdminMeta to Next.js Metadata as needed.
Replace @next/env dependency with dotenv + dotenv-expand for
framework-agnostic .env file loading. The new implementation supports
the same file priority convention (.env.local, .env.development, etc.)
without requiring Next.js packages.
Replace hardcoded Next.js webpack-hmr WebSocket with a DevReloadStrategy
interface. getPayload() now accepts an optional devReloadStrategy parameter.
The default fallback preserves the current Next.js HMR behavior. Framework
adapters can provide their own strategy (e.g., Vite HMR for TanStack Start).
Replace ReadonlyRequestCookies from next/dist with CookieStore from
the framework adapter contract in getRequestLanguage.ts.
packages/payload now has zero imports from next/ or @next/.
Introduce PAYLOAD_FRAMEWORK env variable to control which framework
adapter the dev server starts with. Extract Next.js-specific startup
into test/adapters/nextDevServer.ts. The dev.ts script dispatches to
the appropriate adapter based on PAYLOAD_FRAMEWORK (defaults to 'next').

This enables future adapters (e.g., tanstack-start) to add their own
dev server module and be selected via PAYLOAD_FRAMEWORK=tanstack-start.
Thread a `renderComponent: ComponentRenderer` parameter through the
entire form state and table state pipelines instead of hardcoding
`RenderServerComponent` imports.

Files modified:
- renderField.tsx: accepts renderComponent param instead of importing directly
- buildColumnState/index.tsx, renderCell.tsx: accept renderComponent param
- renderTable.tsx, renderFilters: accept renderComponent param
- buildFormState.ts, buildTableState.ts: pass RenderServerComponent as default
- iterateFields.ts, addFieldStatePromise.ts: thread renderComponent through
- fieldSchemasToFormState/index.tsx: accept and forward renderComponent
- renderFieldServerFn.ts: pass RenderServerComponent explicitly
- richtext-lexical rscEntry.tsx, buildInitialState.ts: thread renderComponent

Non-RSC adapters can now pass RenderClientComponent instead.
Move framework-agnostic Nav, DocumentHeader, and Logo elements from
packages/next to packages/ui. Replace next/navigation hooks with
RouterAdapter hooks. Replace @payloadcms/ui barrel imports with
direct source imports. Leave re-exports in packages/next for backward
compatibility.
Move the Default template (Wrapper, NavHamburger) from packages/next
to packages/ui. Replace @payloadcms/ui barrel imports with direct
source imports. Leave re-exports in packages/next.
Move the following view helpers from packages/next to packages/ui:
- Version/RenderFieldsToDiff (entire directory, 22+ files)
- Version/fetchVersions.ts, VersionPillLabel/
- Versions/buildColumns.tsx, cells/, types.ts
- Dashboard/ (entire tree, 18+ files)
- Document/ helpers (getDocumentData, getDocumentPermissions, etc.)
- List/ helpers (handleGroupBy, renderListViewSlots, etc.)

All @payloadcms/ui imports converted to relative paths.
Re-exports left in packages/next for backward compatibility.
Remove outdated TODO comments in PerPage and Autosave components
that referenced next/navigation abstraction - these components
already use RouterAdapter or don't need navigation hooks at all.
Move the following auth-related view components to packages/ui:
- Login/LoginForm, Login/LoginField, Login styles
- ForgotPassword (full view + ForgotPasswordForm)
- ResetPassword (full view + ResetPasswordForm)
- CreateFirstUser (full view + client component)
- Verify (full view + client component)
- Logout (full view + LogoutClient)
- Unauthorized (full view)

All next/navigation imports switched to RouterAdapter.
All @payloadcms/ui barrel imports converted to relative paths.
Re-exports left in packages/next for backward compatibility.
Login entry point stays in packages/next (uses redirect()).
Move APIView, APIViewClient, RenderJSON, LocaleSelector and styles
from packages/next to packages/ui. Switch useSearchParams from
next/navigation to RouterAdapter. Convert @payloadcms/ui barrel
imports to direct relative paths. Re-exports left in packages/next.
Move AccountClient, Settings, LanguageSelector, ToggleTheme,
and ResetPreferences from packages/next to packages/ui.
Account entry point stays in packages/next (uses notFound()).
All @payloadcms/ui barrel imports converted to relative paths.
Move DefaultVersionView, Restore, SelectComparison, SelectLocales,
VersionDrawer, VersionDrawerCreatedAtCell, SelectedLocalesContext,
SetStepNav, and VersionsViewClient to packages/ui.

All next/navigation imports switched to RouterAdapter.
All @payloadcms/ui barrel imports converted to relative paths.
Re-exports created in packages/next for backward compatibility.
Also fixes missing B3 re-exports for VersionPillLabel, Versions
buildColumns/cells, and RenderFieldsToDiff.
Move NotFoundClient and styles to packages/ui. The NotFoundPage
entry point stays in packages/next (uses initReq, Metadata).
Re-export created in packages/next for backward compatibility.
@r1tsuu r1tsuu force-pushed the experiment/framework-adapter-pattern branch from 7ef54a3 to b741714 Compare April 15, 2026 15:15
r1tsuu added 8 commits April 15, 2026 16:45
…on on tanstack

RSC component configs (Select/Radio CustomJSXLabel, ConditionalLogic
CustomServerField, Tabs UIField) are guarded with isRSCEnabled() so
they are excluded when PAYLOAD_FRAMEWORK_RSC_ENABLED=false.

Tests that depend on custom field components (Label, Error,
BeforeInput/AfterInput, custom Field, RowLabel, CollapsibleLabel,
BlockLabel) or on RSC-specific network request patterns
(assertNetworkRequests matching admin URLs) are annotated with
{ framework: 'rsc' } so they are skipped on non-RSC frameworks
like TanStack Start, where the server-side import map is
intentionally empty and rendered React elements are stripped
during serialization.
The PAYLOAD_FRAMEWORK_RSC_ENABLED env var is set inside the dev
server process, not in the Playwright runner process. Fall back
to inferring RSC support from PAYLOAD_FRAMEWORK so that
{ framework: 'rsc' } test annotations work correctly.
This test relies on custom RSC components (beforeInput/afterInput) that
cannot be resolved on TanStack due to the empty server-side import map.
Add waitForFormReady before tabbing through elements to ensure
TanStack's async hydration completes before focus indicator checks.
Two issues prevented document drawers from loading on TanStack Start:

1. toSerializable had no circular reference protection — the server
   function response contained circular DB schema references that
   caused JSON.stringify to throw. Added an ancestors WeakSet to
   detect and break cycles during serialization.

2. DrawerContent expected result.Document (a React node from RSC
   flight) which is unavailable on TanStack. The data-only handler
   returns documentViewData instead. DrawerContent now detects the
   data-only response and builds the document view client-side using
   DocumentInfoProvider + DefaultEditView.
- Radio: adjust expected a11y violation count (JSX label excluded on TanStack)
- Tabs: add waitForFormReady before switchTab to prevent hydration race
- Blocks: mark drawer-dependent block row label test as RSC-only
- JSON: mark custom AfterField component test as RSC-only
- Upload/UploadPoly: use custom test wrapper for framework annotations
- toggleDocDrawer: replace fixed waits with waitForFormReady
@r1tsuu
Copy link
Copy Markdown
Member Author

r1tsuu commented Apr 15, 2026

@tannerlinsley I removed all the references to vinxi! thank you for looking at this so much!

🧪 E2E Test Results — TanStack Start Adapter (Updated)

Run: 24477822883 · 2026-04-15
Commit: d297a1b

Suite-level pass rates

Framework ✅ Passing ❌ Failing ⏱ Cancelled Total Rate
TanStack Start 33 51 22 (timeout) 106 (†) 31.1%
Next.js 100 6 0 106 94.3%

Individual test-level pass rates (estimated)

Framework ✅ Passed ❌ Failed ⏭ Skipped Total ran Rate
TanStack Start ~540 ~410 ~55 ~950 ~57%
Next.js ~1,575 ~9 ~65 ~1,584 ~99.4%

Progress vs April 10 run

April 10 April 15 Δ
TanStack passing suites ~10 / 102 33 / 106 +22
TanStack suite pass rate ~10% 31% +20pp
TanStack test pass rate ~49% ~57% +8pp
Next.js auth Lexical regression ❌ failing fixed

r1tsuu and others added 18 commits April 17, 2026 16:25
… and Node.js bundle leakage

- RouterAdapter: strip origin from absolute URLs before router.navigate (fixes auth unlock-on-logout test)
- processMultipart: reject promise gracefully on busboy errors to prevent server crash on oversized uploads
- safeFetch/getDependencies/envPaths: lazy-init Node.js API calls to prevent leakage into browser bundles
- renderCell: import MissingEditorProp/APIError from payload/shared to break server-only import chain
- exports/shared: export APIError and MissingEditorProp for client-safe access
- Form: memoize fieldsReducer tuple to prevent spurious context re-renders (infinite loop with Lexical)
- RenderField: use stable primitive selector and resolve custom field components from client import map
- renderField: populate clientFieldComponentPath in field state for non-RSC adapters
- Auth view: guard auth fields with useFormInitializing to avoid premature enable during hydration
- usePreventLeave: clear cancelledURL before pushing to avoid double-navigation
- AdminView: resolve custom LivePreview component client-side from import map; add trash view rendering
- Root/getRouteData: add trash viewType handling; fetch list data with trash:true for trash views
- live-preview routes: add /live-preview and /live-preview/$ routes with server functions and LivePreviewPage component
- auth test: handle TanStack Start server function POST URL pattern for lockDoc assertion
- auth/CreateFirstUser: support non-RSC render mode for TanStack Start
- fields/Indexed e2e: use gotoAndWaitForForm to wait for form hydration before filling
- gitignore: ignore tanstack-app/media uploads directory
…tart

Strip serverProps from clientFieldComponentPath in renderField to avoid
RSC serialization errors when functions are passed via serverProps.
Add resolve.dedupe for react, react-dom, and @payloadcms/ui in the
tanstack-start vite config to prevent duplicate React context instances.
…iew, query-presets, duplicate dashboard

- Fix Lexical editor interactive UI (slash menu, toolbars, node types) not
  rendering in TanStack by passing clientFieldComponentProps (features,
  featureClientSchemaMap) from form state to the resolved richText client
  component. Skip richText fields in the generic import-map resolution path
  so they reach the dedicated case with full props.

- Fix duplicate .dashboard class in TanStack by removing templateClassName
  from DefaultTemplate (already applied via the view content).

- Fix live-preview custom component rendering by passing livePreviewComponent
  path from server config through SerializableDocumentViewData instead of
  reading it from the stripped client-side entity config.

- Use framework-agnostic @payloadcms/ui imports for query-presets, folders,
  and slug field components instead of @payloadcms/next/client, enabling
  these features in non-Next.js adapters.
…ontent serialization

Extract duplicated ListViewData→SerializableListViewData conversion into
a shared `toSerializableListViewData` utility, used by both the TanStack
Root page loader and the data-only render-list handler. This eliminates
~280 lines of workarounds from ListDrawer/DrawerContent.tsx (hardcoded
QueryPresetDrawerList, normalizeQueryPresetCollectionConfig, and inline
serialization) by having the server return fully serializable data with a
`collectionConfigOverride` for hidden collections like payload-query-presets.

Additional fixes included in this changeset:
- TanStack RouterAdapter: handle search-only URL replacements correctly
- DocumentDrawer: prevent redirect-on-create for drawer-based operations
- QueryPresetBar: call router.refresh() after preset title-only saves
- sanitizeQuery: preserve explicit empty overrides when a preset is active
- TableColumns: sync optimistic state with server-provided column state
- handleGroupBy: return per-group PaginatedDocs for client-side rendering
- buildListViewClientProps: support grouped table reconstruction
- transformColumnPreferences: harden JSON parsing for bare string values
Keep TanStack list reconstruction from dropping rich text and blocks field metadata, and stabilize router navigation handlers so route changes do not recreate the adapter context on every navigation.
Capture the remaining TanStack admin view, generated import map, and payload type updates so the branch stays aligned with the current serialized admin page shape.
Avoid react-dom/server in TanStack version diff field renderers so the tanstack-app production build stays on the client-safe side of import protection.
…in object

The SerializableVersionViewData type stored clientSchemaMap as a Map, but
TanStack Start's server→client data transfer serializes via JSON, which
turns Maps into `{}`. This caused VersionDiffViewContent to crash when
buildVersionFields iterated an empty Map, taking down all 40 tests in
the versions diff-view shard (3/3).

- Change SerializableVersionViewData.clientSchemaMap to Record<string, unknown>
- Convert Map→object on the server side (Root/index.tsx)
- Reconstruct Map on the client side in VersionDiffViewContent (AdminView.tsx)
  with a guard that still accepts Map instances for backward-compat

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
toSerializableListViewData emits a lossy collectionConfigOverride.fields
array (only name/type/label/admin-subset are kept), intended as a fallback
for callers with no client config access. buildListViewClientProps was
using those stripped fields to OVERRIDE the full baseClientCollectionConfig
fields, losing hasMany, required, localized, maxLength, etc.

During TanStack Start SSR, this crashed TextFieldComponent and other
field renderers that destructure those properties:
  TypeError: Cannot destructure property 'admin' of '{}' as it is undefined
    at TextFieldComponent (packages/ui/src/fields/Text/index.tsx:24)

Flip the priority so the base client config fields are used when present,
falling back to the serializable override only when no base exists.
Also harden the payloadProxy collection-config fallback with admin defaults
(components/hidden/useAsTitle) so downstream destructuring is safe.

Fixes regressions in locked-documents plus field suites (Date, Radio,
Tabs, Text) on TanStack.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
dataloader is listed in optimizeDeps.include as 'payload > dataloader',
so Vite pre-bundles it into .vite/deps. But the browser was still pulling
in the raw CJS /dataloader/index.js (via payload's internal loader) and
crashing with:

  SyntaxError: The requested module '.../dataloader/index.js' does not
  provide an export named 'default'

Add a load() hook transform — mirroring the existing deepmerge/pluralize
shims — that rewrites the raw CJS import to re-export the pre-bundled
default. Fixes the access-control TanStack suite (0/2 → functional).

Also drops the capturing group in the ssr-empty-style regex that was
failing the lint job (regexp/no-unused-capturing-group) and blocking
CI on the lint step.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The multi-tenant plugin registers GlobalViewRedirect in
admin.components.actions. The component is an async server helper that
calls next/headers and next/navigation — in Next.js RSC this works; in
TanStack Start it gets rendered as a React component and crashes:

  Error: %s is an async Client Component. Only Server Components can be
  async. <GlobalViewRedirect>

All 31 tests in the plugin-multi-tenant TanStack suite failed because
the admin view crashed before rendering. Filter out action paths that
target next-only server helpers in getRouteData so the admin view
renders cleanly. The tenant-redirect behavior itself is a separate
feature parity gap (the plugin needs a TanStack-native redirect path).

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
The admin-bar test navigates to /admin-bar and expects the
#payload-admin-bar element to render. The Next.js test setup has a
dedicated page at test/admin-bar/app/admin-bar/page.tsx — the shared
TanStack app had no equivalent, so the whole suite (1/1) failed.

Add a TanStack file route that mounts @payloadcms/admin-bar's
PayloadAdminBar component, deriving cmsURL from window.location.origin
so it talks to the same dev server that serves /admin and /api.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Run #4 shipped a dataloader load() hook, but the access-control TanStack
suite stayed 0/2 with the same
  SyntaxError: ...dataloader/index.js does not provide an export named 'default'
because nothing intercepted the original import.

Two issues were stacked:

1. In CI, payload is installed from packed tarballs into test/node_modules.
   When payload/dist/collections/dataloader.js does `import 'dataloader'`,
   Vite resolves it via test/node_modules/.pnpm/dataloader@2.2.3/... —
   a different physical path than the pre-bundled entry (which resolved
   via the workspace packages/payload → root node_modules/.pnpm/...).
   Vite's resolver sees the mismatch and serves the raw CJS instead of
   the pre-bundled ESM wrapper.

2. Vite then appends ?v=<hash> to the served URL, so the existing
   load() hook's `id.endsWith('/index.js')` check never fires against
   the queried path.

Mirror the deepmerge/pluralize/ajv pattern: add a transform() hook that
rewrites `from 'dataloader'` inside payload/dist/collections/dataloader.js
directly to /node_modules/.vite/deps/payload___dataloader.js. That is the
primary fix and bypasses any resolution ambiguity.

Also make the load() hook query-safe (strip ?v=<hash> before the
endsWith check) as a defensive fallback for any other importer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…otstrap

Run #4's Map→object fix for versionViewData.clientSchemaMap landed but
the versions diff-view shard stayed 0/40. The actual blocker was a
separate non-serializable value in the payload — RegExp literals from
rich-text feature markdownTransformers (e.g. UPLOAD_PLACEHOLDER_REGEX,
/!\[([^\]:]+):([^\]]+)\]\(\)/).

toSerializable previously preserved RegExp unchanged. seroval then
serializes the regex into the SSR bootstrap script, but with doubled
backslashes in source (emits `/!\\[.../` instead of `/!\[.../`). That
is syntactically invalid JS regex ("Unmatched ')'"), so the script
that populates `window.$_TSR` throws mid-stream. TanStack hydration
then fails with "Invariant: Expected to find bootstrap data on
window.$_TSR", the client re-renders the tree empty, and the diff
wrapper never reaches the DOM — hence every test in
`versions/e2e.spec.ts › Versions diff view` timed out waiting for
`.render-field-diffs`.

Next.js doesn't hit this because its server→client transport is JSON,
which silently drops RegExp. Strip RegExp here to match that behavior.
Rich-text transformer regexes are server-only (markdown import/export)
and aren't referenced by buildVersionFields or any client renderer,
so the loss is harmless.

Verified locally against test/versions/config.ts: before, the page
stuck at the Invariant error with an empty client tree; after, the
bootstrap parses, hydration completes, and .render-field-diffs
renders through SSR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@r1tsuu
Copy link
Copy Markdown
Member Author

r1tsuu commented Apr 21, 2026

🧪 E2E Test Results — TanStack Start Adapter (Updated — April 21)

Run: 24743389080 · 2026-04-21
Commit: f5870b3

Suite-level pass rates

Framework ✅ Passing ❌ Failing ⏱ Cancelled Total Rate
TanStack Start 42 48 16 106 39.6%
Next.js 99 7 0 106 93.4%

Individual test-level pass rates

Framework ✅ Passed ❌ Failed ⏭ Skipped 🔁 Flaky Total ran Rate
TanStack Start 817 318 96 8 1,231 72.0%
Next.js 1,614 11 61 29 1,686 99.3%

Progress vs April 15 run

April 15 April 21 (this) Δ
TanStack passing suites 33 / 106 42 / 106 +9
TanStack suite pass rate 31.1% 39.6% +8.5pp
TanStack tests passed ~540 817 +277
TanStack tests ran ~950 1,231 +281
TanStack test pass rate ~57% 72.0% +15pp
Next.js suite pass rate 94.3% 93.4% −0.9pp
Next.js test pass rate ~99.4% 99.3% −0.1pp

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.

3 participants