Skip to content

Monorepo restructuring: standalone build, aligned dependencies, dist + source exports - #9

Merged
mkotelnikov merged 25 commits into
mainfrom
migration/monorepo-restructuring
Oct 4, 2026
Merged

mkotelnikov merged 25 commits into
mainfrom
migration/monorepo-restructuring

Conversation

@mkotelnikov

Copy link
Copy Markdown
Contributor

Part of the statewalker monorepo restructuring (layer 5). The repository builds, typechecks and tests on its own, with every @statewalker dependency taken from npm.

What changes

  • The kernel packages (workspace, shell, render, settings, …) moved to statewalker-kernel; this repo keeps the React views and consumes the kernel from npm.
  • Internal references are workspace:^; external dependencies go through the pnpm catalog and are aligned on their latest versions across all statewalker repos (TypeScript 7, vitest 5, dockview 8).
  • Public packages publish built dist with a source export condition; ai-config.view.react gets the index it exported.
  • Typecheck is clean in every package (it was not on main); CI builds before it typechecks and runs on Node 24.
  • biome.json is a root config again, so lint:check runs standalone.

Commits

  • refactor(shell.core): depend on dockview-core instead of dockview-react
  • fix: typecheck ai-agent.core, backbone.core and backbone.node
  • build: webrun-site-builder ^0.2.0, webrun-site-host ^0.2.1
  • fix(shell.core): dockview-core ^6.0.3, the range dockview-react resolves
  • build: pin jsdom to 30.0.1
  • refactor: move the kernel and ai packages out of the workbench
  • build: kernel and ai 0.2 through the catalog, webrun-files 0.10, the convention
  • chore: lockfile with kernel and ai 0.2 from the registry
  • build: internal references as workspace:^
  • build: external dependencies at the latest version of their current line
  • build: vitest 5 and jsdom 30.1 (dependency wave 1)
  • build: dependency wave 3 (tooling and types majors)
  • build: kernel and statewalker-ai ^0.3.0, dockview-react 8 (dependency wave 5)
  • chore: lockfile (kernel / ai 0.3, dockview 8)
  • build: kernel and statewalker-ai ^0.4.0, json-render 0.21 (wave 7)
  • build: kernel 0.5 and statewalker-ai 0.5 (local release round)
  • chore(release): statewalker-workbench, local release round
  • build: publish dist with a source condition (decision 1)
  • chore(release): statewalker-workbench, local release round
  • chore(biome): make biome.json the root config, so lint runs standalone
  • fix(ui.view.react): import siblings relatively, not through the package name
  • ci: build before typecheck
  • ci: Node 24
  • chore: regenerate pnpm-lock.yaml against registry.npmjs.org
  • fix(views): typecheck clean under TypeScript 7 (test fixtures, CSS imports)

Release order

The new versions are published to npm from this branch (next tag, install verified, then promoted to latest) before this PR is merged, so the changesets release job on main finds every version already published.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U

mkotelnikov and others added 25 commits October 3, 2026 13:59
shell.core only uses the DockviewApi / IDockviewPanel types; the React host
component lives in shell.view.react. With dockview-core, shell.core is headless
(no React in its tree) and can move to statewalker-kernel. Type-only imports in
dock-host.ts and its two tests; no runtime change.
- ai-agent.core, backbone.core: lib ESNext + DOM, types node, @types/node via
  catalog (they use console, fetch, URL, AbortSignal, ReadableStream, process);
  same pattern as ai-openai-compat.core
- backbone.node: types node (a Node package: no DOM); @types/node was already a
  devDependency but never referenced
- ai-agent.core tests, latent type bugs (vitest strips types, so they ran):
  - executor.test.ts: setW took `typeof open` (status narrowed to "open"), so
    setW(done) could not compile -> TodoItem[]
  - model-manager.test.ts: a bare vi.fn() is not a LocalModelFactory -> cast
