QTI Item Roundtrip
@citolab/prose-qti/item-roundtrip bundles the editor’s item import and export bridge. Use it when you want to move a single assessment item between QTI XML and ProseMirror without wiring the transform chain together yourself.
When to use this
Use this package when you need to:
- import a QTI 3.0 item XML string or URL into an editor document
- export a ProseMirror document back to a complete QTI assessment item
- keep the import transform chain, schema parsing, and export composer in one place
What it provides
The package exposes a small set of convenience helpers:
importItemFromStringimportItemFromUrlimportItemXmlDocitemBodyAndGapsFromString/itemBodyAndGapsFromUrl(see Scoring gaps below)exportItemXmlexportItemXmlDoc
It also exports the roundtrip transform and export option types so callers can type their integration code precisely, plus the prompt-handling helper stripEmptyPrompts (see Prompt handling below).
Typical usage
Import an item from a URL:
import { importItemFromUrl } from '@citolab/prose-qti/item-roundtrip';
const node = await importItemFromUrl('https://example.com/items/item-1.xml', schema);Export the current editor document:
import { exportItemXml } from '@citolab/prose-qti/item-roundtrip';
const xml = exportItemXml(editor.state.doc, schema, { identifier: 'item-1', title: 'Sample Item',});Prompt handling
qti-prompt is optional on block interactions, in QTI 3.0 and in this package’s schemas alike, so
importing needs no transform for it. An item that omits a prompt parses as an interaction without
one.
This used to say the opposite. The schemas required a leading qti-prompt — so that authors always
saw a slot to fill in — and a pair of transforms bridged the gap: ensureInteractionPrompts injected
an empty prompt on import, stripEmptyPrompts took it out again on export. The requirement was the
bug: synthesising a prompt made a valid QTI item import as a document containing an element its
author never wrote, which then exported back out as real markup. The roundtrip added content. Every
interaction’s content expression now leads with qtiPrompt? and ensureInteractionPrompts is gone.
- On export,
stripEmptyPromptsremains, with a smaller and no longer symmetrical job: it removes any<qti-prompt>whose text content is empty or whitespace-only before the item-body is wrapped in<qti-assessment-item>. Sinceqti-promptis0..1in QTI 3.0, dropping an empty one is lossless. It now cleans up after an author who left a prompt blank, rather than after us. It is part ofdefaultRoundtripExportTransformsand runs automatically; pass{ transforms: [] }to disable it, or your own list to customize.
Scoring gaps
The editor’s ProseMirror schema only models three response-processing templates —
match_correct, map_response, map_response_point. An item scored any other way still imports
and exports cleanly, but is worth zero marks in the editor, with nothing to say so unless you ask.
itemBodyAndGapsFromString and itemBodyAndGapsFromUrl are the way to ask. They run the same
import pipeline as importItemFromString / importItemFromUrl, but return both the item body and
what the item’s response processing could not be represented as:
import { itemBodyAndGapsFromString } from '@citolab/prose-qti/item-roundtrip';import { findUnrepresentableElements, TRANSPARENT_WRAPPER_TAGS } from '@citolab/prose-extensions/schema-gaps';
const { itemBody, scoringGaps } = itemBodyAndGapsFromString(xml);const elementGaps = findUnrepresentableElements(schema, itemBody.documentElement, { ignoreTags: TRANSPARENT_WRAPPER_TAGS,});scoringGaps is a SchemaGapOutcome from Schema Gaps —
the same shape findUnrepresentableElements returns, so both can feed one notice. Silence is the
expected result for items scored by the three standard models; a notice that fired on every import
would teach readers to dismiss the one time it matters.
Notes
- The caller still supplies the ProseMirror schema, because the editor schema is application-specific.
- Import uses the standard QTI 3.0 roundtrip transforms before parsing into ProseMirror.
- Export produces the same canonical item-body and full assessment-item XML used by the editor’s save flow.