Skip to content

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

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

mkotelnikov merged 34 commits into
mainfrom
migration/monorepo-restructuring

Conversation

@mkotelnikov

Copy link
Copy Markdown
Contributor

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

What changes

  • 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, tsdown 0.23).
  • Public packages publish built dist with a source export condition pointing at the TypeScript source.
  • biome.json is a root config again (with "root": false biome ignored it in a standalone checkout).
  • The lockfile is resolved against npm, locking the layer-0 releases published today.

Commits

  • chore: empty root for webrun-http-browser history import
  • chore: empty root for webrun-http history import
  • feat: import webrun-http history into packages/webrun-http
  • chore: empty root for webrun-ports history import
  • feat: import webrun-ports history into packages/webrun-ports
  • feat(webrun-biscuit): Biscuit tokens in pure TypeScript
  • Merge pull request fix(member): mountEdge recovers a page its ServiceWorker does not control (hard reload hang) #5 from statewalker/feat/webrun-biscuit
  • chore(release): webrun-biscuit and webrun-http-proxy at 0.1.0
  • fix(webrun-biscuit): write variables as interned names, not parser-local ids
  • feat(webrun-biscuit): parameters, queries, token-less evaluation, WebCrypto verification
  • chore(release): webrun-biscuit at 0.2.0
  • Merge pull request Adopt @statewalker/webrun-http-browser 0.5.0; mountEdge relies on its uncontrolled-page recovery #6 from statewalker/biscuit-ts
  • fix(webrun-biscuit): parse the names and arities the reference parses; ship LICENSE
  • chore(release): webrun-biscuit at 0.2.1
  • Merge pull request LiteLLM without Traefik basic auth: only LiteLLM's own authentication #7 from statewalker/fix/biscuit-parser-names
  • fix(webrun-biscuit): enforce the run budget inside the join, not only between iterations
  • Merge pull request Hub: the LiteLLM dashboard's Authorization key reaches LiteLLM #8 from statewalker/fix/biscuit-parser-names
  • refactor: take in webrun-biscuit from webrun-wire, with history
  • build: carry rolldown.preset.js and webrun-biscuit's catalog entries over from webrun-wire
  • build: pnpm 10.16.1, the reference convention, webrun-files 0.10
  • build: one @libp2p/interface (3.2.5) through pnpm overrides
  • build: one @libp2p/interface, 3.3.0, without an override; 0.1.1
  • 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)
  • chore(release): httpeers, local release round
  • build: publish dist with a source condition (decision 1)
  • chore(release): httpeers, local release round
  • chore(biome): make biome.json the root config, so lint runs standalone
  • fix(httpeers-member): platform transports are optional peers, not dependencies
  • chore(release): httpeers, local release round
  • chore: regenerate pnpm-lock.yaml against registry.npmjs.org

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 a release job on main finds every version already published.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U

mkotelnikov and others added 30 commits April 17, 2026 12:59
Merged the full history of statewalker/webrun-http into this monorepo,
with all files rewritten under packages/webrun-http/.
Merged the full history of statewalker/webrun-ports into this monorepo,
with all files rewritten under packages/webrun-ports/.
Adds @statewalker/webrun-biscuit: the proto2 codec, the Ed25519 and
secp256r1 signature chain, the Datalog fixpoint engine, the text parser,
the authorizer and the token builder. Two runtime dependencies, no WASM
and no Node built-ins in src/, so it runs wherever the rest of the wire
runs. npm carries no other pure JS/TS Biscuit implementation.

The library arrives from its own session repository; this commit is the
port onto this repo's conventions, and the port is only worth trusting
because the mutation harness still passes.

**Tests: 156 + 55, none removed.** The suite moved from `tsx --test` to
vitest under tests/, and the conversion was deliberately the smallest one
that works: the `node:test` import is dropped so vitest's global `test`
takes over, and every `node:assert` assertion is kept byte-identical, so
nothing was retranslated. node:test's `test(name, {skip}, fn)` has no
vitest equivalent and became `test.skipIf(...)`, which is the only
semantic edit in the suite.

`pnpm mutate` injects ten known defects and requires each to break a
test. All ten are still caught after the port — a green suite would not
by itself show that the ported tests can fail. Each mutation's `find`
string is a literal source excerpt, so biome's reformat of src/ made six
go stale; they are re-derived against the formatted source rather than
loosened.

The cross-reference suite stays out of `pnpm test` behind its own vitest
config. It drives @biscuit-auth/biscuit-wasm, whose build reports
spurious RunLimit timeouts under CPU contention, and a flaky reference
must never be able to redden the main suite or a turbo build.

Two defects fixed in the corpus fetcher while wiring it up:

