@longsightgroup/qti3-core 0.9.3 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +88 -8
  2. package/dist/adaptive-turn-materializer.d.ts +2 -0
  3. package/dist/adaptive-turn-materializer.d.ts.map +1 -1
  4. package/dist/adaptive-turn-materializer.js +25 -19
  5. package/dist/adaptive-turn-materializer.js.map +1 -1
  6. package/dist/adaptive-turn.d.ts +8 -1
  7. package/dist/adaptive-turn.d.ts.map +1 -1
  8. package/dist/adaptive-turn.js +15 -5
  9. package/dist/adaptive-turn.js.map +1 -1
  10. package/dist/delivery-preparation.d.ts +20 -0
  11. package/dist/delivery-preparation.d.ts.map +1 -0
  12. package/dist/delivery-preparation.js +71 -0
  13. package/dist/delivery-preparation.js.map +1 -0
  14. package/dist/delivery-redaction.d.ts +5 -0
  15. package/dist/delivery-redaction.d.ts.map +1 -1
  16. package/dist/delivery-redaction.js +26 -5
  17. package/dist/delivery-redaction.js.map +1 -1
  18. package/dist/delivery-security.js +2 -2
  19. package/dist/delivery-security.js.map +1 -1
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +6 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/item-scoring-expectation.d.ts +4 -0
  25. package/dist/item-scoring-expectation.d.ts.map +1 -0
  26. package/dist/item-scoring-expectation.js +6 -0
  27. package/dist/item-scoring-expectation.js.map +1 -0
  28. package/dist/item-submission-materialization.d.ts +25 -0
  29. package/dist/item-submission-materialization.d.ts.map +1 -0
  30. package/dist/item-submission-materialization.js +68 -0
  31. package/dist/item-submission-materialization.js.map +1 -0
  32. package/dist/qti-package-assets.d.ts +8 -0
  33. package/dist/qti-package-assets.d.ts.map +1 -0
  34. package/dist/qti-package-assets.js +173 -0
  35. package/dist/qti-package-assets.js.map +1 -0
  36. package/dist/qti-package-items.d.ts +14 -0
  37. package/dist/qti-package-items.d.ts.map +1 -0
  38. package/dist/qti-package-items.js +138 -0
  39. package/dist/qti-package-items.js.map +1 -0
  40. package/dist/qti-package-manifest.d.ts +13 -0
  41. package/dist/qti-package-manifest.d.ts.map +1 -0
  42. package/dist/qti-package-manifest.js +109 -0
  43. package/dist/qti-package-manifest.js.map +1 -0
  44. package/dist/qti-package-metadata.d.ts +8 -0
  45. package/dist/qti-package-metadata.d.ts.map +1 -0
  46. package/dist/qti-package-metadata.js +115 -0
  47. package/dist/qti-package-metadata.js.map +1 -0
  48. package/dist/qti-package-paths.d.ts +6 -0
  49. package/dist/qti-package-paths.d.ts.map +1 -0
  50. package/dist/qti-package-paths.js +32 -0
  51. package/dist/qti-package-paths.js.map +1 -0
  52. package/dist/qti-package-types.d.ts +98 -0
  53. package/dist/qti-package-types.d.ts.map +1 -0
  54. package/dist/qti-package-types.js +2 -0
  55. package/dist/qti-package-types.js.map +1 -0
  56. package/dist/qti-package-xml.d.ts +21 -0
  57. package/dist/qti-package-xml.d.ts.map +1 -0
  58. package/dist/qti-package-xml.js +68 -0
  59. package/dist/qti-package-xml.js.map +1 -0
  60. package/dist/qti-package-zip.d.ts +24 -0
  61. package/dist/qti-package-zip.d.ts.map +1 -0
  62. package/dist/qti-package-zip.js +185 -0
  63. package/dist/qti-package-zip.js.map +1 -0
  64. package/dist/qti-package.d.ts +7 -0
  65. package/dist/qti-package.d.ts.map +1 -0
  66. package/dist/qti-package.js +76 -0
  67. package/dist/qti-package.js.map +1 -0
  68. package/dist/response-input.d.ts +9 -0
  69. package/dist/response-input.d.ts.map +1 -0
  70. package/dist/response-input.js +9 -0
  71. package/dist/response-input.js.map +1 -0
  72. package/dist/response-validation-policy.d.ts +31 -0
  73. package/dist/response-validation-policy.d.ts.map +1 -0
  74. package/dist/response-validation-policy.js +166 -0
  75. package/dist/response-validation-policy.js.map +1 -0
  76. package/dist/response-validation.d.ts +23 -0
  77. package/dist/response-validation.d.ts.map +1 -0
  78. package/dist/response-validation.js +170 -0
  79. package/dist/response-validation.js.map +1 -0
  80. package/dist/scoring-disposition-policy.d.ts +8 -0
  81. package/dist/scoring-disposition-policy.d.ts.map +1 -0
  82. package/dist/scoring-disposition-policy.js +24 -0
  83. package/dist/scoring-disposition-policy.js.map +1 -0
  84. package/dist/template-presentation-materializer.d.ts +14 -0
  85. package/dist/template-presentation-materializer.d.ts.map +1 -0
  86. package/dist/template-presentation-materializer.js +143 -0
  87. package/dist/template-presentation-materializer.js.map +1 -0
  88. package/dist/trusted-item-session.d.ts +25 -8
  89. package/dist/trusted-item-session.d.ts.map +1 -1
  90. package/dist/trusted-item-session.js +51 -46
  91. package/dist/trusted-item-session.js.map +1 -1
  92. package/dist/trusted-session-materialization.d.ts +33 -0
  93. package/dist/trusted-session-materialization.d.ts.map +1 -0
  94. package/dist/trusted-session-materialization.js +26 -0
  95. package/dist/trusted-session-materialization.js.map +1 -0
  96. package/dist/validation-interactions.js +1 -0
  97. package/dist/validation-interactions.js.map +1 -1
  98. package/dist/validation-primitives.d.ts +1 -0
  99. package/dist/validation-primitives.d.ts.map +1 -1
  100. package/dist/validation-primitives.js +5 -0
  101. package/dist/validation-primitives.js.map +1 -1
  102. package/dist/variable-snapshot.d.ts +9 -0
  103. package/dist/variable-snapshot.d.ts.map +1 -0
  104. package/dist/variable-snapshot.js +9 -0
  105. package/dist/variable-snapshot.js.map +1 -0
  106. package/package.json +1 -1
  107. package/src/adaptive-turn-materializer.ts +29 -16
  108. package/src/adaptive-turn.ts +29 -6
  109. package/src/delivery-preparation.ts +121 -0
  110. package/src/delivery-redaction.ts +36 -5
  111. package/src/delivery-security.ts +2 -2
  112. package/src/index.ts +65 -0
  113. package/src/item-scoring-expectation.ts +9 -0
  114. package/src/item-submission-materialization.ts +120 -0
  115. package/src/qti-package-assets.ts +256 -0
  116. package/src/qti-package-items.ts +240 -0
  117. package/src/qti-package-manifest.ts +210 -0
  118. package/src/qti-package-metadata.ts +143 -0
  119. package/src/qti-package-paths.ts +56 -0
  120. package/src/qti-package-types.ts +117 -0
  121. package/src/qti-package-xml.ts +97 -0
  122. package/src/qti-package-zip.ts +325 -0
  123. package/src/qti-package.ts +176 -0
  124. package/src/response-input.ts +19 -0
  125. package/src/response-validation-policy.ts +226 -0
  126. package/src/response-validation.ts +298 -0
  127. package/src/scoring-disposition-policy.ts +37 -0
  128. package/src/template-presentation-materializer.ts +219 -0
  129. package/src/trusted-item-session.ts +80 -74
  130. package/src/trusted-session-materialization.ts +67 -0
  131. package/src/validation-interactions.ts +1 -0
  132. package/src/validation-primitives.ts +5 -0
  133. package/src/variable-snapshot.ts +20 -0
