Skip to content

feat(workbench): setup#907

Draft
gu-stav wants to merge 47 commits into
mainfrom
feat/workbench
Draft

feat(workbench): setup#907
gu-stav wants to merge 47 commits into
mainfrom
feat/workbench

Conversation

@gu-stav
Copy link
Copy Markdown
Member

@gu-stav gu-stav commented Apr 9, 2026

Description

Note

Please ignore this PR. It's just open to have visibility into the state of the feature branch.

What to review

Testing

Notes for release

@claude
Copy link
Copy Markdown

claude Bot commented Apr 9, 2026

Claude finished @gu-stav's task in 6m 1s —— View job


Claude PR Review

  • Read PR diff vs origin/main
  • Review changeset(s)
  • Review dev server / workbench code
  • Review build / manifest changes
  • Review cli-core schema/config changes
  • Review test coverage
  • Post review findings

A few issues worth fixing before this graduates from a tracking-only feature branch.

1. Typegen plugin is registered twice in dev mode (bug)

In packages/@sanity/cli/src/actions/build/getViteConfig.ts:164-172, the typegen plugin is added to sharedPlugins when typegen?.enabled. In packages/@sanity/cli/src/server/devServer.ts:75-86, the typegen plugin is also passed in via additionalPlugins. Since additionalPlugins is spread into the non-federation branch of plugins (getViteConfig.ts:235), running sanity dev (non-federated) with typegen.enabled: true registers sanityTypegenPlugin twice. The Vite plugin pipeline will then execute every hook twice — duplicate buildStart, watcher registrations, and writes to sanity.types.ts.

The existing tests use .find() and toHaveBeenCalledWith(...), which mask the duplicate; assert toHaveBeenCalledTimes(1) to make this visible.

Fix: stop passing additionalPlugins from devServer.ts for typegen (now that the plugin is centralized in getViteConfig), or pull it out of sharedPlugins and keep the original behaviour.

2. Workbench requires authentication when only api.projectId is configured

packages/@sanity/cli/src/actions/dev/startWorkbenchDevServer.ts:226-242 falls back to getProjectById(cliConfig.api.projectId) to resolve the organization id. That call goes through getProjectCliClient({requireUser: true}) (services/projects.ts:58), so:

  • Running sanity dev while logged out crashes with an auth error rather than the helpful message at line 239 telling the user to set app.organizationId. Catch the error (or check auth first) and surface a clearer message including the option to sanity login.
  • It also adds a synchronous API round-trip to every workbench-enabled dev startup. Consider caching the resolved organizationId (e.g. alongside the workbench lock or in ~/.sanity), or only resolving lazily when the workbench actually needs it.

3. appPort may be undefined and silently drop the registration

packages/@sanity/cli/src/actions/dev/startFederationRegistration.ts:37-47:

const addr = server.httpServer?.address()
const appPort = typeof addr === 'object' && addr ? addr.port : server.config.server.port

server.config.server.port is optional in Vite's types, so appPort can be undefined. registerDevServer writes that to disk; getRegisteredServers then fails schema validation (port: z.number()) and prunes the entry — silently. Either narrow the type with a runtime check + warning, or require httpServer.address() to have returned by this point.

4. Lock host can store binding-only addresses

When a user runs sanity dev --host 0.0.0.0, the workbench lock stores host: '0.0.0.0' (startWorkbenchDevServer.ts:77). Other studio processes reading the lock then build URLs like http://0.0.0.0:3333 for both the displayed dev URL and reactRefreshHost. The displayed URL is unfriendly; 0.0.0.0 works in browsers on most platforms but not all. Consider normalizing 0.0.0.0 / :: to localhost for the URL/refresh host (keep the bind address in the lock for actual listening).

5. Stale optimizeDeps exclusion

startWorkbenchDevServer.ts:166 excludes ['sanity', '@sanity/workbench'] from dep pre-bundling. The workbench is imported as sanity/workbench (subpath of sanity), and the externals changeset only mentions sanity and @sanity/workbench. If @sanity/workbench is not actually consumed at runtime, drop it from the list to avoid confusion when readers grep for the package.

6. Internal codename leaks into source comments

packages/@sanity/cli/src/actions/manifest/extractCoreAppManifest.ts:17: Brett sanitizes SVGs so it's skipped here. and packages/@sanity/cli/src/actions/deploy/deployApp.ts:112: // Sync app title from manifest when it has changed (Brett user-applications). "Brett" is an internal codename that won't mean anything to readers outside the team. Rewrite as the concrete reason (e.g. "the user-applications service sanitizes the SVG server-side").

