Repository navigation
-I library include roots for build and run, and an m0 session example - #120
Merged
Merged
Conversation
`napi-mojo build` and `run` passed exactly one -I, the framework's, so nothing built through the CLI could import another Mojo library. -I <dir> is now repeatable on both, and every root goes to the compiler AFTER the framework's: Mojo takes the first root holding a package of the imported name and says nothing about the rest, so a library placed first could replace `napi` silently. For the same reason a root holding its own `napi` (directory, .mojopkg or .mojoc) is refused rather than ordered, along with a root that is missing, not a directory, the framework root again, or given twice. A root that is itself a package gets a warning that -I wants its parent. `run`'s build cache now hashes every root, in order. Writing the tests for that found the cache was already incomplete: its walk skipped symlinked directories and hashed only .mojo files, so a library symlinked beside the entry — the obvious workaround before -I — served the old binary after every edit behind the link. The walk now follows symlinks behind a realpath cycle guard, hashes .🔥, .mojopkg and .mojoc too, and skips node_modules (Mojo never imports through it, and pnpm's symlinks would otherwise walk every package). The compiler's version is still not in the key; CLAUDE.md now says so instead of calling the set complete. tests/cli.test.js drives the CLI with a stub compiler passed through --mojo, which records each compile, so the order of roots and every cache decision are tested with no toolchain. Each rule was reverted once and its test failed (15 of 15). The new "Host-mode program with library include roots" step proves the same with a real compile: an import resolved through -I, a cache hit, and an edit reaching the next run through the root and through a symlink beside the entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013tXV7MkRfP97g5hPfNNXHC
m0 is mojo-http's application framework; its PyPI wheel ships the framework's Mojo source under m0/_mojo. examples/m0-session/lib.mojo compiles m0's session and grant code into an addon with `napi-mojo build -I <wheel>/m0/_mojo`, unchanged, so a Node service beside an m0 application can check m0's cookies and stream grants with m0's own code rather than a JavaScript copy of the format kept in step by hand. m0 0.2.0 and the framework compile together on Mojo 1.1.0 with --Werror clean, in about 9 s. parity.mjs (node --test; named so Jest never collects it) judges the addon with an issuer written on node:crypto, the role grant.py and notes_session.py play in mojo-http, after first proving that oracle reproduces mojo-http's pinned vectors. It then checks those vectors, 300 seeded random grants and sessions with non-ASCII keys and cookies (issueSession byte-equal to the oracle), every refusal class, m0's own refusal messages whole, and that text crosses as its UTF-8 bytes past the 256-byte fast path, with a NUL and with a lone surrogate. Eight reverted rules, three on the oracle's side and five on the addon's, each fail it; a build without the -I root fails naming m0_core. The new m0-interop job is non-required and PR-only, like runtimes. It downloads the wheel from PyPI at a pinned M0_VERSION, so neither repo gains a build-time dependency on the other, and checks the wheel's gated_mojo against `mojo --version` before compiling, so a drifted pin fails with a sentence. The example ships in the npm package as a template, minus its build output. The hashes take strings rather than a Uint8Array: m0_core's public fnv1a/xxhash32 are String-based, and the byte-pointer forms are underscore-private. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013tXV7MkRfP97g5hPfNNXHC
codetalcott
marked this pull request as ready for review
September 26, 2026 15:43
codetalcott
enabled auto-merge
September 26, 2026 15:43
This was referenced Sep 26, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two commits, reviewable separately. The second can be dropped without affecting the first.
1.
feat(cli): -I library include roots for build and runnapi-mojo buildandrunpassed exactly one-I(the framework's), so nothing built through the CLI could import another Mojo library.-I <dir>is now repeatable on both commands.napi.napi(directory,.mojopkgor.mojoc).-Iwants its parent directory.run's build cache now hashes every root, in order. Writing those tests showed the cache was already incomplete:.mojofiles.So a library symlinked beside the entry (the obvious workaround before
-I) kept serving the old binary after every edit behind the link. The walk now:.🔥,.mojopkgand.mojoc;node_modules.The compiler's version is still not in the key. CLAUDE.md now says so instead of calling the input set complete.
Tests
tests/cli.test.js: 19 new tests drive the CLI with a stub compiler passed via--mojo, which records each compile. Root order and every cache decision are tested without a Mojo toolchain.mainas it stands.testjob, "Host-mode program with library include roots", does a real compile:-I;2.
examples: m0-session, an existing Mojo library compiled through -Im0 is mojo-http's application framework, and its wheel ships the framework's Mojo source.
examples/m0-session/lib.mojocompiles m0's session and grant code, unchanged, into an addon withnapi-mojo build -I <wheel>/m0/_mojo. A Node service beside an m0 app can then check m0 cookies and stream grants with m0's own code. m0 0.2.0 and the framework compile together on Mojo 1.1.0,--Werror-clean, in about 9 s.parity.mjsruns undernode --testand is named so Jest never collects it. It judges the addon against an issuer written onnode:crypto, after first proving that issuer reproduces mojo-http's pinned vectors. It checks:issueSessionbyte-equal to the oracle;Eight reverted rules each fail it: three on the oracle side, five on the addon side. A build without the
-Iroot fails namingm0_core.The new
m0-interopjob is non-required and PR-only, likeruntimes.M0_VERSION, so neither repo gains a build-time dependency on the other.gated_mojoagainstmojo --version, so a drifted pin fails with a message instead of a compile error.The example ships in the npm package as a template, minus its build output (
!examples/m0-session/build). Deviation from the plan: the hash exports take strings, not aUint8Array. m0_core's publicfnv1a/xxhash32takeString; the byte-pointer forms are underscore-private.Checked locally (Linux, pinned pixi toolchain)
build.sh;npm test: 835 passed, 12 skipped.run:block of the newm0-interopjob, taken from the YAML: all pass. Its mismatch branch exits 1 with its message.check-doc-samples,generate-rules-extract --check,sync-versions --check,generate-api-reference --check(in pixi).npm pack --dry-runshipsexamples/m0-session/{lib.mojo,parity.mjs,README.md}only.Not checked locally: macOS. CI's required macOS job covers the stub tests and the new e2e step.
🤖 Generated with Claude Code
https://claude.ai/code/session_013tXV7MkRfP97g5hPfNNXHC
Generated by Claude Code