npm.io
0.4.0 • Published 3d ago

@timeback/qti

Licence
Version
0.4.0
Deps
2
Size
286 kB
Vulns
0
Weekly
0

@timeback/qti

TypeScript client for the QTI 3.0 assessment API.

Installation

bun add @timeback/qti

Quick Start

import { QtiClient } from '@timeback/qti'

const client = new QtiClient({
	env: 'staging',
	auth: { clientId: '...', clientSecret: '...' },
})

const { items } = await client.assessmentItems.list({ query: 'fractions', limit: 20 })
const { items: stimuli } = await client.stimuli.list({ query: 'fractions', limit: 20 })
const item = await client.assessmentItems.get('item-123')

Resources

Namespace Methods
client.assessmentItems List, get, create, update, delete assessment items
client.assessmentTests List, get, create, update, delete assessment tests; fetch questions
client.stimuli List, get, create, update, delete stimuli (passages)
client.validate Validate QTI XML against schema (single and batch)
client.lesson Lesson feedback operations
client.general General/utility endpoints

Filtering

The top-level assessment-item and assessment-test lists accept the same type-safe where clause as the other Timeback clients, compiled to the server-side filter expression:

// Exact match
const drafts = await client.assessmentTests.list({
	where: { title: 'Unit 5 Final' },
})

// Substring/regex match (case-insensitive)
const mine = await client.assessmentItems.list({
	where: { identifier: { contains: '^playcademy-test-' } },
})

// Streams filter server-side too
for await (const test of client.assessmentTests.stream({
	where: { title: { contains: 'math' } },
})) {
	console.log(test.identifier)
}

Filterable fields are curated per entity (AssessmentItemFilterFields, AssessmentTestFilterFields from @timeback/types). Nested lists (test parts, sections, stimuli) do not support filtering.

Configuration

The client supports three configuration modes:

// Environment mode — resolves URLs automatically
new QtiClient({ env: 'staging', auth: { clientId, clientSecret } })

// Explicit mode — custom API endpoint
new QtiClient({ baseUrl: 'https://qti.example.com/api', auth: { clientId, clientSecret, authUrl } })

// Environment variables fallback
// QTI_BASE_URL, QTI_TOKEN_URL, QTI_CLIENT_ID, QTI_CLIENT_SECRET
new QtiClient()

Parsing QTI XML

@timeback/qti/parse provides dependency-free utilities for extracting structured data from QTI 3.0 XML. Works in any runtime (browser, Node, Bun).

import { parseQtiXml } from '@timeback/qti/parse'

const item = parseQtiXml(xml)
item.prompt // "What is 2 + 2?"
item.choices // [{ identifier: "A", text: "3" }, { identifier: "B", text: "4" }]
item.correctAnswer // "B"
item.feedback // inline feedback per choice
item.modalFeedback // modal feedback (correct/incorrect)

Individual extractors are also available for fine-grained control:

import { extractChoices, extractCorrectResponse, extractPrompt } from '@timeback/qti/parse'

const prompt = extractPrompt(xml)
const choices = extractChoices(xml)
const answer = extractCorrectResponse(xml)

Additional Exports

Entry point Purpose
@timeback/qti/parse QTI XML parsing (see above)
@timeback/qti/errors Error classes: QtiError, NotFoundError, ValidationError, etc.
@timeback/qti/types All public TypeScript types