A SvelteKit static site offering three browser-side conversion tools, all powered by @correlaid/formtransform:
- XLSForm → LimeSurvey TSV — convert XLSForm questionnaires to the LimeSurvey TSV format.
- Kobo → DDI — emit DDI-Codebook 2.5 XML from an XLSForm. With a raw KoboToolbox CSV export (
,- or;-separated) added, it also emits a response-data CSV whose columns are the DDI variable names. - LimeSurvey → DDI — emit DDI-Codebook 2.5 XML from a LimeSurvey structure TSV.
The app is 100% client-side. No data leaves the browser.
FormTransform only covers the CDL survey ecosystem: forms using question types, appearances or expressions outside the supported subset are rejected, not converted.
# Install dependencies. The postinstall step copies the @correlaid/cdl-design
# favicons into static/.
bun install
# Copy the example env file and fill in values as needed
cp .env.example .env| Variable | Required | Description |
|---|---|---|
GITHUB_TOKEN |
No | GitHub personal access token. Avoids API rate limits (60 req/hour unauthenticated) when fetching content snippets during build. |
bun run dev
# or start the server and open the app in a new browser tab
bun run dev -- --openbun run buildPreview the production build locally:
bun run preview# Browser-mode unit tests
bun run test:unit
# Playwright e2e
bun run testThe Playwright suite has four parts:
tests/golden.spec.tsruns the library's own blessed fixtures through the GUI and compares the downloaded files with them. The fixtures come from the library release'sformtransform-fixtures-<version>.tar.gzasset.bun run testdownloads it first (scripts/fetch-fixtures.mjs), checks the sha256 pinned intests/formtransform-fixtures.jsonand unpacks it tonode_modules/.cache/. When you bump@correlaid/formtransform, update that pin too; the script refuses to run on a version mismatch. The fixtures cover every registered type (registry/entities/*: XLSForm → TSV, Kobo → DDI, LimeSurvey → DDI) and every whole-survey fixture (tests/fixtures/surveys/*).tests/gui.spec.tschecks UI behaviour: tab states, the Kobo mode switch, rejected forms, whether each option reaches the converter, keyboard use, and phone-width layout.tests/accessibility.spec.tsruns an axe-core WCAG 2.1 AA scan of every tab, in DE and EN, before and after a conversion.tests/main.spec.tsruns one upload smoke test per tab.
The dev server fetches the intro and liability snippets from the GitHub API when it starts. Without GITHUB_TOKEN you can hit the anonymous rate limit, and the imprint test then fails.
To diff the TSV tab's output against another version of the library, print it with scripts/snapshot-tsv.mjs (the second argument overrides the conversion config):
bun scripts/snapshot-tsv.mjs tests/fixtures/minimal.xlsx '{"hideNoAnswer":false}'The app is deployed via Coolify using nixpacks. The nixpacks.toml pins the bun version used in the build container.
The build output (build/) is fully static. serve.js is a tiny zero-dependency Node HTTP server used by the Coolify container; it serves build/. No special headers or WASM mime types are needed.