Skip to content

This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.

Development setup

Terminal window
git clone https://github.com/LiamRiddell/solve-engine.git
cd solve-engine
npm install

That is everything the engine needs. The documentation site and the playground sit outside the npm workspaces, because each pulls in a front-end dependency tree that has no business in the published package’s lockfile, so they take one more step:

Terminal window
npm run setup

It builds the engine (the site depends on it through file:, and that package publishes only dist), then installs docs and playground. Rerun it after pulling changes that touch either project’s dependencies, and after any change to the engine, since a stale dist leaves the site running yesterday’s engine.

Terminal window
npm run verify

The fast loop: the type check, the default test run, and the package build. Run it while iterating.

Terminal window
npm run verify:ci

The gate. Every check continuous integration applies, as one command, in the order the table below lists them. Continuous integration runs the same named scripts split across jobs for speed, and a release runs this whole command again before anything reaches npm, so if it passes locally the pull request passes. It takes several minutes; run it before pushing anything that touches the lexer vocabulary, a unit, the public exports, the docs examples or the bundle.

The difference between the two is four test suites and a dozen lint and packaging checks. verify skips heavy/MemoryLeak, LexerFuzz, LexerVocabularyFuzz and LongDocumentRobustness, which are slow and memory-hungry; verify:ci runs them and then checks everything the site and the registry depend on. The coverage floor is the one gate outside it, measured daily by its own workflow because the measurement is slow; run npm run test:coverage for a change that removes tests.

