Saxon-Forms is an XForms implementation written using Interactive XSLT 3.0. Interactive XSLT 3.0 is a feature of Saxon-JS, which is an XSLT 3.0 run-time written in pure JavaScript for use in the browser or a platform that supports JavaScript.
See conference papers below:
Implementing XForms using interactive XSLT 3.0. XML Prague 2018.
Distributing XSLT Processing between Client and Server. XML London 2017.
For first-time contributor onboarding, run:
npm run setup
npm run doctor
npm run verify:setup- Node.js + npm
- Saxon-JS 3 runtime assets available in
Saxon-JS/(or copied to your hosting location)
Saxon-JS can be downloaded at Saxon-JS (older versions available at the archive).
npm run setupnpm run build:sefThis generates sef/saxon-xforms.sef.json and syncs it into tests/runtime/sef/saxon-xforms.sef.json.
npm run examplesThen open http://127.0.0.1:5174/ and start with:
examples/hello.htmlexamples/native-xforms-fiddle.htmlexamples/sample1.html(direct XForms-source loading)examples/sample2.html(restored legacy sample page)examples/sample3.html(restored legacy sample page)
npm run examples starts a local server and continues running until you stop it manually.
npm run test:e2eRun with a specific browser mode:
npm run test:e2e:chrome
npm run test:e2e:firefox
npm run test:e2e:bothOr pass the harness flag directly:
npm run test:e2e -- --browser=firefoxTo run tests interactively:
npm run test:e2e:ui- Build SEF:
npm run build:sef - Run all examples:
npm run examples - Compile stylesheet-driven examples on demand:
npm run examples:compile - Fetch W3C suite:
npm run fetch:w3c - Fetch NIST xsdtests dataset:
npm run fetch:nist - Build precomputed NIST Playwright case index:
npm run build:nist-engine-index - Run e2e in Chrome only:
npm run test:e2e:chrome - Run e2e in Firefox only:
npm run test:e2e:firefox - Run e2e in both browsers:
npm run test:e2e:both - Run full e2e flow (prepare + build + test):
npm run test:e2e:full - Run xsd helper tests:
npm run test:xsd-helpers - Run NIST facet harness with Saxon-Forms engine:
npm run test:nist-facets - Run NIST facet inventory by datatype families:
npm run test:nist-facets:inventory - Run diagnostics tests:
npm run test:e2e:diagnostics
tests/xsd/nist/nist-facets-engine.spec.ts reads precomputed case data from:
tests/xsd/nist/.cache/nist-engine-case-index.json
Generate/update this file with:
npm run build:nist-engine-indexUse a custom index path for experimentation by setting NIST_ENGINE_CASE_INDEX to an absolute or repo-relative JSON path.
- If W3C tests are missing assets, run
npm run fetch:w3c. - If NIST workflows are missing assets, run
npm run fetch:nist. - If
tests/xsd/nist/nist-facets-engine.spec.tsreports missing index data, runnpm run build:nist-engine-index. - If runtime output looks stale, rerun
npm run build:sef. - If docs builds fail, ensure
antis on PATH (or use--ant-binwithscripts/run-docbook-build.mjs).
See CONTRIBUTING.md for development workflow, validation expectations, and PR standards.
For complete machine/bootstrap setup (including external datasets and tools), see SETUP.md.
Saxon-Forms support maturity is tracked by conformance behavior and implementation coverage.
- Supported and stable: core controls, many processing model actions, and major portions of chapter-based W3C tests.
- Partial support: areas with known behavioral gaps or edge-case limitations.
- In progress: features tracked as active gaps in conformance status.
For current details:
tests/xforms/w3c/STATUS.mdfor live gap and resolution trackingdocs/docbook/for implementation architecture and behavior mapping
If you are evaluating feature readiness for production, use those files as the source of truth.
The repository now includes a DocBook-based implementation guide under docs/docbook/, with chapter-level checkpoints and standalone UML/BPMN references.
Prerequisite for DocBook builds:
-
Install Apache Ant and ensure
antis available on PATH (or provide a custom path via--ant-bininscripts/run-docbook-build.mjs). -
Validate DocBook sources, XInclude targets, and checkpoint metadata:
npm run docs:docbook:validate
-
Build DocBook HTML + PDF using the local
ant4docbook-0.10.0distribution:npm run docs:docbook:build
-
Build only one output format:
npm run docs:docbook:build -- --format=htmlnpm run docs:docbook:build -- --format=pdf
-
List checkpoint definitions and targeted chapter test commands:
npm run docs:docbook:checkpoints
Generated DocBook outputs are written to builds/docs-docbook/.
The repository supports an assembled static docs site artifact under builds/site/.
This includes:
- Curated markdown docs (allowlisted)
- DocBook implementation guide HTML output
- Published
examples/pages and required runtime assets
Build and validate locally:
npm run site:buildnpm run site:validate
The CI workflow .github/workflows/pages-docs.yml builds and validates this artifact on pull requests, and deploys to GitHub Pages on pushes to the default branch.
Note on examples in static hosting:
- Most examples render normally on Pages.
- Endpoint-backed submission demos (paths such as
/api/test,/api/echo,/api/book-submit) require the local examples server for full behavior (npm run examples).
The XForms digest() and hmac() functions require the
@noble/hashes library (MIT license).
These functions are optional — forms that do not
use digest() or hmac() work without it. When the library is not loaded,
these functions return an empty string instead of crashing.
-
Install the dependency:
npm install @noble/hashes
-
Build the crypto bundle:
node scripts/build-crypto.mjs
This produces
builds/saxon-forms-crypto.js(~14 KB, 6 KB gzipped). -
Load the bundle before Saxon-Forms in your HTML:
<script src="saxon-forms-crypto.js"></script> <!-- then load SaxonJS and your XForm as usual -->
If you prefer not to install locally, you can load the pre-built bundle from
your own CDN or copy builds/saxon-forms-crypto.js alongside your other assets.
If the crypto bundle is not loaded, digest() and hmac() return empty strings.
Forms that use these functions will render normally but any conditional logic
that depends on hash values (e.g. ref="self::node()[digest(...) = '...']")
will not match.