Files
xslt-tools/README.md

8.1 KiB

XSLT tools

Browser-based MVP for testing XML/XSLT transformations. The application itself does not intentionally upload opened files, and it uses LocalStorage plus local file dialogs where available. XSLT is executable code, however: a trusted stylesheet may access browser or network resources, so processing is not described as guaranteed local-only.

The application is independently deployable and also implements Toolbox Contract v1. The shared top bar keeps the app title centered and provides Help, its Gitea source link, persistent System/Light/Dark personalization, and app selection. When launched with a same-origin ?toolbox=<catalog URL> context, the Apps menu includes the catalogue tools; without that context, the same build runs standalone.

Features in this MVP

  • Three-column workbench:
    • XML input
    • XSL transformation code
    • XML output
  • CodeMirror editors with:
    • line numbers
    • XML syntax highlighting
    • theme-aware light and dark editor, gutter, and highlighting palettes
    • editor undo/redo
    • cut/copy/paste helpers
    • find support via CodeMirror shortcuts
  • Transformation engines:
    • SaxonJS 2.7 as the default engine for XSLT 1.0/2.0/3.0-style workflows
    • Browser-native XSLTProcessor as a lightweight XSLT 1.0 fallback
  • A mandatory SHA-256 trust confirmation for the exact executable stylesheet; every edit, replacement, format, or reset revokes trust for both engines
  • Defense-in-depth input/output limits and a best-effort asynchronous deadline
  • XML and XSLT well-formedness checks
  • Basic XSLT root/version checks
  • Snippet toolbox for common XSLT constructs
  • Open/save local files
    • File System Access API when supported
    • file input/download fallback otherwise
  • Output → Input action for chained transformations
  • Confirmation dialog before destructive overwrites
  • LocalStorage persistence
  • Approximate “explain transformation” table after a run
  • Shared toolbox shell with standalone fallback, unified Help/source controls, personalization, and application switching
  • Relocatable static output for deployment at root or nested paths

SaxonJS note

SaxonJS 2.7 is vendored as the unmodified browser distribution file at public/vendor/saxon/SaxonJS2.js and loaded lazily when the SaxonJS engine runs. It is not bundled from the npm package during the application build.

SaxonJS is available free of charge but is not open source. Its separate redistribution terms are preserved at public/vendor/saxon/LICENSE.txt. Release archives include that notice as LICENSES/SaxonJS-LICENSE.txt alongside LICENSES/xslt-tools-LICENSE.txt for the AGPL-3.0-only application; do not describe the complete archive as AGPL-only.

Executable stylesheet security

Treat every stylesheet like executable code. Transformation is disabled until the user reviews a warning and trusts the SHA-256 fingerprint of the exact current stylesheet. Trust is session-only and tied to both the content hash and an edit revision; changing and then reverting text still requires another confirmation. The same gate covers SaxonJS and native XSLTProcessor.

The application enforces conservative input/output limits and a best-effort asynchronous deadline, but transformations are not isolated from the browser or network and synchronous CPU exhaustion cannot be forcibly stopped. A trusted SaxonJS stylesheet may read data stored by other Toolbox apps on the same site, including PDF workspaces and portal preferences, then send it over the network or navigate the page away.

When served with the recommended portal CSP, cross-origin fetches and dynamic JavaScript evaluation are restricted without granting unsafe-eval. CSP is defense in depth, not complete containment. Only execute stylesheets from trusted sources.

Run locally

npm install
npm run dev

Then open the local Vite URL shown in your terminal.

Build

npm run build
npm run preview

Vite uses base: './', so a single build can be hosted at an unknown nested path such as /apps/xslt/. The SaxonJS runtime is resolved from that application base as well.

Toolbox manifest

src/toolboxApp.ts is the typed source for both the shared shell and the published public/toolbox-app.json manifest. Keep the generated file current with:

npm run manifest:generate
npm run manifest:check

Toolbox mode is contextual rather than a separate build:

/apps/xslt/                                      standalone
/apps/xslt/?toolbox=/toolbox.catalog.json       toolbox mode

An unavailable or invalid catalogue falls back to standalone operation.

Appearance

The stable shared top bar keeps Personalize, Apps, the XSLT source link, and Help in a fixed left cluster, the app title and one tagline centered, and the Toolbox icon with the add·ideas Toolbox label in a fixed far-right block. Use Personalize to select System, Light, or Dark. The choice is stored only in this browser using the same preference record as the Toolbox Portal, so it follows you between current Toolbox apps on the same origin. System mode follows the operating-system color preference. CodeMirror is reconfigured when the effective palette changes, including its editor background, gutters, active line, and XML syntax colors.

Release archive

After a successful build, create the deterministic application ZIP and SHA-256 sidecar with:

npm run release

The command refuses to overwrite a same-version archive. Use npm run release -- --force only when intentionally replacing it.

The archive contains the static app and toolbox-app.json, plus CHANGELOG.md, SOURCE.md with the corresponding tagged source link, THIRD_PARTY_NOTICES.md, the application licence at LICENSES/xslt-tools-LICENSE.txt, the separate vendored SaxonJS licence, and licence/notice files for every installed production npm dependency below LICENSES/npm/. Generated artifacts are written below release/ and are not committed.

Suggested next steps

  1. Add parameter handling for xsl:param.
  2. Add a real diagnostics/lint bridge into CodeMirror gutter markers.
  3. Add transformation test cases with expected output assertions.
  4. Add project import/export as a single .xslt-tools.json file.
  5. Improve explain mode through stylesheet instrumentation.
  6. Add optional precompiled SEF import/export support for larger SaxonJS projects.

Structure

src/
├── components/     # Toolbar, editor panels, dialogs, and app-specific actions
├── editor/         # CodeMirror integration and XSLT snippets
├── file/           # Local file open/save helpers
├── transform/      # Transform engine abstraction, SaxonJS, native engine
├── validation/     # XML/XSLT validation helpers
├── workspace/      # LocalStorage-backed workbench state
└── toolboxApp.ts   # Toolbox manifest and shared-shell application definition

Development checks

Use Node.js 22 or newer. The main local quality gate is:

npm run check

Useful individual commands:

npm run typecheck      # TypeScript only
npm run lint           # ESLint
npm run lint:fix       # ESLint autofix where possible
npm run format:check   # Prettier check
npm run format         # Prettier write
npm run test           # Vitest/jsdom test suite
npm run build          # Production build
npm run smoke:static   # Nested-path static build smoke test
npm run smoke:toolbox  # Contract, asset, standalone, and contextual checks
npm run release        # Deterministic ZIP and SHA-256 release artifacts
npm run clean          # Remove generated build/cache files

The vendored SaxonJS browser file is intentionally excluded from ESLint and Prettier because it is a third-party distribution artifact.

Transformation engines

The app currently exposes two engines:

  • SaxonJS 2 dynamic XSLT 3.0: default engine. It loads the vendored public/vendor/saxon/SaxonJS2.js, compiles raw XSLT text to SEF in the browser, then executes the generated SEF after explicit trust confirmation.
  • Native browser XSLTProcessor: XSLT 1.0 fallback using the browser implementation.

The SaxonJS runtime is loaded only for a trusted Saxon transformation, so the initial app shell does not block on the large compiler/runtime file.