7. Changeset summaries

  • .changeset/fancy-cycles-strive.md: externalize sanity and @sanity/workbench — vague and implementation-focused for a @sanity/cli patch. Reword to a user-facing effect, e.g. Federated builds now treat 'sanity' and '@sanity/workbench' as external dependencies so they are no longer bundled into each remote.
  • .changeset/bump-federation-alpha-6.md: pure dependency bump — fine as-is but consider chore framing if not user-facing.
  • The remaining workbench changesets read well.

8. Minor / nits

  • packages/@sanity/cli/src/actions/dev/devAction.ts:36: comment says Use workbenchPort + 1 when workbench claims the configured port — it actually uses the passed workbenchPort (which may itself have been bumped by Vite's strictPort: false). Document that desiredAppPort is "preferred" rather than "next available" since Vite may bump again.
  • packages/@sanity/cli/src/actions/dev/writeWorkbenchRuntime.ts:13: the template fallback 'undefined' produces {organizationId: undefined} in the generated JS, but the caller throws when org id is missing so that branch is unreachable. Either remove the fallback or remove the throw — keeping both is dead code.
  • packages/@sanity/cli/src/actions/dev/startFederationRegistration.ts:44: cliConfig?.api?.projectIdcliConfig is non-optional in the interface, drop the ?.
    · branch: feat/workbench

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

📦 Bundle Stats — @sanity/cli

Compared against main (dafed5c3)

@sanity/cli

Metric Value vs main (dafed5c)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 10.97 MB +70 B, +0.0%
Bundled (gzip) 2.06 MB +14 B, +0.0%
Import time 820ms -13ms, -1.6%

bin:sanity

Metric Value vs main (dafed5c)
Internal (raw) 1023 B -
Internal (gzip) 486 B -
Bundled (raw) 9.84 MB -
Bundled (gzip) 1.77 MB -
Import time 2.23s +293ms, +15.1% ⚠️

🗺️ 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.

📦 Bundle Stats — @sanity/cli-core

Compared against main (dafed5c3)

Metric Value vs main (dafed5c)
Internal (raw) 98.8 KB +2.5 KB, +2.6%
Internal (gzip) 23.3 KB +667 B, +2.9%
Bundled (raw) 21.64 MB +2.2 KB, +0.0%
Bundled (gzip) 3.43 MB +682 B, +0.0%
Import time 780ms -1ms, -0.1%

🗺️ 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.

📦 Bundle Stats — create-sanity

Compared against main (dafed5c3)

Metric Value vs main (dafed5c)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Coverage Delta

File Statements
packages/@sanity/cli-core/src/config/cli/getCliConfig.ts 100.0% (±0%)
packages/@sanity/cli-core/src/config/cli/schemas.ts 100.0% (±0%)
packages/@sanity/cli-core/src/services/cliUserConfig.ts 100.0% (±0%)
packages/@sanity/cli-core/src/util/getSanityConfigDir.ts 66.7% (new)
packages/@sanity/cli/src/actions/build/buildApp.ts 95.3% (±0%)
packages/@sanity/cli/src/actions/build/buildStaticFiles.ts 97.6% (+ 1.0%)
packages/@sanity/cli/src/actions/build/buildStudio.ts 96.7% (±0%)
packages/@sanity/cli/src/actions/build/getViteConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/build/writeSanityRuntime.ts 96.2% (+ 0.7%)
packages/@sanity/cli/src/actions/deploy/deployApp.ts 80.5% (±0%)
packages/@sanity/cli/src/actions/deploy/deployStudioSchemasAndManifests.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/dev/devAction.ts 96.8% (- 3.2%)
packages/@sanity/cli/src/actions/dev/devServerRegistry.ts 93.8% (new)
packages/@sanity/cli/src/actions/dev/extractDevServerManifest.ts 20.0% (new)
packages/@sanity/cli/src/actions/dev/getDevServerConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/dev/startAppDevServer.ts 100.0% (+ 13.0%)
packages/@sanity/cli/src/actions/dev/startDevManifestWatcher.ts 90.9% (new)
packages/@sanity/cli/src/actions/dev/startFederationRegistration.ts 100.0% (new)
packages/@sanity/cli/src/actions/dev/startStudioDevServer.ts 100.0% (+ 5.0%)
packages/@sanity/cli/src/actions/dev/startWorkbenchDevServer.ts 98.7% (new)
packages/@sanity/cli/src/actions/dev/writeWorkbenchRuntime.ts 100.0% (new)
packages/@sanity/cli/src/actions/init/bootstrapLocalTemplate.ts 89.7% (±0%)
packages/@sanity/cli/src/actions/init/bootstrapTemplate.ts 0.0% (±0%)
packages/@sanity/cli/src/actions/init/createAppCliConfig.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/init/createCliConfig.ts 100.0% (+ 50.0%)
packages/@sanity/cli/src/actions/init/createStudioConfig.ts 83.3% (±0%)
packages/@sanity/cli/src/actions/init/initAction.ts 97.2% (+ 0.1%)
packages/@sanity/cli/src/actions/init/initApp.ts 96.0% (±0%)
packages/@sanity/cli/src/actions/init/initStudio.ts 87.0% (±0%)
packages/@sanity/cli/src/actions/init/scaffoldTemplate.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/init/types.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/extractCoreAppManifest.ts 93.1% (new)
packages/@sanity/cli/src/actions/manifest/extractManifest.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/types.ts 100.0% (±0%)
packages/@sanity/cli/src/actions/manifest/writeManifestFile.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/dev.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/init.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/manifest/extract.ts 100.0% (±0%)
packages/@sanity/cli/src/commands/schemas/deploy.ts 95.0% (±0%)
packages/@sanity/cli/src/prompts/init/federation.ts 100.0% (new)
packages/@sanity/cli/src/server/devServer.ts 94.1% (±0%)
packages/@sanity/cli/src/util/resolveReactStrictMode.ts 100.0% (new)

Comparing 42 changed files against main @ dafed5c321960164da255ff024dda49667cc5ec3

Overall Coverage

Metric Coverage
Statements 84.6% (+ 0.3%)
Branches 74.7% (+ 0.4%)
Functions 84.5% (+ 0.3%)
Lines 85.1% (+ 0.4%)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 9, 2026

Preview this PR with pkg.pr.new

Run the Sanity CLI

npx https://pkg.pr.new/sanity-io/cli/@sanity/cli@e6795c3 <command>

...Or upgrade project dependencies

📦 @sanity/cli
pnpm install https://pkg.pr.new/@sanity/cli@e6795c3
📦 @sanity/cli-build
pnpm install https://pkg.pr.new/@sanity/cli-build@e6795c3
📦 @sanity/cli-core
pnpm install https://pkg.pr.new/@sanity/cli-core@e6795c3
📦 @sanity/cli-test
pnpm install https://pkg.pr.new/@sanity/cli-test@e6795c3
📦 @sanity/eslint-config-cli
pnpm install https://pkg.pr.new/@sanity/eslint-config-cli@e6795c3

View Commit (e6795c3)

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 9, 2026

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

View full report

@gu-stav
Copy link
Copy Markdown
Member Author

gu-stav commented Apr 10, 2026

The axios security warning will be resolved by module-federation/core#4644 I think

@gu-stav
Copy link
Copy Markdown
Member Author

gu-stav commented Apr 13, 2026

Rebased onto main

@joshuaellis joshuaellis force-pushed the feat/workbench branch 3 times, most recently from ffc8344 to f2b1294 Compare April 17, 2026 12:39
@gu-stav gu-stav force-pushed the feat/workbench branch 2 times, most recently from 5e50a1d to 1e221bd Compare April 23, 2026 12:19
@gu-stav gu-stav force-pushed the feat/workbench branch 2 times, most recently from 16e7dfe to 319ef83 Compare May 5, 2026 09:17
@joshuaellis joshuaellis force-pushed the feat/workbench branch 2 times, most recently from 905b492 to cf3839a Compare May 7, 2026 11:47
gu-stav and others added 9 commits May 21, 2026 15:44
Co-authored-by: Josh <joshua.ellis18@gmail.com>
fix(workbench): allow for a dynamic port (#830)
)

* feat(dev): forward CLI config organization id to workbench runtime

* chore: update auto-generated changeset for PR #905

---------

Co-authored-by: ecospark[bot] <ecospark[bot]@users.noreply.github.com>
Co-authored-by: Gustav Hansen <gustav.hansen@sanity.io>
…913)