ScriptWhat it does
npm run verifyType check, default test run, build, smoke checks. The fast loop.
npm run verify:ciThe gate: every check CI applies, run as one command. The rows below are the main ones.
npm run typecheckThe type check of the engine source, through TypeScript 7’s tsc (installed as typescript7); TypeScript 5.9’s tsc runs as a second check via typecheck:tsc.
npm run typecheck:testsThe spec files and test tools, type-checked by the same compiler against a per-file baseline in packages/engine/__tests__/typecheck-baseline.json that may only fall. A new error fails; a fixed one rewrites the baseline lower, to commit.
npm test, npm run test:ciThe default test run: every suite except the four slow ones.
npm run test:fullEvery suite, single-threaded with a raised heap. Writes the report stats:tests reads.
npm run test:coverageEvery suite with coverage measured against the floor in jest.coverage.config.cjs, in three shards run side by side and merged before the floor is checked (SOLVE_COVERAGE_SHARDS sets the count). Slow; CI runs it daily rather than per pull request.
npm run test:lightThe default run minus the fuzz and robustness suites, for a quick signal on a slow machine.
npm run lintoxlint over the engine source, the spec files, the tools, the playground bridge and the scripts. Correctness rules fail; style rules warn.
npm run lint:commentsComment style, over the whole tree.
npm run lint:messagesThe messages the engine writes for a reader (errorValue, lineMessage, ErrorFactory, console.warn): no em-dash, British spelling, no JavaScript operator, and no host method named in a line’s result.
npm run lint:docsEvery public export carries a doc block.
npm run lint:actionsEvery GitHub Action in the workflows is pinned to a commit.
npm run lint:action-inputsEvery with: input a workflow passes is one its action declares, read from .github/action-inputs.json. After moving a pin, refresh that list with node scripts/check-action-inputs.mjs --update.
npm run lint:licensesThe runtime dependency and everything under it carry an allowlisted licence.
npm run audit:depsnpm audit at high severity. The docs and playground lockfiles are audited in their own CI jobs.
npm run buildtsup: ESM, CJS and declarations into packages/engine/dist.
npm run smoke, npm run smoke:bundledImport the built package by subpath; prove the sideEffects: false claim survives bundling.
npm run lint:packagepublint and arethetypeswrong against the built package, both pinned.
npm run lint:stats, npm run stats:testsCheck, or regenerate, the test counts the site quotes, from the last test:full report.
npm run lint:size, npm run stats:sizeCheck, or regenerate, the bundle and tarball sizes the site quotes. The tarball is packed under a pinned npm so the figure is the same on every machine; the brotli figure is measured on the exact Node in .nvmrc. Regenerate only after a clean npm run build.
npm run lint:units, npm run stats:unitsCheck, or regenerate, the unit reference page, generated by probing the built engine.
npm run lint:keywordsEvery word and phrase the built engine reads (its keywords, function names, as converters, call words and normaliser phrases) is mentioned on a syntax page. A name that should not be documented yet is set aside, with its reason, in scripts/keyword-docs-allowlist.json. Needs npm run build first.
npm run lint:sidebarEvery documentation page is on the sidebar, once.
npm run lint:linksEvery root-relative docs link reaches a page, and every #fragment a heading on that page (a page title is #_top). /playground/ and /api/ are built separately and allowed by name; external addresses are not followed, so the gate never waits on the network.
npm run docs:llmsRegenerate docs/public/llms.txt and llms-full.txt from the cheatsheet and the proven examples. LlmsTxt.spec.ts fails in every test run while the committed copies are stale, so run this after changing a syntax page’s examples or the cheatsheet.
npm run data:cpiRegenerate the bundled price indices, one per currency, each from its recorded source: the US CPI-U in packages/finance/data/CpiTable.ts from BLS series CUUR0000SA0 (#700, scripts/build-cpi-table.mjs), the UK index in UkCpiTable.ts from ONS series CDKO (#756, scripts/build-uk-cpi-table.mjs), and the euro-area HICP in EuroHicpTable.ts from ECB series ICP.M.U2.N.000000.4.INX (#756, scripts/build-euro-hicp-table.mjs). By default each rebuilds from the snapshot in scripts/fixtures/cpi/, so a plain run reproduces the committed tables; -- --check fails when any table has drifted from its snapshot. The options that pick a source belong to one index, so they take --index=us, --index=uk or --index=euro: -- --index=us --from-bls --save-csv=scripts/fixtures/cpi/cpiai.csv fetches the BLS public API (a BLS_API_KEY raises its window from 10 to 20 years), -- --index=uk --from-ons --save-csv=scripts/fixtures/cpi fetches the ONS generator, -- --index=uk --from-mirror=<dir> reads a newer copy of the Frictionless Data mirror, and -- --index=euro --from-ecb --save-csv=scripts/fixtures/cpi/ecb-icp-hicp-euro-area.csv fetches the ECB data API, each recording what it fetched. The engine never fetches an index at runtime.
npm run stats:parityRegenerate the parity counts the internal audits quote (docs-internal/parity-stats.json and the markers in the audits) from SoulverParity.spec.ts and OtherAppsParity.spec.ts, which fail while a quoted count is not the measured one.
npm run lint:cheatsheetEvery syntax page is linked from the cheatsheet, and every link there reaches a page. A page that is a reference rather than an area of the language is set aside, with its reason, in scripts/check-cheatsheet.mjs.
npm run test:consumerPack the tarball, install it into a scratch project, and use it by bare specifier.
npm run sizeReport the bundled size. It does not gate.
npm run bench, npm run bench:compare, npm run bench:baseline, npm run stats:benchRun the benchmarks, compare two runs, refresh the committed baselines, and record the throughput figures the site quotes. See performance.
npm run fuzzThe fuzzer’s soak mode, across four generators: bytecode corrupts an opcode stream, expression writes a line of source, document edits a whole document and checks every answer against a plain pass over the same text, and crosspath asks one document of both parseDocument and evaluateDocument and checks they agree line for line. Runs nightly in CI; run one with -- --generator=document, or reproduce a finding with -- --seed=<seed>.
npm run verify:allverify plus the playground build.
npm run changeset:versionWhat the version pull request runs: bump, lockfile, build, full suite, and every regenerated figure.
npm run release:checkThe release preflight, read-only: npm against the changelog and the GitHub releases, stuck or cancelled publish runs, pending changesets, a throwaway changeset version, and a release-note skeleton. Needs the network and gh; see releasing.
PathContents
packages/engineThe published package
packages/playground-bridgeShared glue between the engine and the playground
playgroundThe interactive playground application
docsThis documentation site
docs-internalMaintainer notes, not published
Terminal window
npm run dev --prefix playground
Terminal window
npm run dev --prefix docs

On Windows, stop the dev server before rerunning npm run setup. Astro holds a native binary open inside docs/node_modules, and reinstalling over it fails with EPERM.