npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@longsightgroup/qti3-migrator

v0.13.3

Published

Framework-neutral QTI 1.2 and QTI 2.x to QTI 3 migration utilities.

Readme

@longsightgroup/qti3-migrator

Framework-neutral QTI 1.2 and QTI 2.x to QTI 3 migration utilities.

QTI 1.2 items with multiple responses are rejected with qti12_composite_responses_unsupported unless the mapper handles every response through the matching conversion. This prevents scoring only the first answer of a multi-answer condition. Nonempty QTI 2 matchGroup restrictions produce qti2_match_group_not_preserved; empty groups remain unrestricted. Both refusals apply with safe repair. Hotspot migration preserves surrounding instructions, region labels, and textual object descriptions. Graphic association and graphic gap targets also retain their region labels. See graphic-and-composite-fidelity.test.ts.

QTI 2 item language and the placement of surrounding content are retained when migrating block interactions. Hottext response cardinality is preserved independently of its selection limit. Missing response declarations or changed cardinality/base type produce qti2_response_type_not_preserved. Item-level modal feedback is not yet translated and produces qti2_modal_feedback_not_preserved. Both errors block a successful migration, including with safe repair; explicitly requested review stubs retain the errors. Regression coverage is in item-preservation-regressions.test.ts.

QTI 2.0's http://www.imsglobal.org/xsd/imsqti_v2p0 namespace is recognized alongside QTI 2.1 and 2.2 when translating content and checking scoring fidelity. Standard QTI 2.0 response-processing template URLs are recognized too; unsupported programs still produce preservation errors. qti20-fidelity.test.ts verifies choice structure, rubric translation, correct/incorrect/unanswered grades, and rejection of unpreserved scoring under both repair policies.

QTI 2 choice items may retain an explicit finite non-negative MAXSCORE default through the canonical choice writer. Import still requires exact structural processing and outcome fidelity: maximum metadata alone never authorizes replacing a custom program. choice-point-roundtrip.test.ts verifies fractional/zero grades and refusals of changed programs through standard QTI 2.1/2.2 and the Brightspace export profile.

The migrator reads legacy QTI package or item XML and produces QTI-shaped authoring models from @longsightgroup/qti3-writer. It does not own application-specific draft models or authoring UI review policy.

import { migrateQtiToQti3 } from "@longsightgroup/qti3-migrator";

const result = await migrateQtiToQti3({ filename: "quiz.zip", bytes });

Use migrateQtiToQti3Package for complete legacy QTI packages when the output should be passed directly to @longsightgroup/qti3-writer package APIs. Package migration preserves manifest item and asset paths from the source package:

import { migrateQtiToQti3Package } from "@longsightgroup/qti3-migrator";
import { writeQti3PackageZipResult } from "@longsightgroup/qti3-writer";

const migrated = await migrateQtiToQti3Package({ filename: "quiz.zip", bytes });

if (migrated.ok) {
  const qti3Package = writeQti3PackageZipResult(migrated.package);
  console.log(qti3Package);
}

Use migrateQtiResourceToQti3 when a host package, such as IMS Common Cartridge, has already identified one QTI resource and can provide that resource's local file closure. The result includes the underlying migration, launchable QTI 3 package entries, a launchHref, item hrefs, and diagnostics:

import { migrateQtiResourceToQti3 } from "@longsightgroup/qti3-migrator";

const migrated = await migrateQtiResourceToQti3({
  sourcePath: "assessment/quiz.xml",
  files: {
    "assessment/quiz.xml": quizXmlBytes,
    "assessment/map.png": mapPngBytes,
  },
});

if (migrated.ok) {
  console.log(migrated.title);
  console.log(migrated.launchHref);
  console.log(migrated.entries);
}

files is the local file closure for one IMS Common Cartridge QTI resource: keys are package-relative paths within that resource, backslashes are normalized to forward slashes, and sourcePath must match one of those keys after normalization. On success, entries is a standalone QTI 3 package file set rooted at imsmanifest.xml; the caller decides where to store or repackage those files.

QTI 1.2 XML resources that contain multiple <item> elements are emitted as stable sibling item paths so package output does not duplicate item paths while source-relative asset references keep the same base directory.

Resource migration attaches every non-XML file in the provided closure to each migrated item. The writer emits shared asset paths once when the referenced bytes are identical across items.

Defaults are strict: source repair is disabled and unsupported interactions are reported as diagnostics instead of silently converted.