The catalog range ^0.1.1 resolves to webrun-site-host 0.1.7 on a fresh
resolution, and 0.1.7 (like 0.2.0) was published with raw catalog: / workspace:
specifiers: ERR_PNPM_SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER. Only the lockfile's 0.1.1
pin kept installs working. 0.2.1 is packed by pnpm; 0.2 is API-compatible for
webapp.core / webapp.browser (verified by typecheck and tests).
dockview-react 6.0.3 depends on dockview ^6.0.3, which brings dockview-core 6.6.1.
With shell.core pinned to exactly 6.0.3, a fresh resolution installs both, and
shell.view.react fails to typecheck (DockviewApi from 6.6.1 not assignable to
6.0.3's); mime.view.* and explorer.view.react inherit the error and three
mime.view tests fail. The base only had one copy through its lockfile.
jsdom 30.1.1 (picked by any fresh resolution; the base lockfile had 30.0.1)
breaks `new Blob([bytes])` in mime.view.image / pdf / video tests: the view shows
"Cannot read properties of undefined (reading '_buffer')" and the test times out.
Verified: same tests pass on 30.0.1 and fail on 30.1.1.
17 headless packages move to statewalker-kernel and the 6 headless ai-* packages
to statewalker-ai, both with history (git filter-repo), together with the two
backbone scripts. The 15 React packages stay (the future statewalker-shell-react).
…convention

- references to the 23 moved packages: workspace:* -> catalog: at ^0.2.0
  (11 kernel / ai packages are used by the 15 that stay)
- prunecatalog.py: 58 entries only the moved packages used
- webrun-files, -mem ^0.7 -> ^0.10.0; root turbo adopts the catalog ^2.10.9
- test fixtures build complete file entries (size, lastModified): FileInfo and
  DirectoryEntry are unions on `kind` since webrun-files 0.9 / kernel 0.2
  (explorer.view.react files-list-view, inline.view.react inline-content)
workspace:* publishes an exact version of a sibling package ("0.2.0");
workspace:^ links the local package the same way during development and
publishes a range ("^0.2.0"), so consumers can take compatible patches.
Wave 0 of the dependency update (tools/align.mjs): every external catalog entry
becomes ^<latest on npm within its breaking line>, the same value in every
statewalker repo. No breaking line is crossed (those are separate waves); peer
ranges, prerelease pins and documented exact pins are unchanged.
vitest, @vitest/* -> ^5.0.3; jsdom -> ^30.1.1, the same in every statewalker repo.
The jsdom 30.0.1 pin is gone: the mime.view.* failure under jsdom 30.1 came from
vitest 4's jsdom environment (its URL.createObjectURL read jsdom's internal
Blob._buffer, renamed in jsdom 30.1); vitest 5 reads the new field. Peer ranges
widened, not replaced (vitest "^4.x || ^5.0.3"), so published packages keep
accepting existing consumers.
Latest major, the same in every statewalker repo, where this repo uses it:
@changesets/cli ^3.0.3 (the existing .changeset/config.json works unchanged),
testcontainers ^12.2.0, @hono/node-server ^2.1.3, @types/node ^26.6.4,
@testing-library/jest-dom ^7.0.1 (6.10.0, which wave 0 had picked, is deprecated
by its maintainer as a breaking minor; 7 is the upgrade they recommend).
… wave 5)

- kernel and ai packages ^0.2.0 -> ^0.3.0 (their 0.3.0 releases: dockview 8 in
  shell.core, dependency waves 0-5)
- dockview-react 6.0.3 -> ^8.4.0, the same dockview line as kernel's shell.core;
  shell.view.react needs no code change (its typecheck errors are the 9 in test
  files that already existed on dockview 6)
kernel 0.4.0 (json-render 0.21) and statewalker-ai 0.4.0 (AI SDK 7, widened
provider / model types); @json-render/core, -react, -shadcn ^0.21.0.
Versions from tools/releaseplan.mjs: minor on 0.x where a runtime or peer
dependency moved to another breaking line, patch where only the published
manifest changed (internal ranges ^x.y.z, dependency updates).
- @statewalker/ai-config.view.react 0.2.0
- @statewalker/ai-local-models.view.react 0.2.0
- @statewalker/explorer.view.react 0.2.0
- @statewalker/inline.view.react 0.2.0
- @statewalker/mime.view.image 0.2.0
- @statewalker/mime.view.markdown 0.2.0
- @statewalker/mime.view.pdf 0.2.0
- @statewalker/mime.view.video 0.2.0
- @statewalker/render.view.react 0.2.0
- @statewalker/settings.view.react 0.2.0
- @statewalker/shell.view.react 0.2.0
- @statewalker/ui.view.react 0.2.0
- @statewalker/ui.view.shadcn 0.1.2
- @statewalker/webapp.view.react 0.2.0
- @statewalker/workspace.view.react 0.2.0
Every public package now exports built JavaScript and declarations
from dist, with a "source" condition pointing at the TypeScript file:
  { "source": "./src/x.ts", "types": "./dist/x.d.ts",
    "import": "./dist/x.js", "default": "./dist/x.js" }
Packages that exported raw source get an unbundled tsdown build (one
entry per exported module). src stays in "files" for the source
condition. CSS exports are unchanged.

Also: ai-config.view.react exported "." from src/index.ts, which did not
exist. Added the index that re-exports ./public, as in its siblings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
Versions from tools/releaseplan.mjs: minor on 0.x where a runtime or peer
dependency moved to another breaking line, patch where only the published
manifest changed (internal ranges ^x.y.z, dependency updates).
- @statewalker/ai-config.view.react 0.2.1
- @statewalker/ai-local-models.view.react 0.2.1
- @statewalker/explorer.view.react 0.2.1
- @statewalker/inline.view.react 0.2.1
- @statewalker/mime.view.image 0.2.1
- @statewalker/mime.view.markdown 0.2.1
- @statewalker/mime.view.pdf 0.2.1
- @statewalker/mime.view.video 0.2.1
- @statewalker/render.view.react 0.2.1
- @statewalker/settings.view.react 0.2.1
- @statewalker/shell.view.react 0.2.1
- @statewalker/ui.view.react 0.2.1
- @statewalker/ui.view.shadcn 0.1.3
- @statewalker/webapp.view.react 0.2.1
- @statewalker/workspace.view.react 0.2.1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
"root": false marked this config as nested under the umbrella workspace.
In a standalone checkout (CI, or a clone of this repo alone) biome then
found no root configuration and ignored the file, so lint:check reported
every file. The config is now a root config, migrated to the installed
biome, with biome's safe fixes applied. Tools running biome from the
umbrella root should pass --config-path for each repo.

Remaining errors fixed by hand where present: an assignment inside an
expression (layout-store connect), unsafe optional chaining in tests,
a non-null assertion on an optional chain, ambiguous anchor text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
…ge name

use-adapter-value.ts and app-root.tsx imported useAdapter and
AppWorkspaceProvider from "@statewalker/ui.view.react", i.e. the package
itself. While the package exported raw source that resolved to the same
modules; with dist exports it resolves to dist, a second copy with its
own React context, so useAdapterValue threw "useAppWorkspace must be used
inside <AppWorkspaceProvider>" under a provider from src.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
Packages export their types from dist with a "source" condition, so a
package's types for its siblings are the built dist/*.d.ts. Typecheck
therefore needs the build first, the order the standalone check uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
TypeScript 7, tsdown 0.23 and vitest 5 need a newer Node than 20: tsdown
loads tsdown.config.ts with Node's type stripping, and the toolchain
uses Promise.withResolvers. Everything was verified locally on Node 24,
the version statewalker-sandbox's CI already uses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
Resolved from scratch (no lockfile, no node_modules) against npm. The
previous lockfile held three versions of workspace.core (0.2, 0.3, 0.5)
and the raw-source ai-local-models.browser 0.4.0, which typechecked that
package's sources; it now locks the dist + source releases only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
…ports)

CI typechecks every package; these errors had been tolerated locally.
- shell.view.react: declare *.css modules (TypeScript 7 checks
  side-effect imports); ClosePanelCommand resolves void; LineTab tests
  pass full header props instead of casting the api to any.
- ui.view.react: the test's CounterAdapter shares close() with the
  all-optional WorkspaceAdapter type, as real adapters do.
- workspace.view.react: the stubbed ChangeWorkspaceCommand resolves with
  the workspace its result type requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
@mkotelnikov
mkotelnikov merged commit 2eed5e6 into main Oct 4, 2026
1 check passed
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