feat(workbench): application extension API#1143
Conversation
|
Claude finished @gu-stav's task in 4m 43s —— View job Review
Reviewed the 18 commits on top of Already fixed — Cursor Bugbot's
|
📦 Bundle Stats —
|
| Metric | Value | vs feat/workbench (c492c9f) |
|---|---|---|
| Internal (raw) | 2.4 KB | +269 B, +12.3% |
| Internal (gzip) | 922 B | +123 B, +15.4% |
| Bundled (raw) | 11.08 MB | +115.7 KB, +1.0% |
| Bundled (gzip) | 2.08 MB | +21.0 KB, +1.0% |
| Import time | 837ms | +15ms, +1.8% |
bin:sanity
| Metric | Value | vs feat/workbench (c492c9f) |
|---|---|---|
| Internal (raw) | 1023 B | - |
| Internal (gzip) | 486 B | - |
| Bundled (raw) | 9.84 MB | - |
| Bundled (gzip) | 1.77 MB | - |
| Import time | 1.97s | +26ms, +1.3% |
🗺️ 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 feat/workbench (c492c9f7)
| Metric | Value | vs feat/workbench (c492c9f) |
|---|---|---|
| Internal (raw) | 102.3 KB | +3.6 KB, +3.6% |
| Internal (gzip) | 24.7 KB | +1.4 KB, +5.8% |
| Bundled (raw) | 21.75 MB | +114.4 KB, +0.5% |
| Bundled (gzip) | 3.45 MB | +20.3 KB, +0.6% |
| Import time | 801ms | +3ms, +0.4% |
🗺️ 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 feat/workbench (c492c9f7)
| Metric | Value | vs feat/workbench (c492c9f) |
|---|---|---|
| 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.
Calling `unstable_defineApp` opts a project into workbench behavior. Detect its brand right after the config module loads and route the branded `app` around the legacy `app` object schema, which would otherwise strip the helper's identity fields. Configs that don't call it are untouched.
Coverage Delta
Comparing 35 changed files against main @ Overall Coverage
|
9ac8f28 to
f60f27d
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 0ecd70b. Configure here.
| @@ -0,0 +1,34 @@ | |||
| import {cliConfigSchema} from './schemas' | |||
There was a problem hiding this comment.
Missing .js extension in ESM import path
High Severity
The value import from './schemas' lacks the .js extension required for Node.js ESM resolution. Every other file importing this module (e.g., getCliConfig.ts, getCliConfigSync.ts) uses './schemas.js'. The package is "type": "module" and SWC does not rewrite import extensions (no rewriteRelativeImportExtensions in .swcrc), so the compiled output will retain './schemas' and fail at runtime with ERR_MODULE_NOT_FOUND when the workbench app path is taken.
Reviewed by Cursor Bugbot for commit 0ecd70b. Configure here.
08b2890 to
0ecd70b
Compare
Surfaces the workbench app extension API from the CLI's public entry so `sanity/cli` can pass it through to app authors. Pins @sanity/federation to 0.1.0-alpha.9 (the release adding the root export); resolves once that is published.
Point the federation dependency at the pkg.pr.new preview built from workbench#226 so the `unstable_defineApp` re-export resolves now, instead of pinning an unpublished version. Drops the eslint-disable that masked the previously-unresolvable import.
cli-core is NodeNext ESM — the compiled workbenchApp.js needs explicit .js specifiers or Node can't resolve ./schemas and ./types/cliConfig at runtime (MODULE_NOT_FOUND during build). Was masked while install failed.
Preview this PR with pkg.pr.newRun the Sanity CLInpx https://pkg.pr.new/sanity-io/cli/@sanity/cli@c496368 <command>...Or upgrade project dependencies📦
|
Point @sanity/federation at the rebuilt pkg.pr.new preview whose root export no longer leaks src under the development condition (fixes the Node worker type-stripping crash). Allow `unstable_defineApp` in the v5↔v6 exports parity test as an intentional new value export.
A branded config implies federation — force `federation: {enabled: true}`
when `unstable_defineApp` is used, so the workbench dev server and
federation registration start without a separate flag. Adds unit tests
for the brand-branch (detection, auto-enable, branded-app preservation).
…ineApp
Calling `unstable_defineApp` is now the sole workbench opt-in — the
`federation: {enabled}` config flag is removed entirely. Dev/build/config
gating keys off the `isWorkbenchApp` identity (imported from
`@sanity/federation`, not re-declared). `applicationType` is resolved at
config load (explicit, else inferred from a `sanity.config.*` → studio)
so studio-vs-app classification is settled once. Tests updated to opt in
via a branded app.
`sanity init` no longer prompts for or writes any `federation` config; new
projects are plain studios/apps. Authors opt into workbench by adding
`unstable_defineApp` to `sanity.cli.ts`. Removes the federation prompt,
the `federation` flag/variable threaded through the init flow, and the
`federation: { enabled }` block from the generated config templates.
Drop the pkg.pr.new preview override now that the federation changes (unstable_defineApp, isWorkbenchApp, root export without the src-leaking development condition) are published.
…xture The federation config flag is gone; `unstable_defineApp` is the sole workbench opt-in. Remove the leftover `--federation` CLI flag and the init tests that still passed/asserted it. Convert the federated-studio fixture to opt in via `unstable_defineApp` (its `sanity.config.ts` resolves it to a studio workbench build) and add `@sanity/federation` to the fixture deps so the import resolves. Reword the changeset to be user-facing.
`@sanity/cli-test` copies fixtures from the repo root via `copy:fixtures`, but the generic `build` task only keys on files inside the package. Editing a repo-root fixture left the cache key unchanged, so turbo replayed a stale bundled fixture — the federated-studio test then read the old config and failed. Key cli-test's build on `$TURBO_ROOT$/fixtures` and the workspace catalog so fixture edits invalidate it.
The federation flag is gone, so the e2e init flows that still passed `--no-federation` errored out with "Nonexistent flag" before reaching their prompts.
`parseWorkbenchCliConfig` no longer mutates the caller's `app` object — it resolves `applicationType` onto a clone (copying descriptors so the non-enumerable brand survives), so re-parsing the same app for a different directory can't inherit a stale inference. An explicit `applicationType` is now validated against the known set and fails fast, since `unstable_defineApp` is a pure identity wrapper that doesn't validate. Cover the workbench branch in both the async and sync loaders.
Forcing `@module-federation/vite`'s plugins to run inside a vitest worker (via MFE_VITE_NO_TEST_ENV_CHECK) crashes the worker on Windows — esbuild's service pipe dies with "Unexpected end of JSON input [plugin onEnd]". Real `sanity build` runs as its own process and the federation-artifact shape is platform-independent, so Linux coverage is enough.
cb95e6a to
c496368
Compare


Depends on
unstable_defineAppin@sanity/federation, consumed here via the released0.1.0-alpha.9Required by: sanity-io/sanity#12911
Why
Workbench application extension API on the CLI side:
unstable_defineAppis the sole workbench opt-in. Thefederation: { enabled }config flag is removed entirely — gating (workbench dev server, federation registration, the federation Vite build plugin) keys off theisWorkbenchAppidentity (imported from@sanity/federation, not re-declared). The brandedappbypasses the legacyappobject schema so the brand survives config parsing.applicationTyperesolved at config load — explicit wins, else inferred from asanity.config.*(⇒ studio) else coreApp.determineIsAppreads it, so a studio that opts in viaunstable_defineAppisn't misclassified as an SDK app.@sanity/clire-exportsunstable_defineApp(+DefineAppInput) sosanity/clican surface it to app authors.sanity initno longer prompts for / scaffolds federation — workbench is opt-in.@sanity/federationis the released0.1.0-alpha.9(no preview override).All CLI tests pass (cli-core config + dev/build suites updated to opt in via a branded
app).