Exports and mappings
All exports run in the browser, in the backend's services/transformers/ folder. TransformationApplicationService is the facade; DocumentRecord exposes it per document as exportMarkdown(), exportHtml(), exportPdfBlob() and exportJsonLd(). In the standalone editor, src/standalone/utils/documentExport.ts turns the selected format into a file download.
| Format | Function | How |
|---|---|---|
| JSON | none | The stored document data as is (with its $schema). |
| Markdown | documentToMarkdown | Walks the document data and writes headings, lists and tables. Labels come from the profile (labelSingularEn, labelPluralEn). Ends with a SideDoc attribution footer. |
| HTML | documentToHtml | Converts the Markdown with marked, wraps it in a standalone HTML page with DEFAULT_DOCUMENT_EXPORT_CSS (or your own CSS). Links pass through safeHref. |
htmlToPdfBlob | Lays the HTML out as text (headings, paragraphs, bullets) with pdf-lib and the standard Helvetica fonts. URLs get link annotations. Characters outside WinAnsi are transliterated or replaced. | |
| JSON-LD | documentToJsonLd on the service | Applies a declarative RDF mapping. Today the only target is DCAT-AP 3.0. |
The toolbar offers JSON-LD only for profiles whose id starts with ds: (datasheet) or dcatap: (DCAT-AP dataset), the two sources that have a mapping. It is labelled experimental.
RDF mappings
The JSON-LD export is data driven. A mapping file says, per source field, where the value goes in the RDF output and how to convert it. The engine (transformers/rdf/rdf-mapping-engine.ts) is a generic interpreter that does not name any vocabulary itself; every RDF term comes from the mapping.
Where the files live
Mapping files are authored in the schemas repository, next to the profiles they map:
schemas/rdf-targets/dcat-ap/
├── datasheet-v2.dcat-ap.mapping.json datasheet (ds:) -> DCAT-AP
├── dcat-ap-dataset.dcat-ap.mapping.json dcat-ap-dataset (dcatap:) -> DCAT-AP
├── *.target-extension.json extension terms used by the mappings
└── *.md mapping notes, open SHACL issuesThe backend cannot read files at run time, so scripts/sync-dcat-mappings.mjs copies every *.dcat-ap.mapping.json to app/sidedoc-backend/src/lib/application/services/transformers/dcat/<name>.dcat-ap.mapping.generated.json. It checks that each file parses and fails the build if one does not. It runs as part of prebuild (npm run build:dcat-mappings runs it on its own) and needs SIDEDOC_SCHEMAS_DIR. The generated files are committed, so builds without a schemas checkout still have them. Do not edit the generated copies: change the file in the schemas repository and sync.
scripts/dcat-map.mjs runs the same engine from the command line, against the built dist/:
npm run build -w @sidedoc/backend
node app/sidedoc-backend/scripts/dcat-map.mjs <mapping.json> <document.json> <out.jsonld>The registry
transformers/rdf/rdf-mapping-registry.ts lists which (source profile, target model) pairs exist, in RDF_MAPPING_MATRIX. Each entry has the profile id prefixes it accepts (ds:, dcatap:), a target id (dcat-ap), a label and the imported mapping. findRdfMapping(profileId, target) returns the entry or null, and TransformationApplicationService.documentToJsonLd throws a clear error when there is none. availableRdfTargets(profileId) lists the targets for a profile.
To add a conversion: write the mapping file in the schemas repository, sync it, import the generated JSON in the registry and add one entry.
Mapping format
{
"targetModelId": "dcat-ap-v3-ddchd",
"sourceProfileId": "datasheet-v2",
"rootType": "dcat:Dataset",
"recordType": "dcat:CatalogRecord",
"recordTopicProp": "foaf:primaryTopic",
"vocab": { "label": "rdfs:label", "periodType": "dct:PeriodOfTime" },
"rules": [
{
"id": "ds:f00100",
"source": "#/title",
"target": "dataset.dcterms:title",
"cardinality": "1..*",
"onMissing": "error",
"extract": { "flatten": "unwrap", "as": "langLit" }
}
]
}rootType,recordType,recordTopicProp: types of the root node and of an optional record node (for statements about the datasheet itself), and the property that links them.context: optional JSON-LD@context. Without it the engine uses a default with the common DCAT-family prefixes (dcat,dct,foaf,prov,skos,adms,spdx,vcard, ...).vocab: logical names for RDF terms that the computed transforms use, so they contain no hard-coded vocabulary.rules: one per field, usually with the profile'sx-sidedocid asid.source: JSON pointer into the document (#/description/publisher).target:dataset.<property>orrecord.<property>.dataset.<container>[*].<property>with a#/<container>/*/<leaf>source is a sub-rule for each item of an array container (for example distributions); the container itself is a rule with acontainerblock.extract: the declarative conversion.flatten(unwrap,flatItems,toArr,first, ...) selects the items;as(langLit,text,plain,markdownLit,iri,termNode,termOrLiteral,periodObject,typedDate,urlNode, ...) converts each item.transform: one named computed transform for cases the DSL cannot express:checksumToSpdx,identifierToStructured,datesOrTermToTemporal,qualifiedRelationToDcat,agentOrRichTextToPublisher,agentToQualifiedAttribution,agentToVcardNode,sourceDataArrayToSourceStatements(KNOWN_TRANSFORM_NAMES).fallback: where values go that cannot be mapped to the main target (for example a free-text value next to a structured one).markdownTo: extra target for the Markdown version of a RichText value.action: "drop": an explicit "not exported" entry.cardinality,onMissing,note: documentation and checks.
validateMapping(mapping) checks that every rule has an id, source and target, that ids are unique and that named transforms exist. __tests__/unit/services/dcat-mapping-validation.test.ts runs it on the bundled mappings, and rdf-mapping-engine-coverage.test.ts and dcat-export.test.ts check the output against example datasheets from the schemas repository. SHACL validation of the output against DCAT-AP is done in the schemas repository (scripts/validate_dcatap.py).
The current state of the DCAT-AP alignment, and which mapping gaps are still open, is tracked in the mapping notes in rdf-targets/dcat-ap/.