Pass `reactRefreshHost` to `@vitejs/plugin-react` so federated Studio
modules connect their react-refresh preamble to the workbench host,
enabling component-level HMR across the module federation boundary.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
joshuaellis and others added 23 commits May 21, 2026 15:50
* feat(init): add promt for federation

* chore: update auto-generated changeset for PR #988

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix: types

* chore: update auto-generated changeset for PR #989

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(workbench): extend local application payloads

* fix: types

* fix: pr feedback

* chore: improve tests

* fix: concise

* fix: pr feedback
* feat(init): use `workbench` dist-tag for `sanity` package

* chore: update auto-generated changeset for PR #992

* test: add unit tests

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(init): do not resolve dist tags

* chore: update auto-generated changeset for PR #1000

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* feat(dev): extract studio manifest and pass it for local applications

* chore: update auto-generated changeset for PR #997

* fix: rework to use manifests for both

* chore: update auto-generated changeset for PR #997

* fix: cleanup

* chore: share cache dir constant

* feat: extract manifest in background

* fix: path resolution on windows

* fix: pr feedback

* fix: pr feedback

---------

Co-Authored-By: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(workbench): remove warmup for dependencies

* chore: update auto-generated changeset for PR #1047

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Adds `--federation` / `--no-federation` flag to `sanity init` and wires
it through `initAction` so the value is passed to `initApp` / `initStudio`
alongside the federation prompt added in #988.