- A partial download poisoned samples/ permanently. samples.json was
  written before the tokens it names, while the "already fetched" guard
  checked only samples.json — so an interrupted fetch left a directory
  every later run skipped as complete, and tests then failed on missing
  files for ever. The manifest is now written last and the guard checks
  every file it names.
- All 38 downloads were issued at once, which raw.githubusercontent
  resets, surfacing as a bare `TypeError: fetch failed`. Now pooled at
  six with retries.

Three lint errors in src/ were exhaustive switches with no terminal
statement; they gain a `default` that throws, so a future unhandled
variant is loud rather than `undefined`.

README follows the package shape in ~/.claude/rules.md, and its four
examples were run against the built dist/ — the published entry point,
not the source. Design documents from the implementation sessions are
kept under docs/webrun-biscuit/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both packages are new and have never been on npm. A minor changeset
against a 0.1.0 manifest would have bumped them to 0.2.0 and made that
their first published version, so both were reset to 0.0.0 and the same
changesets re-applied, landing them on 0.1.0 with CHANGELOGs that
document the version actually shipped.

Verified before publishing: the full suite is green (1,100+ tests across
17 packages), and both tarballs were packed, unpacked and installed into
a throwaway consumer project. `catalog:` specifiers are rewritten to real
ranges in the published manifests (`@noble/curves` ^2.4.0, `@noble/hashes`
^2.4.0), no `workspace:` or `catalog:` protocol leaked, and both entry
points import and run from the tarball rather than merely typechecking.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cal ids

A variable's wire id is a symbol index. The builder wrote the parser's local
ids (1, 2, ...), so the reference printed `$k` as `$write`, and a block with
28 or more distinct variables minted a token this library could not read back
("unknown default symbol 28"). BlockContent now carries the parser's variable
names and SymbolWriter interns them, closure parameters included.

Found by porting httpeers-access onto this package: its failed-check text
came back naming `$write`.

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

What an application needs to use Biscuit without working around the API,
found by porting httpeers-access off biscuit-wasm:

- `{name}` parameters, bound as terms, across build/attenuate/authorize/
  evaluate/query; unbound and unused parameters are a ParseError.
- `evaluate(token | null, code)` returning the result plus `query(rule)`,
  scoped like the authorizer; the snapshot is computed only on request, so
  `authorize` stops printing the world on every call.
- `FailedCheck.rule`, compared against the official corpus and, in the
  random differential suite, against the reference.
- `verifyAsync` / `loadTokenAsync` on WebCrypto Ed25519 (0.25 ms against
  1.9 ms per token in Node), sharing one list of signature checks with the
  sync path.

The seal mutation is re-derived against the refactored chain, and three new
mutations cover the async verdict, query scope and unused parameters: 13/13
caught. Main suite 180, cross-reference 56.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
webrun-biscuit 0.2.0: parameters, queries, token-less evaluation, WebCrypto verification
…; ship LICENSE

Names are `[A-Za-z0-9_:]+`, ASCII, with any character first, for predicates and
variables alike, and a predicate takes at least one term. The parser had
required a Unicode letter first and accepted `f()`: 9 of 12 probed sources
disagreed with @biscuit-auth/biscuit-wasm, including `_m(true)`, which the
httpeers shell prototypes use. `$ x` is no longer a variable either.

tests/grammar-cases.ts records what the reference decided; cross/05-grammar
re-asks it for every row and for 400 generated names. Two new mutations
(zero-term predicates, Unicode names) are caught: 15/15.

0.2.0 shipped without LICENSE because the file lived only at the repository
root; the package now carries its own copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
webrun-biscuit 0.2.1: parse the names and arities the reference parses; ship LICENSE
… between iterations

The limits were read after each fixpoint iteration, so one combinatorial rule
enumerated its whole product first, and a check or policy (which derives no
facts) was not bounded at all. Measured: a token held the verifier 4.8 s
against a 50 ms budget; an exploding check ran 12 s. Found by httpeers-access's
"exploding rule set denies rather than hanging" test, which went over its 5 s
bound in CI once the engine was this package instead of biscuit-wasm.

- The clock is polled every 1024 candidate facts inside `combine`, which
  rules, checks and policies all go through.
- `maxFacts` counts distinct facts as they are derived, so it fires the moment
  the world would exceed it: the same verdict as the end-of-iteration check,
  without enumerating the rest. On the last permitted iteration it stands
  aside, so TooManyIterations keeps the precedence the reference gives it.
- The budget closes when the evaluation ends: a later `query` is not charged
  against it.

Main 185, cross-reference 58 (verdict parity unchanged). Two new mutations are
caught; the leak one first survived, because its test queried one fact and
never reached the clock's polling interval — fixed with 3,000. 17/17.

