feat: integrate XSLT Tools with toolbox portal

This commit is contained in:
2026-07-20 18:16:18 +02:00
parent 3193cc395c
commit 8f8a59e76c
36 changed files with 1737 additions and 243 deletions

View File

@@ -1,6 +1,12 @@
# XSLT tools
Browser-only MVP for testing local XML/XSLT transformations. Nothing is uploaded; the app uses browser APIs, LocalStorage, and local file open/save dialogs where available.
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. When launched with a same-origin `?toolbox=<catalog URL>` context, the shared application shell adds toolbox navigation and an application switcher; without that context, the same build runs standalone.
## Features in this MVP
@@ -17,6 +23,9 @@ Browser-only MVP for testing local XML/XSLT transformations. Nothing is uploaded
- 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
@@ -27,12 +36,34 @@ Browser-only MVP for testing local XML/XSLT transformations. Nothing is uploaded
- Confirmation dialog before destructive overwrites
- LocalStorage persistence
- Approximate “explain transformation” table after a run
- Shared toolbox shell with standalone fallback, application switching, and the XSLT-specific Help action
- Relocatable static output for deployment at root or nested paths
## SaxonJS note
SaxonJS is bundled through the `saxon-js` npm package and loaded lazily when the SaxonJS engine runs. The browser build may show Vite warnings about Node modules such as `fs`, `path`, or `stream` being externalized. Those warnings come from the package containing Node.js support as well; the app does not use file-based SaxonJS APIs in the browser.
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 itself is free to use but not open source. Check `node_modules/saxon-js/LICENSE.txt` after installation before distributing a packaged version.
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
@@ -50,6 +81,45 @@ 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:
```bash
npm run manifest:generate
npm run manifest:check
```
Toolbox mode is contextual rather than a separate build:
```text
/apps/xslt/ standalone
/apps/xslt/?toolbox=/toolbox.catalog.json toolbox mode
```
An unavailable or invalid catalogue falls back to standalone operation.
## Release archive
After a successful build, create the deterministic application ZIP and SHA-256 sidecar with:
```bash
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`.
@@ -63,12 +133,13 @@ npm run preview
```text
src/
├── components/ # Layout, toolbar, editor panels, dialogs
├── 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
── workspace/ # LocalStorage-backed workbench state
└── toolboxApp.ts # Toolbox manifest and shared-shell application definition
```
## Development checks
@@ -89,6 +160,9 @@ 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
```
@@ -98,7 +172,8 @@ The vendored SaxonJS browser file is intentionally excluded from ESLint and Pret
The app currently exposes two engines:
- **SaxonJS 2 dynamic XSLT 3.0**: default engine. It lazy-loads `public/vendor/saxon/SaxonJS2.js`, compiles raw XSLT text to SEF in the browser, then executes the generated SEF locally.
- **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 when a Saxon transformation is requested, so the initial app shell does not block on the large compiler/runtime file.
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.