@longsightgroup/qti3-core 0.9.2 → 0.9.4
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.
- package/README.md +108 -11
- package/dist/adaptive-turn-materializer.d.ts +2 -0
- package/dist/adaptive-turn-materializer.d.ts.map +1 -1
- package/dist/adaptive-turn-materializer.js +25 -19
- package/dist/adaptive-turn-materializer.js.map +1 -1
- package/dist/adaptive-turn.d.ts +8 -1
- package/dist/adaptive-turn.d.ts.map +1 -1
- package/dist/adaptive-turn.js +15 -5
- package/dist/adaptive-turn.js.map +1 -1
- package/dist/assert-never.d.ts +3 -0
- package/dist/assert-never.d.ts.map +1 -0
- package/dist/assert-never.js +5 -0
- package/dist/assert-never.js.map +1 -0
- package/dist/attempt-state-constants.d.ts +6 -0
- package/dist/attempt-state-constants.d.ts.map +1 -0
- package/dist/attempt-state-constants.js +6 -0
- package/dist/attempt-state-constants.js.map +1 -0
- package/dist/attempt-state.d.ts +5 -0
- package/dist/attempt-state.d.ts.map +1 -0
- package/dist/attempt-state.js +190 -0
- package/dist/attempt-state.js.map +1 -0
- package/dist/catalog.js +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/content-text.d.ts.map +1 -1
- package/dist/content-text.js +3 -0
- package/dist/content-text.js.map +1 -1
- package/dist/custom-operators.d.ts +13 -0
- package/dist/custom-operators.d.ts.map +1 -0
- package/dist/custom-operators.js +2 -0
- package/dist/custom-operators.js.map +1 -0
- package/dist/delivery-preparation.d.ts +20 -0
- package/dist/delivery-preparation.d.ts.map +1 -0
- package/dist/delivery-preparation.js +71 -0
- package/dist/delivery-preparation.js.map +1 -0
- package/dist/delivery-redaction.d.ts +5 -0
- package/dist/delivery-redaction.d.ts.map +1 -1
- package/dist/delivery-redaction.js +27 -6
- package/dist/delivery-redaction.js.map +1 -1
- package/dist/delivery-security.js +2 -2
- package/dist/delivery-security.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/item-scoring-expectation.d.ts +4 -0
- package/dist/item-scoring-expectation.d.ts.map +1 -0
- package/dist/item-scoring-expectation.js +6 -0
- package/dist/item-scoring-expectation.js.map +1 -0
- package/dist/item-submission-materialization.d.ts +25 -0
- package/dist/item-submission-materialization.d.ts.map +1 -0
- package/dist/item-submission-materialization.js +68 -0
- package/dist/item-submission-materialization.js.map +1 -0
- package/dist/parser-custom-interactions.js +1 -1
- package/dist/parser-custom-interactions.js.map +1 -1
- package/dist/parser-declarations.d.ts.map +1 -1
- package/dist/parser-declarations.js +4 -4
- package/dist/parser-declarations.js.map +1 -1
- package/dist/parser-item-metadata.d.ts.map +1 -1
- package/dist/parser-item-metadata.js +8 -7
- package/dist/parser-item-metadata.js.map +1 -1
- package/dist/parser-processing.js +2 -2
- package/dist/parser-processing.js.map +1 -1
- package/dist/parser-values.d.ts +5 -1
- package/dist/parser-values.d.ts.map +1 -1
- package/dist/parser-values.js +33 -0
- package/dist/parser-values.js.map +1 -1
- package/dist/processing-evaluator-boolean.d.ts +8 -0
- package/dist/processing-evaluator-boolean.d.ts.map +1 -0
- package/dist/processing-evaluator-boolean.js +47 -0
- package/dist/processing-evaluator-boolean.js.map +1 -0
- package/dist/processing-evaluator-collection.d.ts +12 -0
- package/dist/processing-evaluator-collection.d.ts.map +1 -0
- package/dist/processing-evaluator-collection.js +35 -0
- package/dist/processing-evaluator-collection.js.map +1 -0
- package/dist/processing-evaluator-comparison.d.ts +8 -0
- package/dist/processing-evaluator-comparison.d.ts.map +1 -0
- package/dist/processing-evaluator-comparison.js +88 -0
- package/dist/processing-evaluator-comparison.js.map +1 -0
- package/dist/processing-evaluator-custom.d.ts +8 -0
- package/dist/processing-evaluator-custom.d.ts.map +1 -0
- package/dist/processing-evaluator-custom.js +14 -0
- package/dist/processing-evaluator-custom.js.map +1 -0
- package/dist/processing-evaluator-geometry.d.ts +8 -0
- package/dist/processing-evaluator-geometry.d.ts.map +1 -0
- package/dist/processing-evaluator-geometry.js +21 -0
- package/dist/processing-evaluator-geometry.js.map +1 -0
- package/dist/processing-evaluator-numeric.d.ts +8 -0
- package/dist/processing-evaluator-numeric.d.ts.map +1 -0
- package/dist/processing-evaluator-numeric.js +112 -0
- package/dist/processing-evaluator-numeric.js.map +1 -0
- package/dist/processing-evaluator-string.d.ts +8 -0
- package/dist/processing-evaluator-string.d.ts.map +1 -0
- package/dist/processing-evaluator-string.js +33 -0
- package/dist/processing-evaluator-string.js.map +1 -0
- package/dist/processing-evaluator-variable.d.ts +8 -0
- package/dist/processing-evaluator-variable.d.ts.map +1 -0
- package/dist/processing-evaluator-variable.js +39 -0
- package/dist/processing-evaluator-variable.js.map +1 -0
- package/dist/processing-evaluator.d.ts +22 -0
- package/dist/processing-evaluator.d.ts.map +1 -0
- package/dist/processing-evaluator.js +137 -0
- package/dist/processing-evaluator.js.map +1 -0
- package/dist/processing-expression-children.d.ts +3 -0
- package/dist/processing-expression-children.d.ts.map +1 -0
- package/dist/processing-expression-children.js +68 -0
- package/dist/processing-expression-children.js.map +1 -0
- package/dist/processing-mapping.d.ts +13 -0
- package/dist/processing-mapping.d.ts.map +1 -0
- package/dist/processing-mapping.js +120 -0
- package/dist/processing-mapping.js.map +1 -0
- package/dist/processing-operators.d.ts +11 -0
- package/dist/processing-operators.d.ts.map +1 -0
- package/dist/processing-operators.js +178 -0
- package/dist/processing-operators.js.map +1 -0
- package/dist/processing-random.d.ts +2 -0
- package/dist/processing-random.d.ts.map +1 -0
- package/dist/processing-random.js +16 -0
- package/dist/processing-random.js.map +1 -0
- package/dist/processing-state.d.ts +8 -0
- package/dist/processing-state.d.ts.map +1 -0
- package/dist/processing-state.js +44 -0
- package/dist/processing-state.js.map +1 -0
- package/dist/processing-templates.d.ts +3 -0
- package/dist/processing-templates.d.ts.map +1 -0
- package/dist/processing-templates.js +12 -0
- package/dist/processing-templates.js.map +1 -0
- package/dist/processing-values.d.ts +15 -0
- package/dist/processing-values.d.ts.map +1 -0
- package/dist/processing-values.js +150 -0
- package/dist/processing-values.js.map +1 -0
- package/dist/processing-variables.d.ts +7 -0
- package/dist/processing-variables.d.ts.map +1 -0
- package/dist/processing-variables.js +40 -0
- package/dist/processing-variables.js.map +1 -0
- package/dist/qti-package-assets.d.ts +8 -0
- package/dist/qti-package-assets.d.ts.map +1 -0
- package/dist/qti-package-assets.js +173 -0
- package/dist/qti-package-assets.js.map +1 -0
- package/dist/qti-package-items.d.ts +14 -0
- package/dist/qti-package-items.d.ts.map +1 -0
- package/dist/qti-package-items.js +138 -0
- package/dist/qti-package-items.js.map +1 -0
- package/dist/qti-package-manifest.d.ts +13 -0
- package/dist/qti-package-manifest.d.ts.map +1 -0
- package/dist/qti-package-manifest.js +109 -0
- package/dist/qti-package-manifest.js.map +1 -0
- package/dist/qti-package-metadata.d.ts +8 -0
- package/dist/qti-package-metadata.d.ts.map +1 -0
- package/dist/qti-package-metadata.js +115 -0
- package/dist/qti-package-metadata.js.map +1 -0
- package/dist/qti-package-paths.d.ts +6 -0
- package/dist/qti-package-paths.d.ts.map +1 -0
- package/dist/qti-package-paths.js +32 -0
- package/dist/qti-package-paths.js.map +1 -0
- package/dist/qti-package-types.d.ts +98 -0
- package/dist/qti-package-types.d.ts.map +1 -0
- package/dist/qti-package-types.js +2 -0
- package/dist/qti-package-types.js.map +1 -0
- package/dist/qti-package-xml.d.ts +21 -0
- package/dist/qti-package-xml.d.ts.map +1 -0
- package/dist/qti-package-xml.js +68 -0
- package/dist/qti-package-xml.js.map +1 -0
- package/dist/qti-package-zip.d.ts +24 -0
- package/dist/qti-package-zip.d.ts.map +1 -0
- package/dist/qti-package-zip.js +185 -0
- package/dist/qti-package-zip.js.map +1 -0
- package/dist/qti-package.d.ts +7 -0
- package/dist/qti-package.d.ts.map +1 -0
- package/dist/qti-package.js +76 -0
- package/dist/qti-package.js.map +1 -0
- package/dist/response-input.d.ts +9 -0
- package/dist/response-input.d.ts.map +1 -0
- package/dist/response-input.js +9 -0
- package/dist/response-input.js.map +1 -0
- package/dist/response-validation-policy.d.ts +31 -0
- package/dist/response-validation-policy.d.ts.map +1 -0
- package/dist/response-validation-policy.js +158 -0
- package/dist/response-validation-policy.js.map +1 -0
- package/dist/response-validation.d.ts +23 -0
- package/dist/response-validation.d.ts.map +1 -0
- package/dist/response-validation.js +157 -0
- package/dist/response-validation.js.map +1 -0
- package/dist/scoring-disposition-policy.d.ts +8 -0
- package/dist/scoring-disposition-policy.d.ts.map +1 -0
- package/dist/scoring-disposition-policy.js +24 -0
- package/dist/scoring-disposition-policy.js.map +1 -0
- package/dist/serializer-processing-expressions.d.ts.map +1 -1
- package/dist/serializer-processing-expressions.js +2 -8
- package/dist/serializer-processing-expressions.js.map +1 -1
- package/dist/serializer-processing-xml.js +2 -2
- package/dist/serializer-processing-xml.js.map +1 -1
- package/dist/serializer-processing.d.ts.map +1 -1
- package/dist/serializer-processing.js +3 -0
- package/dist/serializer-processing.js.map +1 -1
- package/dist/session.d.ts +5 -14
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +76 -1158
- package/dist/session.js.map +1 -1
- package/dist/shared-vocabulary-coverage-policy.js +1 -1
- package/dist/shared-vocabulary-coverage-policy.js.map +1 -1
- package/dist/shared-vocabulary-generated-families.js +1 -1
- package/dist/shared-vocabulary-generated-families.js.map +1 -1
- package/dist/shared-vocabulary-support.js +3 -3
- package/dist/shared-vocabulary-support.js.map +1 -1
- package/dist/shared-vocabulary.d.ts.map +1 -1
- package/dist/shared-vocabulary.js +4 -3
- package/dist/shared-vocabulary.js.map +1 -1
- package/dist/support-evidence.d.ts +22 -0
- package/dist/support-evidence.d.ts.map +1 -0
- package/dist/support-evidence.js +93 -0
- package/dist/support-evidence.js.map +1 -0
- package/dist/support.d.ts.map +1 -1
- package/dist/support.js +30 -43
- package/dist/support.js.map +1 -1
- package/dist/template-presentation-materializer.d.ts +14 -0
- package/dist/template-presentation-materializer.d.ts.map +1 -0
- package/dist/template-presentation-materializer.js +143 -0
- package/dist/template-presentation-materializer.js.map +1 -0
- package/dist/trusted-item-session.d.ts +25 -8
- package/dist/trusted-item-session.d.ts.map +1 -1
- package/dist/trusted-item-session.js +57 -51
- package/dist/trusted-item-session.js.map +1 -1
- package/dist/trusted-session-materialization.d.ts +33 -0
- package/dist/trusted-session-materialization.d.ts.map +1 -0
- package/dist/trusted-session-materialization.js +26 -0
- package/dist/trusted-session-materialization.js.map +1 -0
- package/dist/tts.d.ts.map +1 -1
- package/dist/tts.js +16 -2
- package/dist/tts.js.map +1 -1
- package/dist/validation-geometry.d.ts +8 -0
- package/dist/validation-geometry.d.ts.map +1 -0
- package/dist/validation-geometry.js +187 -0
- package/dist/validation-geometry.js.map +1 -0
- package/dist/validation-interactions.d.ts +3 -0
- package/dist/validation-interactions.d.ts.map +1 -0
- package/dist/validation-interactions.js +644 -0
- package/dist/validation-interactions.js.map +1 -0
- package/dist/validation-primitives.d.ts +12 -0
- package/dist/validation-primitives.d.ts.map +1 -0
- package/dist/validation-primitives.js +53 -0
- package/dist/validation-primitives.js.map +1 -0
- package/dist/validation-processing.d.ts +4 -0
- package/dist/validation-processing.d.ts.map +1 -0
- package/dist/validation-processing.js +569 -0
- package/dist/validation-processing.js.map +1 -0
- package/dist/validation-shared-vocabulary-layout.d.ts +10 -0
- package/dist/validation-shared-vocabulary-layout.d.ts.map +1 -0
- package/dist/validation-shared-vocabulary-layout.js +102 -0
- package/dist/validation-shared-vocabulary-layout.js.map +1 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +27 -1641
- package/dist/validation.js.map +1 -1
- package/dist/variable-snapshot.d.ts +9 -0
- package/dist/variable-snapshot.d.ts.map +1 -0
- package/dist/variable-snapshot.js +9 -0
- package/dist/variable-snapshot.js.map +1 -0
- package/dist/xml.js +1 -1
- package/dist/xml.js.map +1 -1
- package/package.json +1 -1
- package/src/adaptive-turn-materializer.ts +29 -16
- package/src/adaptive-turn.ts +29 -6
- package/src/assert-never.ts +4 -0
- package/src/attempt-state-constants.ts +5 -0
- package/src/attempt-state.ts +245 -0
- package/src/catalog.ts +1 -1
- package/src/content-text.ts +3 -0
- package/src/custom-operators.ts +12 -0
- package/src/delivery-preparation.ts +121 -0
- package/src/delivery-redaction.ts +37 -6
- package/src/delivery-security.ts +2 -2
- package/src/index.ts +67 -0
- package/src/item-scoring-expectation.ts +9 -0
- package/src/item-submission-materialization.ts +120 -0
- package/src/parser-custom-interactions.ts +1 -1
- package/src/parser-declarations.ts +4 -3
- package/src/parser-item-metadata.ts +8 -7
- package/src/parser-processing.ts +2 -2
- package/src/parser-values.ts +36 -0
- package/src/processing-evaluator-boolean.ts +48 -0
- package/src/processing-evaluator-collection.ts +50 -0
- package/src/processing-evaluator-comparison.ts +106 -0
- package/src/processing-evaluator-custom.ts +20 -0
- package/src/processing-evaluator-geometry.ts +26 -0
- package/src/processing-evaluator-numeric.ts +155 -0
- package/src/processing-evaluator-string.ts +60 -0
- package/src/processing-evaluator-variable.ts +81 -0
- package/src/processing-evaluator.ts +173 -0
- package/src/processing-expression-children.ts +69 -0
- package/src/processing-mapping.ts +153 -0
- package/src/processing-operators.ts +184 -0
- package/src/processing-random.ts +16 -0
- package/src/processing-state.ts +69 -0
- package/src/processing-templates.ts +12 -0
- package/src/processing-values.ts +162 -0
- package/src/processing-variables.ts +83 -0
- package/src/qti-package-assets.ts +256 -0
- package/src/qti-package-items.ts +240 -0
- package/src/qti-package-manifest.ts +210 -0
- package/src/qti-package-metadata.ts +143 -0
- package/src/qti-package-paths.ts +56 -0
- package/src/qti-package-types.ts +117 -0
- package/src/qti-package-xml.ts +97 -0
- package/src/qti-package-zip.ts +325 -0
- package/src/qti-package.ts +176 -0
- package/src/response-input.ts +19 -0
- package/src/response-validation-policy.ts +218 -0
- package/src/response-validation.ts +282 -0
- package/src/scoring-disposition-policy.ts +37 -0
- package/src/serializer-processing-expressions.ts +2 -12
- package/src/serializer-processing-xml.ts +2 -2
- package/src/serializer-processing.ts +3 -0
- package/src/session.ts +128 -2006
- package/src/shared-vocabulary-coverage-policy.ts +1 -1
- package/src/shared-vocabulary-generated-families.ts +1 -1
- package/src/shared-vocabulary-support.ts +3 -3
- package/src/shared-vocabulary.ts +3 -2
- package/src/support-evidence.ts +107 -0
- package/src/support.ts +59 -50
- package/src/template-presentation-materializer.ts +219 -0
- package/src/trusted-item-session.ts +86 -78
- package/src/trusted-session-materialization.ts +67 -0
- package/src/tts.ts +29 -15
- package/src/validation-geometry.ts +204 -0
- package/src/validation-interactions.ts +831 -0
- package/src/validation-primitives.ts +68 -0
- package/src/validation-processing.ts +785 -0
- package/src/validation-shared-vocabulary-layout.ts +117 -0
- package/src/validation.ts +44 -2077
- package/src/variable-snapshot.ts +20 -0
- package/src/xml.ts +1 -1
package/README.md
CHANGED
|
@@ -34,24 +34,42 @@ console.log(result.outcomes);
|
|
|
34
34
|
console.log(result.state);
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
### Randomized template items
|
|
38
|
+
|
|
39
|
+
QTI template processing can generate deterministic item variants with expressions such
|
|
40
|
+
as `qti-random-integer`, printed variables, and `qti-set-correct-response`:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const session = createItemSession(parsed.document, undefined, { randomSeed: "attempt-123" });
|
|
44
|
+
const state = session.serialize();
|
|
45
|
+
|
|
46
|
+
store(state);
|
|
47
|
+
|
|
48
|
+
const resumed = createItemSession(parsed.document, state, { randomSeed: "different-seed" });
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Persist the returned `qti3.attempt-state.v1` state for resume. Once an attempt exists,
|
|
52
|
+
saved `templateValues` are authoritative; they are restored before generated correct
|
|
53
|
+
responses are derived, so resuming does not depend on the original seed.
|
|
54
|
+
|
|
37
55
|
### Candidate-safe delivery XML
|
|
38
56
|
|
|
39
57
|
High-stakes delivery systems can redact answer-bearing item XML before sending it to
|
|
40
58
|
a browser:
|
|
41
59
|
|
|
42
60
|
```ts
|
|
43
|
-
import {
|
|
61
|
+
import { prepareQtiDeliveryXml } from "@longsightgroup/qti3-core";
|
|
44
62
|
|
|
45
|
-
const delivery =
|
|
63
|
+
const delivery = prepareQtiDeliveryXml(authoritativeItemXml, { mode: "static" });
|
|
46
64
|
|
|
47
65
|
if (!delivery.ok) {
|
|
48
66
|
throw new Error(delivery.diagnostics.map((item) => item.message).join("; "));
|
|
49
67
|
}
|
|
50
68
|
|
|
51
|
-
sendToCandidate(delivery.
|
|
69
|
+
sendToCandidate(delivery.candidateSafeXml);
|
|
52
70
|
```
|
|
53
71
|
|
|
54
|
-
Use `
|
|
72
|
+
Use `prepareQtiDeliveryXml().ok` for deliverability. The
|
|
55
73
|
`analyzeQtiDeliverySecurity().deliverySafe` flag describes the exact XML being analyzed,
|
|
56
74
|
so it is normally `false` for an authoritative scorable item before redaction and `true`
|
|
57
75
|
only for the redacted output.
|
|
@@ -64,10 +82,34 @@ template processing, set-correct-response, and adaptive response processing.
|
|
|
64
82
|
String-range redaction uses the same dependency-free XML parser as `parseQtiXml`, with
|
|
65
83
|
source ranges recorded during parsing. XML parse failures are reported as `xml.parse`
|
|
66
84
|
error diagnostics; hosts must treat those diagnostics, `parseQtiXml().ok === false`,
|
|
67
|
-
and `
|
|
85
|
+
and `prepareQtiDeliveryXml().ok === false` as non-deliverable. The redacted output is
|
|
68
86
|
re-analyzed before `ok` is returned, but hosts should still treat redacted XML as
|
|
69
87
|
untrusted presentation input.
|
|
70
88
|
|
|
89
|
+
`prepareQtiDeliveryXml()` is the high-level host API. Use `mode: "static"` for
|
|
90
|
+
non-adaptive delivery where all response processing and feedback are removed. Use
|
|
91
|
+
`mode: "server-materialized-adaptive"` only after the server has authoritative outcomes
|
|
92
|
+
for the current adaptive view; pass those outcomes so visible feedback can be retained.
|
|
93
|
+
Pass `templateValues` when the item uses template-derived presentation (`qti-printed-variable`,
|
|
94
|
+
`qti-template-block`, or `qti-template-inline`). Adaptive turn processing supplies these
|
|
95
|
+
from session state automatically; hosts calling `prepareQtiDeliveryXml()` directly must
|
|
96
|
+
provide the same authoritative template snapshot. Generated answer keys, template and response
|
|
97
|
+
processing, mappings, lookup tables, declaration defaults, and hidden feedback are
|
|
98
|
+
stripped from candidate XML. Static delivery fails closed for adaptive response or
|
|
99
|
+
template processing where server materialization is required.
|
|
100
|
+
|
|
101
|
+
Use `analyzeQtiDeliverySecurity()` when a host only needs diagnostics about a specific
|
|
102
|
+
XML string, and `buildQtiDeliverySafeXml()` when a host explicitly wants the lower-level
|
|
103
|
+
static redaction primitive. Most delivery services should call `prepareQtiDeliveryXml()`
|
|
104
|
+
instead.
|
|
105
|
+
|
|
106
|
+
`prepareQtiDeliveryXml()` returns normalized `delivery.preparation.*` diagnostic codes
|
|
107
|
+
for delivery preparation failures across both modes. Hosts can key on
|
|
108
|
+
`result.diagnostics[].code` for logging, i18n, HTTP mapping, or telemetry when using the
|
|
109
|
+
facade. `result.analysis.findings[].kind` remains available as the structured semantic
|
|
110
|
+
contract for hosts that prefer mode-independent finding data. Lower-level APIs keep
|
|
111
|
+
their own diagnostic namespaces for callers that intentionally use those primitives.
|
|
112
|
+
|
|
71
113
|
The parser does not resolve external entities, process DTD entity declarations, or access
|
|
72
114
|
the network or filesystem. Unknown named entities are preserved verbatim, and numeric
|
|
73
115
|
character references expand to at most one valid XML character.
|
|
@@ -105,9 +147,63 @@ then runs response processing. It does not run candidate response-validation pol
|
|
|
105
147
|
as required interactions, cardinality limits, or min/max response counts; delivery hosts
|
|
106
148
|
should enforce that policy before accepting a submission or finalizing an attempt.
|
|
107
149
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
150
|
+
For the same response-validation policy the browser player uses, call
|
|
151
|
+
`validateQtiResponseVariables()` against a parsed assessment item before scoring or
|
|
152
|
+
persisting a submission:
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
import { parseQtiXml, validateQtiResponseVariables } from "@longsightgroup/qti3-core";
|
|
156
|
+
|
|
157
|
+
const parsed = parseQtiXml(authoritativeItemXml);
|
|
158
|
+
if (!parsed.ok) throw new Error("invalid item");
|
|
159
|
+
|
|
160
|
+
const validation = validateQtiResponseVariables({
|
|
161
|
+
item: parsed.document.item,
|
|
162
|
+
responses: { RESPONSE: "A" },
|
|
163
|
+
allowedUndeclaredResponseIdentifiers: ["duration"],
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
if (!validation.ok) {
|
|
167
|
+
throw new Error(validation.diagnostics.map((item) => item.message).join("; "));
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`validateQtiResponseVariables()` uses the same runtime diagnostic codes as the player
|
|
172
|
+
(`response.required`, `response.maximum`, `response.matchMax`, and related codes). It
|
|
173
|
+
checks cardinality shape, required responses, min/max choice and association bounds, and
|
|
174
|
+
per-choice `match-max` limits. It does not render custom validation messages in the DOM
|
|
175
|
+
or validate media play counts beyond what the item model exposes.
|
|
176
|
+
|
|
177
|
+
Use `materializeQtiItemSubmission()` when a server needs the reusable QTI mechanics behind
|
|
178
|
+
finalization: response validation, trusted response application, response processing,
|
|
179
|
+
adaptive turn materialization, normalized response/outcome snapshots, and a generic scoring
|
|
180
|
+
disposition:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { materializeQtiItemSubmission } from "@longsightgroup/qti3-core";
|
|
184
|
+
|
|
185
|
+
const materialized = materializeQtiItemSubmission({
|
|
186
|
+
itemXml: authoritativeItemXml,
|
|
187
|
+
existingState: priorAttemptState,
|
|
188
|
+
trustedResponses: { RESPONSE: "A" },
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
if (!materialized.ok) {
|
|
192
|
+
throw new Error(materialized.diagnostics.map((item) => item.message).join("; "));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
console.log(materialized.scoringDisposition);
|
|
196
|
+
console.log(materialized.responseVariables);
|
|
197
|
+
console.log(materialized.outcomeVariables);
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
`scoringDisposition` uses a default generic taxonomy from `qti3-core`: `scored`,
|
|
201
|
+
`manual-scoring-required`, `unscored-reference`, or `invalid`. Host applications can map
|
|
202
|
+
these dispositions to product-specific finalization statuses, grading queues, result
|
|
203
|
+
aggregation, and external exports.
|
|
204
|
+
|
|
205
|
+
Delivery redaction, server-style scoring, and secure adaptive turn handling are library
|
|
206
|
+
APIs. Dedicated CLI commands for those operations are planned separately.
|
|
111
207
|
|
|
112
208
|
### Secure adaptive turns
|
|
113
209
|
|
|
@@ -144,9 +240,10 @@ player.restore(turn.state);
|
|
|
144
240
|
Refresh turns with no new submission still return updated materialized XML derived from restored outcomes, so a resume flow should load both values again even when the candidate does not submit a new response.
|
|
145
241
|
|
|
146
242
|
The static delivery redactor still fails closed for adaptive response processing. Use
|
|
147
|
-
`processQtiAdaptiveItemTurn()` when a host needs a server-materialized candidate
|
|
148
|
-
and updated authoritative outcomes for the next adaptive turn.
|
|
149
|
-
|
|
243
|
+
`processQtiAdaptiveItemTurn()` when a host needs both a server-materialized candidate
|
|
244
|
+
view and updated authoritative outcomes for the next adaptive turn. Use
|
|
245
|
+
`prepareQtiDeliveryXml(..., { mode: "server-materialized-adaptive", outcomes })` only
|
|
246
|
+
when the host already owns the authoritative outcomes and needs to prepare candidate XML.
|
|
150
247
|
|
|
151
248
|
## Scope
|
|
152
249
|
|
|
@@ -3,6 +3,8 @@ import type { QtiDiagnostic, QtiValue } from "./types.js";
|
|
|
3
3
|
export interface QtiAdaptiveCandidateMaterializationInput {
|
|
4
4
|
itemXml: string;
|
|
5
5
|
outcomes: Record<string, QtiValue>;
|
|
6
|
+
templateValues?: Record<string, QtiValue>;
|
|
7
|
+
responses?: Record<string, QtiValue>;
|
|
6
8
|
}
|
|
7
9
|
export interface QtiAdaptiveCandidateMaterializationResult {
|
|
8
10
|
ok: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptive-turn-materializer.d.ts","sourceRoot":"","sources":["../src/adaptive-turn-materializer.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"adaptive-turn-materializer.d.ts","sourceRoot":"","sources":["../src/adaptive-turn-materializer.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,2BAA2B,EAEjC,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAQ1D,MAAM,WAAW,wCAAwC;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,yCAAyC;IACxD,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,QAAQ,EAAE,2BAA2B,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,wCAAwC,GAC9C,yCAAyC,CAiB3C"}
|
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import { FORBIDDEN_DELIVERY_SECRET_ELEMENT_NAMES, isDeclarationDefaultValue, redactDeliveryXml, } from "./delivery-redaction.js";
|
|
1
|
+
import { FORBIDDEN_DELIVERY_SECRET_ELEMENT_NAMES, analyzeDeliveryXml, isDeclarationDefaultValue, parseDeliveryXml, redactDeliveryXml, } from "./delivery-redaction.js";
|
|
2
|
+
import { materializeTemplatePresentation } from "./template-presentation-materializer.js";
|
|
2
3
|
import { qtiValueToString } from "./value-format.js";
|
|
3
|
-
const
|
|
4
|
-
"template-processing",
|
|
5
|
-
"set-correct-response",
|
|
6
|
-
]);
|
|
4
|
+
const TEMPLATE_PROCESSING_ELEMENT_NAMES = new Set(["template-processing", "set-correct-response"]);
|
|
7
5
|
const FEEDBACK_ELEMENT_NAMES = new Set(["feedback-inline", "feedback-block", "modal-feedback"]);
|
|
8
6
|
export function materializeAdaptiveCandidateView(input) {
|
|
9
|
-
|
|
7
|
+
const policy = adaptiveMaterializationPolicy(input.outcomes);
|
|
8
|
+
const templateMaterialization = materializeTemplatePresentation(input.itemXml, {
|
|
9
|
+
outcomes: input.outcomes,
|
|
10
|
+
templateValues: input.templateValues ?? {},
|
|
11
|
+
responses: input.responses,
|
|
12
|
+
});
|
|
13
|
+
if (!templateMaterialization.ok) {
|
|
14
|
+
const analysis = analyzeDeliveryXml(parseDeliveryXml(input.itemXml), policy);
|
|
15
|
+
const diagnostics = [...templateMaterialization.diagnostics, ...analysis.diagnostics];
|
|
16
|
+
return {
|
|
17
|
+
ok: false,
|
|
18
|
+
diagnostics,
|
|
19
|
+
analysis,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return redactDeliveryXml(templateMaterialization.xml, policy);
|
|
10
23
|
}
|
|
11
24
|
function shouldPreserveFeedback(node, outcomes) {
|
|
12
25
|
if (node.attributes["show-hide"] === "hide")
|
|
@@ -28,27 +41,20 @@ function adaptiveMaterializationPolicy(outcomes) {
|
|
|
28
41
|
isForbiddenElement(node, normalizedName) {
|
|
29
42
|
if (isFeedbackElement(normalizedName))
|
|
30
43
|
return !shouldPreserveFeedback(node, outcomes);
|
|
31
|
-
return (
|
|
44
|
+
return (TEMPLATE_PROCESSING_ELEMENT_NAMES.has(normalizedName) ||
|
|
45
|
+
FORBIDDEN_DELIVERY_SECRET_ELEMENT_NAMES.has(normalizedName) ||
|
|
32
46
|
isDeclarationDefaultValue(node, normalizedName));
|
|
33
47
|
},
|
|
34
|
-
unsupportedFinding(
|
|
35
|
-
|
|
36
|
-
return null;
|
|
37
|
-
return {
|
|
38
|
-
kind: "unsupported-secure-delivery-element",
|
|
39
|
-
qtiName: node.name,
|
|
40
|
-
localName: node.localName,
|
|
41
|
-
message: `${node.name} is not supported by adaptive candidate materialization v1.`,
|
|
42
|
-
source: node.source,
|
|
43
|
-
};
|
|
48
|
+
unsupportedFinding() {
|
|
49
|
+
return null;
|
|
44
50
|
},
|
|
45
|
-
diagnosticCodeForFinding,
|
|
51
|
+
diagnosticCodeForFinding: adaptiveMaterializationDiagnosticCodeForFinding,
|
|
46
52
|
forbiddenElementMessage(node) {
|
|
47
53
|
return `${node.name} exposes answer keys, scoring, mapping, feedback, or solution information during adaptive delivery.`;
|
|
48
54
|
},
|
|
49
55
|
};
|
|
50
56
|
}
|
|
51
|
-
function
|
|
57
|
+
function adaptiveMaterializationDiagnosticCodeForFinding(kind) {
|
|
52
58
|
return kind === "unsupported-secure-delivery-element"
|
|
53
59
|
? "adaptiveTurn.materialization.unsupported"
|
|
54
60
|
: "adaptiveTurn.materialization.forbiddenElement";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptive-turn-materializer.js","sourceRoot":"","sources":["../src/adaptive-turn-materializer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uCAAuC,EACvC,yBAAyB,EACzB,iBAAiB,GAIlB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"adaptive-turn-materializer.js","sourceRoot":"","sources":["../src/adaptive-turn-materializer.ts"],"names":[],"mappings":"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"}
|
package/dist/adaptive-turn.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QtiAttemptStateV1, QtiDiagnostic, QtiValue } from "./types.js";
|
|
2
|
-
import { type QtiTrustedItemParseOptions, type QtiTrustedResponseApplication, type
|
|
2
|
+
import { type QtiTrustedInputDiagnosticPrefix, type QtiTrustedItemDocument, type QtiTrustedItemParseOptions, type QtiTrustedResponseInput, type QtiTrustedResponseApplication, type QtiTrustedSubmissionValidation } from "./trusted-item-session.js";
|
|
3
3
|
export type QtiAdaptiveTurnResponseInput = QtiTrustedResponseInput;
|
|
4
4
|
export interface QtiAdaptiveTurnInput extends QtiTrustedItemParseOptions, QtiTrustedResponseApplication {
|
|
5
5
|
itemXml: string;
|
|
@@ -9,6 +9,12 @@ export interface QtiAdaptiveTurnInput extends QtiTrustedItemParseOptions, QtiTru
|
|
|
9
9
|
*/
|
|
10
10
|
priorState?: unknown;
|
|
11
11
|
}
|
|
12
|
+
export interface TrustedAdaptiveItemTurnInput extends QtiAdaptiveTurnInput {
|
|
13
|
+
parsedItem?: QtiTrustedItemDocument | undefined;
|
|
14
|
+
diagnosticPrefix: QtiTrustedInputDiagnosticPrefix;
|
|
15
|
+
submissionValidation?: QtiTrustedSubmissionValidation | undefined;
|
|
16
|
+
allowIncompleteResponses?: boolean | undefined;
|
|
17
|
+
}
|
|
12
18
|
export interface QtiAdaptiveTurnResult {
|
|
13
19
|
ok: boolean;
|
|
14
20
|
diagnostics: QtiDiagnostic[];
|
|
@@ -20,4 +26,5 @@ export interface QtiAdaptiveTurnResult {
|
|
|
20
26
|
candidateSafeXml?: string | undefined;
|
|
21
27
|
}
|
|
22
28
|
export declare function processQtiAdaptiveItemTurn(input: QtiAdaptiveTurnInput): QtiAdaptiveTurnResult;
|
|
29
|
+
export declare function runTrustedAdaptiveItemTurn(input: TrustedAdaptiveItemTurnInput): QtiAdaptiveTurnResult;
|
|
23
30
|
//# sourceMappingURL=adaptive-turn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptive-turn.d.ts","sourceRoot":"","sources":["../src/adaptive-turn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,6BAA6B,EAClC,KAAK,
|
|
1
|
+
{"version":3,"file":"adaptive-turn.d.ts","sourceRoot":"","sources":["../src/adaptive-turn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAGL,KAAK,+BAA+B,EACpC,KAAK,sBAAsB,EAC3B,KAAK,0BAA0B,EAC/B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EACpC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GAAG,uBAAuB,CAAC;AAEnE,MAAM,WAAW,oBACf,SAAQ,0BAA0B,EAAE,6BAA6B;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,4BAA6B,SAAQ,oBAAoB;IACxE,UAAU,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAChD,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,oBAAoB,CAAC,EAAE,8BAA8B,GAAG,SAAS,CAAC;IAClE,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,OAAO,CAAC;IACZ,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB,EAAE,QAAQ,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACvC;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAE7F;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,4BAA4B,GAClC,qBAAqB,CAiEvB"}
|
package/dist/adaptive-turn.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { materializeAdaptiveCandidateView } from "./adaptive-turn-materializer.js";
|
|
2
2
|
import { readPriorAttemptState, runTrustedItemSession, } from "./trusted-item-session.js";
|
|
3
3
|
export function processQtiAdaptiveItemTurn(input) {
|
|
4
|
-
|
|
4
|
+
return runTrustedAdaptiveItemTurn({ ...input, diagnosticPrefix: "adaptiveTurn" });
|
|
5
|
+
}
|
|
6
|
+
export function runTrustedAdaptiveItemTurn(input) {
|
|
7
|
+
const diagnosticPrefix = input.diagnosticPrefix;
|
|
8
|
+
const priorStateResult = readPriorAttemptState(input.priorState, diagnosticPrefix);
|
|
5
9
|
if (priorStateResult.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
6
10
|
return failed(priorStateResult.diagnostics);
|
|
7
11
|
}
|
|
8
12
|
const sessionResult = runTrustedItemSession({
|
|
9
13
|
itemXml: input.itemXml,
|
|
10
|
-
|
|
14
|
+
parsedItem: input.parsedItem,
|
|
15
|
+
diagnosticPrefix,
|
|
11
16
|
allowedUndeclaredResponseIdentifiers: input.allowedUndeclaredResponseIdentifiers,
|
|
12
17
|
priorState: priorStateResult.state,
|
|
13
18
|
submission: {
|
|
@@ -16,6 +21,8 @@ export function processQtiAdaptiveItemTurn(input) {
|
|
|
16
21
|
},
|
|
17
22
|
scoring: "onSubmission",
|
|
18
23
|
requireNumericScore: true,
|
|
24
|
+
submissionValidation: input.submissionValidation,
|
|
25
|
+
allowIncompleteResponses: input.allowIncompleteResponses,
|
|
19
26
|
});
|
|
20
27
|
if (!sessionResult.ok) {
|
|
21
28
|
return failed(sessionResult.diagnostics, sessionResult.state, sessionResult.outcomes, sessionResult.score);
|
|
@@ -23,13 +30,16 @@ export function processQtiAdaptiveItemTurn(input) {
|
|
|
23
30
|
const delivery = materializeAdaptiveCandidateView({
|
|
24
31
|
itemXml: input.itemXml,
|
|
25
32
|
outcomes: sessionResult.outcomes,
|
|
33
|
+
responses: sessionResult.responses,
|
|
34
|
+
templateValues: sessionResult.state.templateValues ?? {},
|
|
26
35
|
});
|
|
27
36
|
const deliveryDiagnostics = [...sessionResult.diagnostics, ...delivery.diagnostics];
|
|
28
|
-
|
|
37
|
+
const candidateSafeXml = delivery.xml;
|
|
38
|
+
if (!delivery.ok || !candidateSafeXml) {
|
|
29
39
|
return failed([
|
|
30
40
|
...deliveryDiagnostics,
|
|
31
41
|
{
|
|
32
|
-
code:
|
|
42
|
+
code: `${diagnosticPrefix}.delivery.materialization`,
|
|
33
43
|
severity: "error",
|
|
34
44
|
message: "Adaptive turn did not produce candidate-safe item XML.",
|
|
35
45
|
},
|
|
@@ -43,7 +53,7 @@ export function processQtiAdaptiveItemTurn(input) {
|
|
|
43
53
|
outcomes: sessionResult.outcomes,
|
|
44
54
|
score: sessionResult.score,
|
|
45
55
|
completionStatus: sessionResult.outcomes.completionStatus ?? null,
|
|
46
|
-
candidateSafeXml
|
|
56
|
+
candidateSafeXml,
|
|
47
57
|
};
|
|
48
58
|
}
|
|
49
59
|
function failed(diagnostics, state = null, outcomes = {}, score = null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adaptive-turn.js","sourceRoot":"","sources":["../src/adaptive-turn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AAEnF,OAAO,EACL,qBAAqB,EACrB,qBAAqB,
|
|
1
|
+
{"version":3,"file":"adaptive-turn.js","sourceRoot":"","sources":["../src/adaptive-turn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-never.d.ts","sourceRoot":"","sources":["../src/assert-never.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAEjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assert-never.js","sourceRoot":"","sources":["../src/assert-never.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,UAAU,WAAW,CAAC,KAAY,EAAE,OAAgB;IACxD,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,qBAAqB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const ATTEMPT_STATE_SCHEMA = "qti3.attempt-state.v1";
|
|
2
|
+
export declare const COMPLETION_STATUS = "completionStatus";
|
|
3
|
+
export declare const COMPLETION_NOT_ATTEMPTED = "not_attempted";
|
|
4
|
+
export declare const COMPLETION_UNKNOWN = "unknown";
|
|
5
|
+
export declare const COMPLETION_COMPLETED = "completed";
|
|
6
|
+
//# sourceMappingURL=attempt-state-constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-state-constants.d.ts","sourceRoot":"","sources":["../src/attempt-state-constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,0BAA0B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,qBAAqB,CAAC;AACpD,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,eAAO,MAAM,oBAAoB,cAAc,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const ATTEMPT_STATE_SCHEMA = "qti3.attempt-state.v1";
|
|
2
|
+
export const COMPLETION_STATUS = "completionStatus";
|
|
3
|
+
export const COMPLETION_NOT_ATTEMPTED = "not_attempted";
|
|
4
|
+
export const COMPLETION_UNKNOWN = "unknown";
|
|
5
|
+
export const COMPLETION_COMPLETED = "completed";
|
|
6
|
+
//# sourceMappingURL=attempt-state-constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-state-constants.js","sourceRoot":"","sources":["../src/attempt-state-constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,uBAAuB,CAAC;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,kBAAkB,CAAC;AACpD,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AACxD,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAC5C,MAAM,CAAC,MAAM,oBAAoB,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { QtiAttemptStateV1, QtiDocument } from "./types.js";
|
|
2
|
+
export declare function isQtiAttemptStateV1(value: unknown): value is QtiAttemptStateV1;
|
|
3
|
+
export declare function assertQtiAttemptStateV1(value: unknown): asserts value is QtiAttemptStateV1;
|
|
4
|
+
export declare function assertCompatiblePriorState(document: QtiDocument, priorState: QtiAttemptStateV1 | undefined): void;
|
|
5
|
+
//# sourceMappingURL=attempt-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-state.d.ts","sourceRoot":"","sources":["../src/attempt-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EAGjB,WAAW,EAKZ,MAAM,YAAY,CAAC;AAYpB,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,iBAAiB,CAE9E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,iBAAiB,CAG1F;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,EACrB,UAAU,EAAE,iBAAiB,GAAG,SAAS,GACxC,IAAI,CAyDN"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { ATTEMPT_STATE_SCHEMA, COMPLETION_COMPLETED, COMPLETION_NOT_ATTEMPTED, COMPLETION_STATUS, COMPLETION_UNKNOWN, } from "./attempt-state-constants.js";
|
|
2
|
+
import { isQtiPortableCustomStateValue, isQtiValue, qtiValueToString } from "./value-format.js";
|
|
3
|
+
import { isRecordValue } from "./processing-values.js";
|
|
4
|
+
import { isPair, isPoint } from "./validation-primitives.js";
|
|
5
|
+
export function isQtiAttemptStateV1(value) {
|
|
6
|
+
return attemptStateErrors(value).length === 0;
|
|
7
|
+
}
|
|
8
|
+
export function assertQtiAttemptStateV1(value) {
|
|
9
|
+
const [firstError] = attemptStateErrors(value);
|
|
10
|
+
if (firstError)
|
|
11
|
+
throw new Error(firstError);
|
|
12
|
+
}
|
|
13
|
+
export function assertCompatiblePriorState(document, priorState) {
|
|
14
|
+
if (!priorState)
|
|
15
|
+
return;
|
|
16
|
+
assertQtiAttemptStateV1(priorState);
|
|
17
|
+
if (priorState.itemIdentifier !== document.item.identifier) {
|
|
18
|
+
throw new Error(`Cannot restore state for ${priorState.itemIdentifier} into ${document.item.identifier}.`);
|
|
19
|
+
}
|
|
20
|
+
const responseIdentifiers = new Set(document.item.responseDeclarations.map((declaration) => declaration.identifier));
|
|
21
|
+
const outcomeIdentifiers = new Set([
|
|
22
|
+
...document.item.outcomeDeclarations.map((declaration) => declaration.identifier),
|
|
23
|
+
COMPLETION_STATUS,
|
|
24
|
+
]);
|
|
25
|
+
const templateIdentifiers = new Set(document.item.templateDeclarations.map((declaration) => declaration.identifier));
|
|
26
|
+
const interactionStateIdentifiers = new Set(document.item.interactions
|
|
27
|
+
.filter((interaction) => interaction.type === "portableCustom")
|
|
28
|
+
.map((interaction) => interaction.responseIdentifier)
|
|
29
|
+
.filter((identifier) => Boolean(identifier)));
|
|
30
|
+
assertKnownStateIdentifiers("response", priorState.responses, responseIdentifiers);
|
|
31
|
+
assertKnownStateIdentifiers("outcome", priorState.outcomes, outcomeIdentifiers);
|
|
32
|
+
assertKnownStateIdentifiers("template", priorState.templateValues ?? {}, templateIdentifiers);
|
|
33
|
+
assertKnownPortableCustomStateIdentifiers(priorState.interactionStates ?? {}, interactionStateIdentifiers);
|
|
34
|
+
for (const message of priorState.validationMessages) {
|
|
35
|
+
if (message.path && !responseIdentifiers.has(message.path)) {
|
|
36
|
+
throw new Error(`Cannot restore validation message for unknown response ${message.path}.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const declaration of document.item.responseDeclarations) {
|
|
40
|
+
assertRestoredValueMatchesDeclaration("response", declaration, priorState.responses);
|
|
41
|
+
}
|
|
42
|
+
for (const declaration of document.item.outcomeDeclarations) {
|
|
43
|
+
assertRestoredValueMatchesDeclaration("outcome", declaration, priorState.outcomes);
|
|
44
|
+
}
|
|
45
|
+
for (const declaration of document.item.templateDeclarations) {
|
|
46
|
+
assertRestoredValueMatchesDeclaration("template", declaration, priorState.templateValues ?? {});
|
|
47
|
+
}
|
|
48
|
+
const completionStatus = priorState.outcomes[COMPLETION_STATUS];
|
|
49
|
+
if (completionStatus !== undefined &&
|
|
50
|
+
completionStatus !== COMPLETION_NOT_ATTEMPTED &&
|
|
51
|
+
completionStatus !== COMPLETION_UNKNOWN &&
|
|
52
|
+
completionStatus !== COMPLETION_COMPLETED &&
|
|
53
|
+
completionStatus !== "incomplete") {
|
|
54
|
+
throw new Error(`Cannot restore unsupported completionStatus ${qtiValueToString(completionStatus)}.`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function assertKnownStateIdentifiers(kind, record, allowed) {
|
|
58
|
+
const unknown = Object.keys(record).find((identifier) => !allowed.has(identifier));
|
|
59
|
+
if (unknown)
|
|
60
|
+
throw new Error(`Cannot restore unknown ${kind} identifier ${unknown}.`);
|
|
61
|
+
}
|
|
62
|
+
function assertKnownPortableCustomStateIdentifiers(record, allowed) {
|
|
63
|
+
const unknown = Object.keys(record).find((identifier) => !allowed.has(identifier));
|
|
64
|
+
if (unknown)
|
|
65
|
+
throw new Error(`Cannot restore unknown interaction state identifier ${unknown}.`);
|
|
66
|
+
}
|
|
67
|
+
function assertRestoredValueMatchesDeclaration(kind, declaration, record) {
|
|
68
|
+
if (!(declaration.identifier in record))
|
|
69
|
+
return;
|
|
70
|
+
const value = record[declaration.identifier] ?? null;
|
|
71
|
+
const error = restoredValueError(declaration, value);
|
|
72
|
+
if (error)
|
|
73
|
+
throw new Error(`Cannot restore ${kind} ${declaration.identifier}: ${error}.`);
|
|
74
|
+
}
|
|
75
|
+
function restoredValueError(declaration, value) {
|
|
76
|
+
if (value === null)
|
|
77
|
+
return undefined;
|
|
78
|
+
if (declaration.cardinality === "record") {
|
|
79
|
+
return isRecordValue(value) ? undefined : "expected record value";
|
|
80
|
+
}
|
|
81
|
+
if (isRecordValue(value))
|
|
82
|
+
return "expected scalar value";
|
|
83
|
+
if (declaration.cardinality === "single" && Array.isArray(value)) {
|
|
84
|
+
return "expected single value";
|
|
85
|
+
}
|
|
86
|
+
if ((declaration.cardinality === "multiple" || declaration.cardinality === "ordered") &&
|
|
87
|
+
!Array.isArray(value)) {
|
|
88
|
+
return `expected ${declaration.cardinality} value container`;
|
|
89
|
+
}
|
|
90
|
+
if (!declaration.baseType)
|
|
91
|
+
return undefined;
|
|
92
|
+
for (const entry of restoredValueEntries(value)) {
|
|
93
|
+
if (!restoredScalarMatchesBaseType(entry, declaration.baseType)) {
|
|
94
|
+
return `value ${String(entry)} is not valid for base-type ${declaration.baseType}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
function restoredValueEntries(value) {
|
|
100
|
+
if (value === null || isRecordValue(value))
|
|
101
|
+
return [];
|
|
102
|
+
return Array.isArray(value) ? value : [value];
|
|
103
|
+
}
|
|
104
|
+
function restoredScalarMatchesBaseType(value, baseType) {
|
|
105
|
+
if (!baseType)
|
|
106
|
+
return true;
|
|
107
|
+
if (baseType === "integer") {
|
|
108
|
+
return typeof value === "number" ? Number.isInteger(value) : /^-?\d+$/.test(String(value));
|
|
109
|
+
}
|
|
110
|
+
if (baseType === "float") {
|
|
111
|
+
return typeof value === "number" ? Number.isFinite(value) : Number.isFinite(Number(value));
|
|
112
|
+
}
|
|
113
|
+
if (baseType === "boolean")
|
|
114
|
+
return typeof value === "boolean" || value === "true" || value === "false";
|
|
115
|
+
if (baseType === "point")
|
|
116
|
+
return isPoint(String(value));
|
|
117
|
+
if (baseType === "pair" || baseType === "directedPair")
|
|
118
|
+
return isPair(String(value));
|
|
119
|
+
if (baseType === "identifier")
|
|
120
|
+
return typeof value === "string" && value.trim().length > 0 && !/\s/.test(value);
|
|
121
|
+
return typeof value === "string";
|
|
122
|
+
}
|
|
123
|
+
function isAttemptStatus(value) {
|
|
124
|
+
return (value === "initialized" ||
|
|
125
|
+
value === "interacting" ||
|
|
126
|
+
value === "suspended" ||
|
|
127
|
+
value === "completed");
|
|
128
|
+
}
|
|
129
|
+
function attemptStateErrors(value) {
|
|
130
|
+
if (!isRecord(value))
|
|
131
|
+
return ["QTI attempt state must be an object."];
|
|
132
|
+
const schema = value.schema;
|
|
133
|
+
if (schema !== ATTEMPT_STATE_SCHEMA) {
|
|
134
|
+
return [`Unsupported QTI attempt state schema ${String(schema)}.`];
|
|
135
|
+
}
|
|
136
|
+
const errors = [];
|
|
137
|
+
if (typeof value.itemIdentifier !== "string" || value.itemIdentifier.length === 0) {
|
|
138
|
+
errors.push("QTI attempt state itemIdentifier must be a non-empty string.");
|
|
139
|
+
}
|
|
140
|
+
if (typeof value.status !== "string" || !isAttemptStatus(value.status)) {
|
|
141
|
+
errors.push(`QTI attempt state status ${String(value.status)} is not supported.`);
|
|
142
|
+
}
|
|
143
|
+
if (!isQtiValueRecord(value.responses)) {
|
|
144
|
+
errors.push("QTI attempt state responses must be a record of QTI values.");
|
|
145
|
+
}
|
|
146
|
+
if (!isQtiValueRecord(value.outcomes)) {
|
|
147
|
+
errors.push("QTI attempt state outcomes must be a record of QTI values.");
|
|
148
|
+
}
|
|
149
|
+
if (value.templateValues !== undefined && !isQtiValueRecord(value.templateValues)) {
|
|
150
|
+
errors.push("QTI attempt state templateValues must be a record of QTI values.");
|
|
151
|
+
}
|
|
152
|
+
if (value.interactionStates !== undefined &&
|
|
153
|
+
!isPortableCustomStateRecord(value.interactionStates)) {
|
|
154
|
+
errors.push("QTI attempt state interactionStates must be a record of JSON values.");
|
|
155
|
+
}
|
|
156
|
+
if (!isDiagnosticArray(value.validationMessages)) {
|
|
157
|
+
errors.push("QTI attempt state validationMessages must be an array of diagnostics.");
|
|
158
|
+
}
|
|
159
|
+
return errors;
|
|
160
|
+
}
|
|
161
|
+
function isQtiValueRecord(value) {
|
|
162
|
+
if (!isRecord(value))
|
|
163
|
+
return false;
|
|
164
|
+
return Object.values(value).every(isQtiValue);
|
|
165
|
+
}
|
|
166
|
+
function isPortableCustomStateRecord(value) {
|
|
167
|
+
if (!isRecord(value))
|
|
168
|
+
return false;
|
|
169
|
+
return Object.values(value).every(isQtiPortableCustomStateValue);
|
|
170
|
+
}
|
|
171
|
+
function isDiagnosticArray(value) {
|
|
172
|
+
if (!Array.isArray(value))
|
|
173
|
+
return false;
|
|
174
|
+
return value.every((item) => {
|
|
175
|
+
if (!isRecord(item))
|
|
176
|
+
return false;
|
|
177
|
+
if (typeof item.code !== "string" || item.code.length === 0)
|
|
178
|
+
return false;
|
|
179
|
+
if (item.severity !== "info" && item.severity !== "warning" && item.severity !== "error") {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
if (typeof item.message !== "string" || item.message.length === 0)
|
|
183
|
+
return false;
|
|
184
|
+
return item.path === undefined || typeof item.path === "string";
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function isRecord(value) {
|
|
188
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=attempt-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempt-state.js","sourceRoot":"","sources":["../src/attempt-state.ts"],"names":[],"mappings":"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"}
|
package/dist/catalog.js
CHANGED
|
@@ -39,7 +39,7 @@ function selectedCandidates(card, languages, options) {
|
|
|
39
39
|
rank: languageMatchRank(candidate.language, languages),
|
|
40
40
|
}))
|
|
41
41
|
.filter((entry) => Number.isInteger(entry.rank))
|
|
42
|
-
.
|
|
42
|
+
.toSorted((a, b) => a.rank - b.rank || a.index - b.index);
|
|
43
43
|
if (languageMatches.length > 0)
|
|
44
44
|
return languageMatches.map((entry) => entry.candidate);
|
|
45
45
|
if (options.includeDefaultFallback === false)
|