The flag plumbing was previously part of the rebase merge that resolved
feat/workbench against main, and was dropped in the latest rebase.
* fix(workbench): prune stale lock files

* fix: add back

* chore: update auto-generated changeset for PR #1057

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…OTE_URL` (#1066)

* fix(workbench): throw error in invalid `SANITY_INTERNAL_WORKBENCH_REMOTE_URL`

* chore: update auto-generated changeset for PR #1066

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
* fix(workbench): detect PID reuse on Windows via PowerShell

* chore: update auto-generated changeset for PR #1067

* chore(changeset): rewrite changeset summary as user-facing description

Co-authored-by: Rune Botten <runeb@users.noreply.github.com>

* chore: update auto-generated changeset for PR #1067

---------

Co-authored-by: squiggler-app[bot] <265501495+squiggler-app[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Rune Botten <runeb@users.noreply.github.com>
After rebasing onto main:
- buildStudio.ts: plumb `federation` through `InternalBuildOptions` so the
  `!options.federation?.enabled` guard and the `federation` prop passed to
  `buildStaticFiles` resolve. Mirrors the same plumbing already done in
  buildApp.ts during the rebase.
- buildStaticFiles.test.ts: `copyDir`/`writeFavicons` moved to `@sanity/cli-build/_internal`
  in main's #1062 refactor — point the mock at the new path.
- startStudioDevServer.test.ts: `checkStudioDependencyVersions` moved to
  `@sanity/cli-build/_internal`; `getLocalPackageVersion` moved to
  `@sanity/cli-core` (main's #1062 and #1053). Update mocks accordingly.
Fix stale plugin mock paths and align typegen test assertions with the
app dev server output.
gu-stav and others added 2 commits May 27, 2026 16:55
# Conflicts:
#	packages/@sanity/cli/src/actions/manifest/extractManifest.ts
#	packages/@sanity/cli/src/commands/manifest/extract.ts
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 27, 2026

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​sanity/​federation@​0.1.0-alpha.7761008094100
Addednpm/​turbo@​2.9.6100988597100

View full report

gu-stav and others added 2 commits May 27, 2026 17:16
* refactor(workbench): drop reactRefreshHost plumbing now that federation owns HMR

With `dev.remoteHmr: true` landing in `@module-federation/vite` (via workbench PR
sanity-io/workbench#208), the federation plugin wires Fast Refresh through the host
automatically. The workbench port no longer needs to leak from `devAction` down through
`start{Studio,App}DevServer` → `startDevServer` → `getViteConfig` into `@vitejs/plugin-react`.

Removing that field exposed `workbenchAvailable` as the last piece of workbench-derived state
on `DevActionOptions`, used only to suppress a URL log suffix in the leaf starters. Hoisted
that output into `devAction`, which already owns the "Workbench dev server started at …"
line — all dev-server output formatting now lives in one place.

Net: the build pipeline has zero awareness of whether a workbench host exists.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore: remove .__mf__temp gitignore entry

The federation pipeline no longer creates `.__mf__temp` directories, so
the gitignore entry (and the test asserting it gets scaffolded) are
obsolete. Removes the entry from the template gitignore, the federated
fixture, and the bootstrap test, and drops the changeset that introduced
the entry since the change is no longer shipping.

* fix(workbench): restore dev-server startup log lines

The reactRefreshHost cleanup also dropped the "App dev server started on port X"
and "...ms and running at http://..." lines from the leaf starters and replaced
them with a single "${label} dev server started at ${appUrl}" line in devAction.
That changed user-visible text and broke dev.test.ts on every shard.

Restore the original log lines (and the workbenchAvailable plumbing they depend
on for the no-workbench branch). The reactRefreshHost removal stays untouched —
build pipeline still has zero awareness of workbench hosts.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(deps): bump @sanity/federation to 0.1.0-alpha.8

* test(cli): opt out of @module-federation/vite test-env guard in federated build test

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.

2 participants