Skip to content

fix: reference root block objects by name at every nested position (v5 backport)#2995

Draft
christianhg wants to merge 1 commit into
bridge-v5-ltsfrom
fix-bridge-v5-definition-tree-size
Draft

fix: reference root block objects by name at every nested position (v5 backport)#2995
christianhg wants to merge 1 commit into
bridge-v5-ltsfrom
fix-bridge-v5-definition-tree-size

Conversation

@christianhg

Copy link
Copy Markdown
Member

Backport of #2993 to the last Sanity-v5-compatible bridge line. Bridge 3.1.2 and later depend on @sanity/types v6, so v5 studios top out at 3.1.1, which carries the factorial definition-tree blow-up: conversion is fast (the memoized walk builds a shared DAG), but the emitted definition inlines each type's expansion at every position not covered by an ancestor cycle, and everything downstream walks it as a tree. Wide mutually recursive schemas freeze and OOM on document open, the same failure #2976 reports against the 3.2.x line.

Same fix as main: members that are root block objects emit a bare {type: name} reference at every position, keyed by canonical instance so same-named inline declarations keep their own shape. The era's own test suite passes unchanged, and the mutually-embedding benchmark gains the output-size bound, red on the unfixed converter (the size assertion fails), green with the fix.

Targets the bridge-v5-lts branch (cut from the @portabletext/sanity-bridge@3.1.1 tag), not main. Publishing is a hand-publish from this branch after merge, changesets doesn't manage the old line. The version number needs a decision: 3.1.7 slots after the Sanity-6-only 3.1.2-3.1.6 and risks tilde-range floats mixing dependency majors, while an exact-pinned prerelease-style version (e.g. 3.1.2-v5-lts.1) can never be range-matched and requires Studio v5 to pin it exactly, which is also the safer shape for Studio v5's own range (its current ^3.1.0 floats into the Sanity-6-only versions today).

Backport of the same fix on `main` to the last Sanity-v5-compatible
bridge line (3.1.1; 3.1.2 and later depend on `@sanity/types` v6).

The combinatorial-blow-up fix made conversion time linear by building
shared `OfDefinition` objects, a DAG. But each named type's single
expansion still inlined the expansions of every type not on its
first-visit ancestor path, so the emitted definition was factorial in
the number of mutually-embedding types AS A TREE, which is the shape
every consumer walks: schema compilation, React reconciliation, any
clone or serialization. Studios with wide mutually recursive schemas
froze and ran out of memory on document open.

`sanityOfMemberToOfDefinition` now emits a bare `{type: name}`
reference whenever the member is one of the root block-object
instances, at every position, not just on ancestor cycles. The root
`blockObjects` entry materializes the fields exactly once. The check
is keyed by canonical instance rather than name, so an inline
declaration that merely shares a name with a root type keeps its own
inline shape. The mutually-embedding benchmark now also bounds
`JSON.stringify(schema).length`, the assertion whose absence let the
factorial output ship green.
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
portable-text-example-basic Ready Ready Preview, Comment Jul 21, 2026 12:02pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
portable-text-playground Skipped Skipped Jul 21, 2026 12:02pm

Request Review

@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: bf47b30

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @portabletext/editor

Compared against bridge-v5-lts (24e2efd5)

@portabletext/editor

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 775.3 KB -
Internal (gzip) 148.1 KB -
Bundled (raw) 1.38 MB -
Bundled (gzip) 310.0 KB -
Import time 73ms -2ms, -3.1%

@portabletext/editor/behaviors

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 467 B -
Internal (gzip) 207 B -
Bundled (raw) 424 B -
Bundled (gzip) 171 B -
Import time 1ms -0ms, -1.2%

@portabletext/editor/plugins

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 2.7 KB -
Internal (gzip) 894 B -
Bundled (raw) 2.5 KB -
Bundled (gzip) 827 B -
Import time 5ms -0ms, -1.0%

@portabletext/editor/selectors

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 79.3 KB -
Internal (gzip) 14.5 KB -
Bundled (raw) 74.8 KB -
Bundled (gzip) 13.4 KB -
Import time 6ms +0ms, +0.3%

@portabletext/editor/traversal

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 25.4 KB -
Internal (gzip) 5.0 KB -
Bundled (raw) 25.4 KB -
Bundled (gzip) 5.0 KB -
Import time 4ms +0ms, +1.1%

@portabletext/editor/utils

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 28.7 KB -
Internal (gzip) 6.0 KB -
Bundled (raw) 26.7 KB -
Bundled (gzip) 5.7 KB -
Import time 4ms +0ms, +0.1%

🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @portabletext/markdown

Compared against bridge-v5-lts (24e2efd5)

Metric Value vs bridge-v5-lts (24e2efd)
Internal (raw) 53.0 KB -
Internal (gzip) 9.6 KB -
Bundled (raw) 347.6 KB -
Bundled (gzip) 96.0 KB -
Import time 29ms -2ms, -5.5%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

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.

1 participant