@@ -0,0 +1,185 @@
1
+ import { normalizePackagePath, pushPackageDiagnostic } from "./qti-package-paths.js";
2
+ /** Read stored ZIP entries and optionally inflate raw DEFLATE entries through caller code. */
3
+ export function readQtiPackageZipEntries(bytes, options, diagnostics) {
4
+ try {
5
+ return readZipEntriesUnsafe(bytes, options, diagnostics);
6
+ }
7
+ catch {
8
+ pushPackageDiagnostic(diagnostics, "package.zip.invalid", "error", "QTI package ZIP central directory is malformed.");
9
+ return [];
10
+ }
11
+ }
12
+ // Intentionally manual: core's TS lib is ES2023-only (no DOM/Node globals).
13
+ // TextDecoder is available on all supported runtimes, but using it would require
14
+ // widening lib, ambient types, or globalThis casts in this zero-runtime-dependency package.
15
+ /** Decode UTF-8 package entry names and XML payloads. */
16
+ export function decodeUtf8(bytes) {
17
+ const input = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf ? bytes.subarray(3) : bytes;
18
+ let text = "";
19
+ for (let index = 0; index < input.length;) {
20
+ const first = input[index];
21
+ if (first === undefined)
22
+ break;
23
+ if (first < 0x80) {
24
+ text += String.fromCharCode(first);
25
+ index += 1;
26
+ continue;
27
+ }
28
+ const decoded = decodeUtf8CodePoint(input, index);
29
+ text += String.fromCodePoint(decoded.codePoint);
30
+ index += decoded.length;
31
+ }
32
+ return text;
33
+ }
34
+ function readZipEntriesUnsafe(bytes, options, diagnostics) {
35
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
36
+ const eocdOffset = findEndOfCentralDirectory(view);
37
+ if (eocdOffset < 0) {
38
+ pushPackageDiagnostic(diagnostics, "package.zip.centralDirectory.missing", "error", "No ZIP central directory was found.");
39
+ return [];
40
+ }
41
+ const entryCount = view.getUint16(eocdOffset + 10, true);
42
+ let offset = view.getUint32(eocdOffset + 16, true);
43
+ const entries = [];
44
+ for (let index = 0; index < entryCount; index += 1) {
45
+ if (offset + 46 > view.byteLength) {
46
+ pushPackageDiagnostic(diagnostics, "package.zip.centralDirectory.truncated", "error", "ZIP central directory ended before all entries could be read.");
47
+ break;
48
+ }
49
+ if (view.getUint32(offset, true) !== 0x02014b50) {
50
+ pushPackageDiagnostic(diagnostics, "package.zip.centralDirectory.entry", "error", "ZIP central directory entry header is invalid.");
51
+ break;
52
+ }
53
+ const compressionMethod = view.getUint16(offset + 10, true);
54
+ const compressedSize = view.getUint32(offset + 20, true);
55
+ const uncompressedSize = view.getUint32(offset + 24, true);
56
+ const nameLength = view.getUint16(offset + 28, true);
57
+ const extraLength = view.getUint16(offset + 30, true);
58
+ const commentLength = view.getUint16(offset + 32, true);
59
+ const localHeaderOffset = view.getUint32(offset + 42, true);
60
+ const nameStart = offset + 46;
61
+ const nameEnd = nameStart + nameLength;
62
+ if (nameEnd > view.byteLength) {
63
+ pushPackageDiagnostic(diagnostics, "package.zip.centralDirectory.name", "error", "ZIP central directory entry name is truncated.");
64
+ break;
65
+ }
66
+ const rawName = bytes.slice(nameStart, nameEnd);
67
+ const decodedName = decodeUtf8(rawName).replaceAll("\\", "/");
68
+ offset += 46 + nameLength + extraLength + commentLength;
69
+ if (decodedName.endsWith("/"))
70
+ continue;
71
+ const path = normalizePackagePath(decodedName, "ZIP entry", diagnostics);
72
+ if (!path)
73
+ continue;
74
+ const content = zipEntryBytes(bytes, view, {
75
+ path,
76
+ compressedSize,
77
+ uncompressedSize,
78
+ compressionMethod,
79
+ localHeaderOffset,
80
+ }, options, diagnostics);
81
+ if (content)
82
+ entries.push({ path, bytes: content });
83
+ }
84
+ return entries;
85
+ }
86
+ function zipEntryBytes(bytes, view, entry, options, diagnostics) {
87
+ if (entry.localHeaderOffset + 30 > view.byteLength) {
88
+ pushPackageDiagnostic(diagnostics, "package.zip.localHeader.truncated", "error", `ZIP local header for ${entry.path} is truncated.`, entry.path);
89
+ return undefined;
90
+ }
91
+ if (view.getUint32(entry.localHeaderOffset, true) !== 0x04034b50) {
92
+ pushPackageDiagnostic(diagnostics, "package.zip.localHeader", "error", `ZIP local header for ${entry.path} is invalid.`, entry.path);
93
+ return undefined;
94
+ }
95
+ const nameLength = view.getUint16(entry.localHeaderOffset + 26, true);
96
+ const extraLength = view.getUint16(entry.localHeaderOffset + 28, true);
97
+ const dataOffset = entry.localHeaderOffset + 30 + nameLength + extraLength;
98
+ const dataEnd = dataOffset + entry.compressedSize;
99
+ if (dataEnd > view.byteLength) {
100
+ pushPackageDiagnostic(diagnostics, "package.zip.entry.truncated", "error", `ZIP entry ${entry.path} content is truncated.`, entry.path);
101
+ return undefined;
102
+ }
103
+ const compressed = bytes.slice(dataOffset, dataEnd);
104
+ if (entry.compressionMethod === 0)
105
+ return compressed;
106
+ if (entry.compressionMethod === 8) {
107
+ const inflateRaw = options.inflateRaw;
108
+ if (!inflateRaw) {
109
+ pushPackageDiagnostic(diagnostics, "package.zip.entry.compression.unsupported", "error", `ZIP entry ${entry.path} uses DEFLATE compression, but no inflateRaw option was provided.`, entry.path);
110
+ return undefined;
111
+ }
112
+ try {
113
+ return inflateRaw(compressed, {
114
+ path: entry.path,
115
+ compressedSize: entry.compressedSize,
116
+ uncompressedSize: entry.uncompressedSize,
117
+ compressionMethod: entry.compressionMethod,
118
+ });
119
+ }
120
+ catch {
121
+ pushPackageDiagnostic(diagnostics, "package.zip.entry.inflate", "error", `ZIP entry ${entry.path} could not be inflated.`, entry.path);
122
+ return undefined;
123
+ }
124
+ }
125
+ pushPackageDiagnostic(diagnostics, "package.zip.entry.compression.unsupported", "error", `ZIP entry ${entry.path} uses unsupported compression method ${entry.compressionMethod}.`, entry.path);
126
+ return undefined;
127
+ }
128
+ function findEndOfCentralDirectory(view) {
129
+ const minimumOffset = Math.max(0, view.byteLength - 65557);
130
+ for (let offset = view.byteLength - 22; offset >= minimumOffset; offset -= 1) {
131
+ if (view.getUint32(offset, true) === 0x06054b50)
132
+ return offset;
133
+ }
134
+ return -1;
135
+ }
136
+ function decodeUtf8CodePoint(bytes, index) {
137
+ const first = bytes[index];
138
+ if (first === undefined)
139
+ return { codePoint: 0xfffd, length: 1 };
140
+ if (first >= 0xc2 && first <= 0xdf) {
141
+ const second = bytes[index + 1];
142
+ if (isUtf8Continuation(second)) {
143
+ return {
144
+ codePoint: ((first & 0x1f) << 6) | (second & 0x3f),
145
+ length: 2,
146
+ };
147
+ }
148
+ }
149
+ if (first >= 0xe0 && first <= 0xef) {
150
+ const second = bytes[index + 1];
151
+ const third = bytes[index + 2];
152
+ if (isUtf8Continuation(second) &&
153
+ isUtf8Continuation(third) &&
154
+ !(first === 0xe0 && second < 0xa0) &&
155
+ !(first === 0xed && second >= 0xa0)) {
156
+ return {
157
+ codePoint: ((first & 0x0f) << 12) | ((second & 0x3f) << 6) | (third & 0x3f),
158
+ length: 3,
159
+ };
160
+ }
161
+ }
162
+ if (first >= 0xf0 && first <= 0xf4) {
163
+ const second = bytes[index + 1];
164
+ const third = bytes[index + 2];
165
+ const fourth = bytes[index + 3];
166
+ if (isUtf8Continuation(second) &&
167
+ isUtf8Continuation(third) &&
168
+ isUtf8Continuation(fourth) &&
169
+ !(first === 0xf0 && second < 0x90) &&
170
+ !(first === 0xf4 && second >= 0x90)) {
171
+ return {
172
+ codePoint: ((first & 0x07) << 18) |
173
+ ((second & 0x3f) << 12) |
174
+ ((third & 0x3f) << 6) |
175
+ (fourth & 0x3f),
176
+ length: 4,
177
+ };
178
+ }
179
+ }
180
+ return { codePoint: 0xfffd, length: 1 };
181
+ }
182
+ function isUtf8Continuation(value) {
183
+ return value !== undefined && value >= 0x80 && value <= 0xbf;
184
+ }
185
+ //# sourceMappingURL=qti-package-zip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qti-package-zip.js","sourceRoot":"","sources":["../src/qti-package-zip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { QtiPackageParseResult } from "./qti-package-types.js";
2
+ import { type QtiPackageParseOptions } from "./qti-package-zip.js";
3
+ export type { QtiAssessmentTestItemRef, QtiAssessmentTestPackageModel, QtiManifestFile, QtiManifestResource, QtiPackageAsset, QtiPackageAssetSource, QtiPackageItem, QtiPackageItemSource, QtiPackageParseResult, QtiPackageShape, QtiStandardAlignment, QtiTimingMetadata, } from "./qti-package-types.js";
4
+ export type { QtiPackageInflateContext, QtiPackageInflateRaw, QtiPackageParseOptions, } from "./qti-package-zip.js";
5
+ /** Parse a QTI ZIP package into a neutral manifest, item, asset, and diagnostic model. */
6
+ export declare function parseQtiPackage(bytes: Uint8Array, options?: QtiPackageParseOptions): QtiPackageParseResult;
7
+ //# sourceMappingURL=qti-package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qti-package.d.ts","sourceRoot":"","sources":["../src/qti-package.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EACV,wBAAwB,EACxB,6BAA6B,EAC7B,eAAe,EACf,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,0FAA0F;AAC1F,wBAAgB,eAAe,CAC7B,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,sBAA2B,GACnC,qBAAqB,CAIvB"}
@@ -0,0 +1,76 @@
1
+ import { pushPackageDiagnostic } from "./qti-package-paths.js";
2
+ import { collectPackageAssets } from "./qti-package-assets.js";
3
+ import { detectPackageShape, diagnoseManifestDependencyReferences, diagnosePrimaryResourceHrefs, indexManifestResources, isQtiAssessmentTestResource, isQtiItemResource, parseManifestResources, QTI_PACKAGE_MANIFEST_PATH, } from "./qti-package-manifest.js";
4
+ import { itemReferencesForPackageShape, parseAssessmentTestPackageModel, parsePackageItems, } from "./qti-package-items.js";
5
+ import { packageTitle, parseStandardAlignments, primaryTiming, uniqueStandards, } from "./qti-package-metadata.js";
6
+ import { parseXmlFiles, pushXmlDiagnostics } from "./qti-package-xml.js";
7
+ import { readQtiPackageZipEntries, } from "./qti-package-zip.js";
8
+ /** Parse a QTI ZIP package into a neutral manifest, item, asset, and diagnostic model. */
9
+ export function parseQtiPackage(bytes, options = {}) {
10
+ const diagnostics = [];
11
+ const entries = readQtiPackageZipEntries(bytes, options, diagnostics);
12
+ return parseQtiPackageEntries(entries, diagnostics);
13
+ }
14
+ function parseQtiPackageEntries(entries, diagnostics) {
15
+ const entriesByPath = indexEntries(entries, diagnostics);
16
+ const xmlFilesByPath = parseXmlFiles(entriesByPath);
17
+ const manifestXml = xmlFilesByPath.get(QTI_PACKAGE_MANIFEST_PATH);
18
+ if (!manifestXml) {
19
+ pushPackageDiagnostic(diagnostics, "package.manifest.missing", "error", "QTI package does not contain imsmanifest.xml.", QTI_PACKAGE_MANIFEST_PATH);
20
+ }
21
+ else {
22
+ pushXmlDiagnostics(manifestXml, diagnostics);
23
+ }
24
+ const manifestRoot = manifestXml?.root?.localName === "manifest" ? manifestXml.root : undefined;
25
+ if (manifestXml?.root && !manifestRoot) {
26
+ pushPackageDiagnostic(diagnostics, "package.manifest.root", "error", `Expected imsmanifest.xml root manifest, found ${manifestXml.root.localName}.`, QTI_PACKAGE_MANIFEST_PATH);
27
+ }
28
+ const manifestResourceRecords = manifestRoot
29
+ ? parseManifestResources(manifestRoot, entriesByPath, diagnostics)
30
+ : [];
31
+ const manifestResources = manifestResourceRecords;
32
+ const resourcesByIdentifier = indexManifestResources(manifestResourceRecords, diagnostics);
33
+ diagnoseManifestDependencyReferences(manifestResourceRecords, resourcesByIdentifier, diagnostics);
34
+ const itemResources = manifestResourceRecords.filter((resource) => isQtiItemResource(resource.type));
35
+ const assessmentTestResources = manifestResourceRecords.filter((resource) => isQtiAssessmentTestResource(resource.type));
36
+ diagnosePrimaryResourceHrefs([...itemResources, ...assessmentTestResources], diagnostics);
37
+ const packageShape = detectPackageShape(itemResources, assessmentTestResources, diagnostics);
38
+ const assessmentTest = parseAssessmentTestPackageModel(packageShape, assessmentTestResources, entriesByPath, xmlFilesByPath, diagnostics);
39
+ const itemReferences = itemReferencesForPackageShape(packageShape, itemResources, assessmentTest, diagnostics);
40
+ const items = parsePackageItems(itemReferences, entriesByPath, xmlFilesByPath, diagnostics);
41
+ const assets = collectPackageAssets(resourcesByIdentifier, assessmentTest, items, entriesByPath, diagnostics);
42
+ const manifestStandards = manifestRoot
43
+ ? parseStandardAlignments(manifestRoot, QTI_PACKAGE_MANIFEST_PATH)
44
+ : [];
45
+ const standards = uniqueStandards([
46
+ ...manifestStandards,
47
+ ...(assessmentTest?.standards ?? []),
48
+ ...items.flatMap((item) => item.standards),
49
+ ]);
50
+ const timing = primaryTiming(assessmentTest, items);
51
+ const title = packageTitle(manifestRoot, assessmentTest, items);
52
+ return {
53
+ ok: diagnostics.every((diagnostic) => diagnostic.severity !== "error"),
54
+ title,
55
+ packageShape,
56
+ items,
57
+ assets,
58
+ manifestResources,
59
+ assessmentTest,
60
+ timing,
61
+ standards,
62
+ diagnostics,
63
+ };
64
+ }
65
+ function indexEntries(entries, diagnostics) {
66
+ const entriesByPath = new Map();
67
+ for (const entry of entries) {
68
+ if (entriesByPath.has(entry.path)) {
69
+ pushPackageDiagnostic(diagnostics, "package.entry.duplicate", "error", `QTI package contains duplicate entry ${entry.path}.`, entry.path);
70
+ continue;
71
+ }
72
+ entriesByPath.set(entry.path, entry);
73
+ }
74
+ return entriesByPath;
75
+ }
76
+ //# sourceMappingURL=qti-package.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qti-package.js","sourceRoot":"","sources":["../src/qti-package.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /** One named response variable in record or array wire form. */
2
+ export interface QtiNamedResponseInput {
3
+ identifier: string;
4
+ value: unknown;
5
+ }
6
+ export type QtiNamedResponsesInput = Record<string, unknown> | readonly QtiNamedResponseInput[] | undefined;
7
+ /** Normalize record or array response payloads into a stable entry list. */
8
+ export declare function listNamedResponseInputs(responses: QtiNamedResponsesInput): readonly QtiNamedResponseInput[];
9
+ //# sourceMappingURL=response-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-input.d.ts","sourceRoot":"","sources":["../src/response-input.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,sBAAsB,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,SAAS,qBAAqB,EAAE,GAChC,SAAS,CAAC;AAEd,4EAA4E;AAC5E,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,sBAAsB,GAChC,SAAS,qBAAqB,EAAE,CAIlC"}
@@ -0,0 +1,9 @@
1
+ /** Normalize record or array response payloads into a stable entry list. */
2
+ export function listNamedResponseInputs(responses) {
3
+ if (!responses)
4
+ return [];
5
+ if (Array.isArray(responses))
6
+ return responses;
7
+ return Object.entries(responses).map(([identifier, value]) => ({ identifier, value }));
8
+ }
9
+ //# sourceMappingURL=response-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-input.js","sourceRoot":"","sources":["../src/response-input.ts"],"names":[],"mappings":"AAWA,4EAA4E;AAC5E,MAAM,UAAU,uBAAuB,CACrC,SAAiC;IAEjC,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAC1B,IAAI,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACzF,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { QtiChoice, QtiDiagnostic, QtiInteraction, QtiValue } from "./types.js";
2
+ export interface QtiResponseValidationPolicy {
3
+ checkMinimum: boolean;
4
+ checkMaximum: boolean;
5
+ checkMatchMax: boolean;
6
+ }
7
+ export declare function responseIsEmpty(value: QtiValue): boolean;
8
+ export declare function responseCount(value: QtiValue): number;
9
+ /** Order and graphic order ignore max-choices unless min-choices is authored. */
10
+ export declare function orderSubsetLimitsActive(interaction: QtiInteraction): boolean;
11
+ export declare function responseLimitAttribute(interaction: QtiInteraction, choiceKey: "min-choices" | "max-choices", associationKey: "min-associations" | "max-associations"): string | undefined;
12
+ export declare function minimumRequiredResponses(interaction: QtiInteraction | undefined): number;
13
+ export declare function maximumAllowedResponses(interaction: QtiInteraction | undefined): number | undefined;
14
+ export declare function associationMaximumResponses(interaction: QtiInteraction): number | undefined;
15
+ export declare function mediaPlayCount(value: QtiValue): number;
16
+ declare function minimumMediaPlays(interaction: QtiInteraction): number;
17
+ declare function maximumMediaPlays(interaction: QtiInteraction): number | undefined;
18
+ export { maximumMediaPlays, minimumMediaPlays };
19
+ export declare function responseValidationPolicy(declaration: {
20
+ readonly correctResponse: QtiValue | null;
21
+ }, interaction: QtiInteraction | undefined): QtiResponseValidationPolicy;
22
+ export declare function requiredResponseDiagnostic(responseIdentifier: string, interaction: QtiInteraction | undefined, minimum: number): QtiDiagnostic;
23
+ export declare function maximumResponseDiagnostic(responseIdentifier: string, interaction: QtiInteraction | undefined, maximum: number): QtiDiagnostic;
24
+ export declare function parseUnlimitedMaximum(value: string | undefined): number | undefined;
25
+ export declare function choiceMatchMaximum(choice: QtiChoice): number | undefined;
26
+ export type DirectedPairUseSide = "source" | "target" | "either";
27
+ export declare function directedPairChoiceUseCount(choice: QtiChoice, selectedPairs: string[], side?: DirectedPairUseSide): number;
28
+ export declare function choiceMatchLimitExceeded(choice: QtiChoice, useCount: number): boolean;
29
+ export declare function wouldExceedChoiceMatchMaximum(choice: QtiChoice, selectedPairs: string[], side?: DirectedPairUseSide): boolean;
30
+ export declare function matchMaxDiagnostics(responseIdentifier: string, interaction: QtiInteraction, response: QtiValue): QtiDiagnostic[];
31
+ //# sourceMappingURL=response-validation-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-validation-policy.d.ts","sourceRoot":"","sources":["../src/response-validation-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrF,MAAM,WAAW,2BAA2B;IAC1C,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAExD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAErD;AAED,iFAAiF;AACjF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,cAAc,GAAG,OAAO,CAK5E;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,cAAc,EAC3B,SAAS,EAAE,aAAa,GAAG,aAAa,EACxC,cAAc,EAAE,kBAAkB,GAAG,kBAAkB,GACtD,MAAM,GAAG,SAAS,CAKpB;AAMD,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,cAAc,GAAG,SAAS,GAAG,MAAM,CAMxF;AAED,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,cAAc,GAAG,SAAS,GACtC,MAAM,GAAG,SAAS,CAapB;AAED,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAE3F;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAGtD;AAED,iBAAS,iBAAiB,CAAC,WAAW,EAAE,cAAc,GAAG,MAAM,CAI9D;AAED,iBAAS,iBAAiB,CAAC,WAAW,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAG1E;AAED,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;AAEhD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE;IAAE,QAAQ,CAAC,eAAe,EAAE,QAAQ,GAAG,IAAI,CAAA;CAAE,EAC1D,WAAW,EAAE,cAAc,GAAG,SAAS,GACtC,2BAA2B,CAyB7B;AAED,wBAAgB,0BAA0B,CACxC,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,cAAc,GAAG,SAAS,EACvC,OAAO,EAAE,MAAM,GACd,aAAa,CAaf;AAED,wBAAgB,yBAAyB,CACvC,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,cAAc,GAAG,SAAS,EACvC,OAAO,EAAE,MAAM,GACd,aAAa,CAWf;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAInF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAExE;AAED,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjE,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,MAAM,EAAE,EACvB,IAAI,GAAE,mBAA8B,GACnC,MAAM,CAOR;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGrF;AAED,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,SAAS,EACjB,aAAa,EAAE,MAAM,EAAE,EACvB,IAAI,GAAE,mBAA8B,GACnC,OAAO,CAKT;AAMD,wBAAgB,mBAAmB,CACjC,kBAAkB,EAAE,MAAM,EAC1B,WAAW,EAAE,cAAc,EAC3B,QAAQ,EAAE,QAAQ,GACjB,aAAa,EAAE,CAsBjB"}
@@ -0,0 +1,166 @@
1
+ import { parseNonNegativeInteger } from "./validation-primitives.js";
2
+ import { qtiValueToIdentifierList } from "./value-format.js";
3
+ export function responseIsEmpty(value) {
4
+ return value === null || value === "" || (Array.isArray(value) && value.length === 0);
5
+ }
6
+ export function responseCount(value) {
7
+ return responseIsEmpty(value) ? 0 : Array.isArray(value) ? value.length : 1;
8
+ }
9
+ /** Order and graphic order ignore max-choices unless min-choices is authored. */
10
+ export function orderSubsetLimitsActive(interaction) {
11
+ return ((interaction.type === "order" || interaction.type === "graphicOrder") &&
12
+ interaction.attributes["min-choices"] !== undefined);
13
+ }
14
+ export function responseLimitAttribute(interaction, choiceKey, associationKey) {
15
+ if (interaction.type === "order" || interaction.type === "graphicOrder") {
16
+ return interaction.attributes[choiceKey];
17
+ }
18
+ return interaction.attributes[choiceKey] ?? interaction.attributes[associationKey];
19
+ }
20
+ function interactionRequiresResponse(interaction) {
21
+ return interaction.attributes.required === "true";
22
+ }
23
+ export function minimumRequiredResponses(interaction) {
24
+ if (!interaction)
25
+ return 1;
26
+ if (interaction.type === "media")
27
+ return minimumMediaPlays(interaction);
28
+ const explicit = responseLimitAttribute(interaction, "min-choices", "min-associations");
29
+ if (explicit === undefined)
30
+ return interactionRequiresResponse(interaction) ? 1 : 0;
31
+ return parseNonNegativeInteger(explicit) ?? 1;
32
+ }
33
+ export function maximumAllowedResponses(interaction) {
34
+ if (!interaction)
35
+ return undefined;
36
+ if (interaction.type === "media")
37
+ return maximumMediaPlays(interaction);
38
+ if ((interaction.type === "order" || interaction.type === "graphicOrder") &&
39
+ !orderSubsetLimitsActive(interaction)) {
40
+ return undefined;
41
+ }
42
+ const explicit = responseLimitAttribute(interaction, "max-choices", "max-associations");
43
+ if (explicit === undefined)
44
+ return undefined;
45
+ const parsed = parseNonNegativeInteger(explicit);
46
+ return parsed === undefined || parsed <= 0 ? undefined : parsed;
47
+ }
48
+ export function associationMaximumResponses(interaction) {
49
+ return interaction.responseCardinality === "single" ? 1 : maximumAllowedResponses(interaction);
50
+ }
51
+ export function mediaPlayCount(value) {
52
+ const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : 0;
53
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
54
+ }
55
+ function minimumMediaPlays(interaction) {
56
+ const parsed = parseNonNegativeInteger(interaction.attributes["min-plays"]);
57
+ if (parsed !== undefined)
58
+ return parsed;
59
+ return interactionRequiresResponse(interaction) ? 1 : 0;
60
+ }
61
+ function maximumMediaPlays(interaction) {
62
+ const parsed = parseNonNegativeInteger(interaction.attributes["max-plays"]);
63
+ return parsed === undefined || parsed <= 0 ? undefined : parsed;
64
+ }
65
+ export { maximumMediaPlays, minimumMediaPlays };
66
+ export function responseValidationPolicy(declaration, interaction) {
67
+ const authoredMinimum = interaction === undefined
68
+ ? undefined
69
+ : responseLimitAttribute(interaction, "min-choices", "min-associations");
70
+ const minimum = interaction === undefined ? undefined : minimumRequiredResponses(interaction);
71
+ const validatesMinimum = declaration.correctResponse !== null ||
72
+ interaction?.type === "media" ||
73
+ authoredMinimum !== undefined ||
74
+ (minimum !== undefined && minimum > 0);
75
+ const maximum = maximumAllowedResponses(interaction);
76
+ if (declaration.correctResponse === null &&
77
+ interaction?.type !== "media" &&
78
+ !validatesMinimum &&
79
+ maximum === undefined) {
80
+ return { checkMinimum: false, checkMaximum: false, checkMatchMax: false };
81
+ }
82
+ return {
83
+ checkMinimum: validatesMinimum,
84
+ checkMaximum: maximum !== undefined,
85
+ checkMatchMax: interaction !== undefined,
86
+ };
87
+ }
88
+ export function requiredResponseDiagnostic(responseIdentifier, interaction, minimum) {
89
+ return {
90
+ code: "response.required",
91
+ severity: "error",
92
+ message: interaction?.attributes["data-min-selections-message"] ??
93
+ (interaction?.type === "media"
94
+ ? `${responseIdentifier} requires at least ${minimum} play${minimum === 1 ? "" : "s"}.`
95
+ : minimum === 1
96
+ ? `${responseIdentifier} requires a response.`
97
+ : `${responseIdentifier} requires at least ${minimum} responses.`),
98
+ path: responseIdentifier,
99
+ };
100
+ }
101
+ export function maximumResponseDiagnostic(responseIdentifier, interaction, maximum) {
102
+ return {
103
+ code: "response.maximum",
104
+ severity: "error",
105
+ message: interaction?.attributes["data-max-selections-message"] ??
106
+ (interaction?.type === "media"
107
+ ? `${responseIdentifier} allows at most ${maximum} play${maximum === 1 ? "" : "s"}.`
108
+ : `${responseIdentifier} allows at most ${maximum} response${maximum === 1 ? "" : "s"}.`),
109
+ path: responseIdentifier,
110
+ };
111
+ }
112
+ export function parseUnlimitedMaximum(value) {
113
+ if (value === undefined)
114
+ return undefined;
115
+ const parsed = parseNonNegativeInteger(value);
116
+ return parsed === undefined || parsed <= 0 ? undefined : parsed;
117
+ }
118
+ export function choiceMatchMaximum(choice) {
119
+ return parseUnlimitedMaximum(choice.attributes["match-max"]);
120
+ }
121
+ export function directedPairChoiceUseCount(choice, selectedPairs, side = "either") {
122
+ return selectedPairs.reduce((count, pair) => {
123
+ const [source = "", target = ""] = pair.split(/\s+/);
124
+ if (side === "source")
125
+ return source === choice.identifier ? count + 1 : count;
126
+ if (side === "target")
127
+ return target === choice.identifier ? count + 1 : count;
128
+ return count + (source === choice.identifier ? 1 : 0) + (target === choice.identifier ? 1 : 0);
129
+ }, 0);
130
+ }
131
+ export function choiceMatchLimitExceeded(choice, useCount) {
132
+ const maximum = choiceMatchMaximum(choice);
133
+ return maximum !== undefined && useCount > maximum;
134
+ }
135
+ export function wouldExceedChoiceMatchMaximum(choice, selectedPairs, side = "either") {
136
+ return choiceMatchLimitExceeded(choice, directedPairChoiceUseCount(choice, selectedPairs, side) + 1);
137
+ }
138
+ function responseChoiceIdentifiers(response) {
139
+ return qtiValueToIdentifierList(response).flatMap((value) => value.split(/\s+/).filter(Boolean));
140
+ }
141
+ export function matchMaxDiagnostics(responseIdentifier, interaction, response) {
142
+ const identifiers = responseChoiceIdentifiers(response);
143
+ if (identifiers.length === 0)
144
+ return [];
145
+ const counts = new Map();
146
+ for (const identifier of identifiers) {
147
+ counts.set(identifier, (counts.get(identifier) ?? 0) + 1);
148
+ }
149
+ const diagnostics = [];
150
+ for (const choice of interaction.choices) {
151
+ const maximum = choiceMatchMaximum(choice);
152
+ if (maximum === undefined)
153
+ continue;
154
+ const count = counts.get(choice.identifier) ?? 0;
155
+ if (count <= maximum)
156
+ continue;
157
+ diagnostics.push({
158
+ code: "response.matchMax",
159
+ severity: "error",
160
+ message: `${choice.text || choice.identifier} may be used at most ${maximum} time${maximum === 1 ? "" : "s"}.`,
161
+ path: responseIdentifier,
162
+ });
163
+ }
164
+ return diagnostics;
165
+ }
166
+ //# sourceMappingURL=response-validation-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-validation-policy.js","sourceRoot":"","sources":["../src/response-validation-policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { QtiAssessmentItem, QtiDiagnostic } from "./types.js";
2
+ import { type QtiNamedResponseInput } from "./response-input.js";
3
+ export type { QtiNamedResponseInput as QtiResponseVariableInput } from "./response-input.js";
4
+ export type QtiResponseVariablesInput = Record<string, unknown> | readonly QtiNamedResponseInput[];
5
+ export type QtiResponseValidationDiagnosticCode = "response.required" | "response.maximum" | "response.matchMax" | "response.cardinality" | "response.undeclared" | "response.identifier.required" | "response.value.invalid";
6
+ export interface QtiResponseValidationDiagnostic extends QtiDiagnostic {
7
+ code: QtiResponseValidationDiagnosticCode;
8
+ identifier?: string | undefined;
9
+ }
10
+ export interface QtiResponseValidationResult {
11
+ ok: boolean;
12
+ diagnostics: QtiResponseValidationDiagnostic[];
13
+ }
14
+ export interface QtiResponseValidationInput {
15
+ item: QtiAssessmentItem;
16
+ responses: QtiResponseVariablesInput;
17
+ allowIncompleteResponses?: boolean | undefined;
18
+ allowedUndeclaredResponseIdentifiers?: readonly string[] | undefined;
19
+ responseIdentifiers?: Iterable<string> | undefined;
20
+ }
21
+ /** Validate submitted response variables against a parsed QTI assessment item. */
22
+ export declare function validateQtiResponseVariables(input: QtiResponseValidationInput): QtiResponseValidationResult;
23
+ //# sourceMappingURL=response-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-validation.d.ts","sourceRoot":"","sources":["../src/response-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAEjB,aAAa,EAId,MAAM,YAAY,CAAC;AAGpB,OAAO,EAA2B,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAc1F,YAAY,EAAE,qBAAqB,IAAI,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC7F,MAAM,MAAM,yBAAyB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,qBAAqB,EAAE,CAAC;AAEnG,MAAM,MAAM,mCAAmC,GAC3C,mBAAmB,GACnB,kBAAkB,GAClB,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,8BAA8B,GAC9B,wBAAwB,CAAC;AAE7B,MAAM,WAAW,+BAAgC,SAAQ,aAAa;IACpE,IAAI,EAAE,mCAAmC,CAAC;IAC1C,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,+BAA+B,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,iBAAiB,CAAC;IACxB,SAAS,EAAE,yBAAyB,CAAC;IACrC,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/C,oCAAoC,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrE,mBAAmB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;CACpD;AAED,kFAAkF;AAClF,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,0BAA0B,GAChC,2BAA2B,CAyD7B"}