QTI 2.x migration checks response defaults, mappings, and area mappings against the emitted item. Mappings that the authoring model cannot reproduce, including weighted choice/pair mappings or unsupported mapping defaults and bounds, return qti2_response_mapping_not_preserved instead of silently changing scores. Response defaults are not yet supported by the authoring model; their loss returns qti2_response_default_not_preserved. These errors suppress the migrated XML and authoring item, unless the caller explicitly requests a review stub. Safe source repair does not permit dropping these semantics.

Text-entry migration preserves string, integer, and float types; input constraints; explicit mapping weights and case sensitivity; and the correct response independently of its mapping weight. Match-correct text answers remain case-sensitive. Other response types and unrepresentable mapping defaults or bounds return diagnostics. defaultValue is never interpreted as correctResponse. Extended-text migration also preserves patternMask input constraints.

QTI 1.2 material preserves ordered text and image components. Plain text remains literal text; text/html material is accepted when it contains a well-formed XHTML fragment, including encoded or CDATA content. Malformed HTML, unsupported content types, and unsupported material components return qti12_material_not_preserved, including under safe repair.

QTI assessment-test structure preservation is not implemented yet. When a legacy package contains an assessment-test resource, migration returns a flat review part and reports assessment_test_structure_not_migrated.

QTI 1.2 choice and associate migration preserves render_choice shuffle and converts response_label rshuffle="No" to fixed choices. Canvas matching preserves compatible target-list shuffle settings and keeps source positions fixed. Conflicting shuffle or fixed settings across lists return qti12_canvas_match_shuffle_conflict, because QTI 3 uses one shared target set. A fixed label is never used to infer an answer key; missing keys follow the explicit repair policy.

An explicit shuffle attribute on legacy graphic gap match returns qti2_graphic_gap_shuffle_unsupported, including false values, rather than silently discarding it. QTI 3 graphic gap match does not define this attribute. Ordinary graphic gap match without this attribute continues to migrate.

Authored QTI 2 response processing is accepted when its canonical standard template or its inline expression tree is preserved by the writer. Changed inline rules (which override a coexisting template), unknown templates, and changed template semantics return qti2_response_processing_not_preserved. The migrator does not replace a supplied scoring program with an inferred answer-key program. Absent or empty processing is also checked: if the writer would introduce scoring, migration returns the same unsupported diagnostic. Outcome declarations, types, defaults, and metadata must be preserved; otherwise migration returns qti2_outcomes_not_preserved. Explicit numeric zero defaults are recognized as equivalent to implicit numeric zero defaults.

Nonzero matchMin on association choices, gap choices, and associable hotspots returns qti2_match_min_not_preserved because the authoring model cannot yet express it. These failures produce no converted item unless the caller explicitly requests a review stub. QTI 2 fixed accepts both XML Boolean spellings, including fixed="1" for a pinned choice.

QTI 1.2 scoring currently accepts a single positive conjunction that sets SCORE to 1 (or adds 1 once), with a zero default, over the supported single-response forms. Negation, alternative scoring conditions, weighted/additive scores, and unpreserved defaults return qti12_response_processing_unsupported, including under repairPolicy: "safe". For example, Canvas matching conditions that add 50 points per pair are rejected; they cannot be represented by the writer's all-or-nothing matching model.

String correct responses and text-entry mapping keys preserve significant whitespace.

QTI 2 migration rejects template declarations or template processing with qti2_template_not_preserved until those semantics can be preserved. It also rejects changes to correct responses with qti2_correct_response_not_preserved, including invented answer keys under safe repair. Hottext and gap-match migration preserve prompts, surrounding item-body content, and the original interaction position.

QTI 2 rubric blocks retain their view audiences when translated to qti-rubric-block. Adaptive QTI 2 items return qti2_adaptive_not_preserved, including under safe repair: the current authoring writer cannot preserve adaptive lifecycle semantics. No converted item is produced unless the caller explicitly requests a review stub.

QTI 2 rubric blocks become QTI 3 rubrics with a qti-content-body wrapper and use="instructions", reflecting QTI 2's definition of rubrics as instructions to the selected audiences. The source view remains unchanged; a scorer audience alone does not imply a scoring use.

Canonical inline-choice all-or-nothing matching can retain explicit finite non-negative point maxima through standard QTI 2.1/2.2 round trips. The parsed maximum is a candidate for the canonical writer. Actual processing and outcome metadata must still agree with the regenerated program before an item is emitted. Changed scoring or unmatched range metadata remains a refusal; maximum metadata alone never authorizes replacing a custom program.

Brightspace retains its configured dropdown-to-text-answer downgrade and loss receipt. A useful downgraded export does not establish a lossless dropdown re-import; the conservative importer refuses programs it cannot reproduce.