Folded into the unreleased 0.2.1 changelog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
webrun-biscuit 0.2.1: enforce the run budget inside the join (missed #7's merge)
…over from webrun-wire

webrun-biscuit imports ../../rolldown.preset.js (a webrun-wire root file) and uses
@noble/curves, @noble/hashes, rimraf and rolldown from webrun-wire's catalog
(values as of webrun-wire 770ff36).
- packageManager pnpm 10.16.0 -> 10.16.1
- comply.py: 129 specifiers through the catalog (apps used literals);
  adopted hono 4.13.7 -> ^4.12.14 in apps/sites
- apps/sites pinned webrun-files, -mem, -node, -s3 at 0.7.x exactly while the
  catalog said ^0.9.0: all via catalog ^0.10.0 (-node, -s3 entries added);
  peers "^0.9.0 || ^0.10.0"
- apps/sites: FileStats -> FileEntryStats in serve.ts and lookup.ts. statFile()
  already returned file stats only; the type now says so, no runtime change.
The packages pin @libp2p/interface 3.2.5, but libp2p and its plugins depend on
^3.x. Upstream's lockfile predates 3.3.0 (2026-08-23), so every transitive copy
resolved to 3.2.5. Once the specifiers moved to catalog:, pnpm re-resolved them
and the transitive copies took 3.3.0: two copies, and httpeers-libp2p failed to
build (TS2322, Libp2p<ServiceMap> not assignable). A fresh install of upstream
without its lockfile hits the same. The override keeps the tested upstream state.
Replaces 45368e0's override. The packages pinned @libp2p/interface 3.2.5 while
libp2p 3.3.8 and its plugins resolve ^3.x to 3.3.0 (published 2026-08-23): two
copies, and Libp2p types from one are not assignable to the other. An override
only applies inside this workspace, so every consumer of httpeers-libp2p /
-member (statewalker-shell-theia, statewalker-sandbox) still got both. With the
catalog on 3.3.0 the whole graph shares one copy. Public packages 0.1.0 -> 0.1.1
(published manifests change; none of them is on npm yet).
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.
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/httpeers-access 0.1.2
- @statewalker/httpeers-bridge 0.1.2
- @statewalker/httpeers-ghost 0.1.2
- @statewalker/httpeers-hub 0.1.2
- @statewalker/httpeers-join 0.1.2
- @statewalker/httpeers-libp2p 0.1.2
- @statewalker/httpeers-member 0.1.2
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: httpeers-member's ./browser and ./node profiles import libp2p,
its transports and @libp2p/crypto at runtime, but they were only
devDependencies (0.1.2 cannot run those subpaths). They are now
dependencies.

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/httpeers-access 0.1.3
- @statewalker/httpeers-bridge 0.1.3
- @statewalker/httpeers-core 0.1.2
- @statewalker/httpeers-ghost 0.1.3
- @statewalker/httpeers-hub 0.1.3
- @statewalker/httpeers-join 0.1.3
- @statewalker/httpeers-libp2p 0.1.3
- @statewalker/httpeers-member 0.1.3
- @statewalker/httpeers-qr 0.1.2
- @statewalker/webrun-biscuit 0.2.2

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: it formatted with its
defaults (tabs) and checked dist/, so lint:check failed on every file.
That is why CI on main has been red.

The config is now a root config, migrated to the installed biome, and
biome's safe fixes are applied (formatting, import order). Tools running
biome from the umbrella root should pass --config-path for each repo.
Remaining errors were fixed by hand (button types and a placeholder link
in demo pages, labels without controls, unsafe optional chaining in tests).

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

The ./node and ./browser entries import libp2p and its transports, which
were only devDependencies. The previous commit made them dependencies,
but the boundary test pins the design: a member depends on the transport
(httpeers-libp2p) and on no platform. They are now optional peer
dependencies (catalog:peers), like idb-keyval: whoever imports ./node or
./browser installs them. @libp2p/crypto stays a dependency because the
root entry reaches it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
mkotelnikov and others added 4 commits October 4, 2026 13:04
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/httpeers-member 0.1.4

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, so the
@statewalker dependencies lock the dist + source releases rather than
the versions installed before them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
webrun-biscuit, carried over from webrun-wire with this restructuring,
builds with a rolldown config that imports ../../rolldown.preset.js. The
hub Dockerfile copies only selected root files, so the image build
failed with "Cannot find module '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/repo/rolldown.preset.js'".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DAiZN6XrTAdQpNN3eXEz2U
Every other statewalker repo's CI now runs Node 24, the version the
restructuring was verified on (TypeScript 7, tsdown 0.23, vitest 5). The
member reservation-fallback conformance test failed once on Node 22 in
CI while passing 10/10 locally on Node 24.

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