@longsightgroup/qti3-core 0.9.3 → 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.
Files changed (130) 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 +158 -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 +157 -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-primitives.d.ts +1 -0
  97. package/dist/validation-primitives.d.ts.map +1 -1
  98. package/dist/validation-primitives.js +5 -0
  99. package/dist/validation-primitives.js.map +1 -1
  100. package/dist/variable-snapshot.d.ts +9 -0
  101. package/dist/variable-snapshot.d.ts.map +1 -0
  102. package/dist/variable-snapshot.js +9 -0
  103. package/dist/variable-snapshot.js.map +1 -0
  104. package/package.json +1 -1
  105. package/src/adaptive-turn-materializer.ts +29 -16
  106. package/src/adaptive-turn.ts +29 -6
  107. package/src/delivery-preparation.ts +121 -0
  108. package/src/delivery-redaction.ts +36 -5
  109. package/src/delivery-security.ts +2 -2
  110. package/src/index.ts +65 -0
  111. package/src/item-scoring-expectation.ts +9 -0
  112. package/src/item-submission-materialization.ts +120 -0
  113. package/src/qti-package-assets.ts +256 -0
  114. package/src/qti-package-items.ts +240 -0
  115. package/src/qti-package-manifest.ts +210 -0
  116. package/src/qti-package-metadata.ts +143 -0
  117. package/src/qti-package-paths.ts +56 -0
  118. package/src/qti-package-types.ts +117 -0
  119. package/src/qti-package-xml.ts +97 -0
  120. package/src/qti-package-zip.ts +325 -0
  121. package/src/qti-package.ts +176 -0
  122. package/src/response-input.ts +19 -0
  123. package/src/response-validation-policy.ts +218 -0
  124. package/src/response-validation.ts +282 -0
  125. package/src/scoring-disposition-policy.ts +37 -0
  126. package/src/template-presentation-materializer.ts +219 -0
  127. package/src/trusted-item-session.ts +80 -74
  128. package/src/trusted-session-materialization.ts +67 -0
  129. package/src/validation-primitives.ts +5 -0
  130. package/src/variable-snapshot.ts +20 -0
@@ -0,0 +1,157 @@
1
+ import { assertNever } from "./assert-never.js";
2
+ import { isNullResponse, isRecordValue } from "./processing-values.js";
3
+ import { listNamedResponseInputs } from "./response-input.js";
4
+ import { matchMaxDiagnostics, maximumAllowedResponses, maximumResponseDiagnostic, mediaPlayCount, minimumRequiredResponses, requiredResponseDiagnostic, responseCount, responseValidationPolicy, } from "./response-validation-policy.js";
5
+ import { readQtiJsonValue } from "./value-format.js";
6
+ /** Validate submitted response variables against a parsed QTI assessment item. */
7
+ export function validateQtiResponseVariables(input) {
8
+ const diagnostics = [];
9
+ const declaredIdentifiers = new Set(input.item.responseDeclarations.map((declaration) => declaration.identifier));
10
+ const allowedUndeclaredIdentifiers = new Set(input.allowedUndeclaredResponseIdentifiers ?? []);
11
+ const responses = ingestSubmittedResponses(input.responses, declaredIdentifiers, allowedUndeclaredIdentifiers, diagnostics);
12
+ const scopedResponseIdentifiers = input.responseIdentifiers
13
+ ? new Set(input.responseIdentifiers)
14
+ : undefined;
15
+ const interactionsByResponse = indexInteractionsByResponse(input.item.interactions);
16
+ for (const declaration of input.item.responseDeclarations) {
17
+ if (scopedResponseIdentifiers !== undefined &&
18
+ !scopedResponseIdentifiers.has(declaration.identifier)) {
19
+ continue;
20
+ }
21
+ const value = responses.get(declaration.identifier);
22
+ if (value !== undefined) {
23
+ validateResponseCardinality(declaration, value, diagnostics);
24
+ }
25
+ const interactions = interactionsByResponse.get(declaration.identifier);
26
+ if (interactions === undefined) {
27
+ validateDeclarationResponse(declaration, undefined, value, input.allowIncompleteResponses, diagnostics);
28
+ continue;
29
+ }
30
+ for (const interaction of interactions) {
31
+ validateDeclarationResponse(declaration, interaction, value, input.allowIncompleteResponses, diagnostics);
32
+ }
33
+ }
34
+ return {
35
+ ok: diagnostics.every((diagnostic) => diagnostic.severity !== "error"),
36
+ diagnostics,
37
+ };
38
+ }
39
+ function ingestSubmittedResponses(input, declaredIdentifiers, allowedUndeclaredIdentifiers, diagnostics) {
40
+ const responses = new Map();
41
+ for (const entry of listNamedResponseInputs(input)) {
42
+ const identifier = entry.identifier.trim();
43
+ if (!identifier) {
44
+ diagnostics.push({
45
+ code: "response.identifier.required",
46
+ severity: "error",
47
+ message: "Response identifiers must be non-empty strings.",
48
+ });
49
+ continue;
50
+ }
51
+ const value = readQtiJsonValue(entry.value);
52
+ if (value === undefined) {
53
+ diagnostics.push({
54
+ code: "response.value.invalid",
55
+ severity: "error",
56
+ identifier,
57
+ message: `Response ${identifier} is not a supported QTI value.`,
58
+ path: identifier,
59
+ });
60
+ continue;
61
+ }
62
+ if (!declaredIdentifiers.has(identifier)) {
63
+ if (allowedUndeclaredIdentifiers.has(identifier))
64
+ continue;
65
+ diagnostics.push({
66
+ code: "response.undeclared",
67
+ severity: "error",
68
+ identifier,
69
+ message: `Response ${identifier} is not declared by the assessment item.`,
70
+ path: identifier,
71
+ });
72
+ continue;
73
+ }
74
+ responses.set(identifier, value);
75
+ }
76
+ return responses;
77
+ }
78
+ function indexInteractionsByResponse(interactions) {
79
+ const indexed = new Map();
80
+ for (const interaction of interactions) {
81
+ const identifier = interaction.responseIdentifier;
82
+ if (!identifier)
83
+ continue;
84
+ const bucket = indexed.get(identifier);
85
+ if (bucket) {
86
+ bucket.push(interaction);
87
+ }
88
+ else {
89
+ indexed.set(identifier, [interaction]);
90
+ }
91
+ }
92
+ return indexed;
93
+ }
94
+ function validateResponseCardinality(declaration, value, diagnostics) {
95
+ if (value === null)
96
+ return;
97
+ if (matchesCardinality(declaration.cardinality, value))
98
+ return;
99
+ diagnostics.push({
100
+ code: "response.cardinality",
101
+ severity: "error",
102
+ identifier: declaration.identifier,
103
+ message: `Response ${declaration.identifier} must match ${declaration.cardinality} cardinality.`,
104
+ path: declaration.identifier,
105
+ source: declaration.source,
106
+ });
107
+ }
108
+ function matchesCardinality(cardinality, value) {
109
+ switch (cardinality) {
110
+ case "single":
111
+ return !Array.isArray(value) && !isRecordValue(value);
112
+ case "multiple":
113
+ case "ordered":
114
+ return Array.isArray(value);
115
+ case "record":
116
+ return isRecordValue(value);
117
+ }
118
+ return assertNever(cardinality);
119
+ }
120
+ function validateDeclarationResponse(declaration, interaction, value, allowIncompleteResponses, diagnostics) {
121
+ const policy = responseValidationPolicy(declaration, interaction);
122
+ if (!policy.checkMinimum && !policy.checkMaximum && !policy.checkMatchMax)
123
+ return;
124
+ const effectiveValue = value ?? null;
125
+ const count = interaction?.type === "media" ? mediaPlayCount(effectiveValue) : responseCount(effectiveValue);
126
+ if (policy.checkMinimum && !allowIncompleteResponses) {
127
+ const minimum = minimumRequiredResponses(interaction);
128
+ if (count < minimum) {
129
+ diagnostics.push(attachResponseIdentifier(declaration.identifier, requiredResponseDiagnostic(declaration.identifier, interaction, minimum)));
130
+ }
131
+ }
132
+ if (policy.checkMaximum && value !== undefined && !isNullResponse(value)) {
133
+ const maximum = maximumAllowedResponses(interaction);
134
+ if (maximum !== undefined && count > maximum) {
135
+ diagnostics.push(attachResponseIdentifier(declaration.identifier, maximumResponseDiagnostic(declaration.identifier, interaction, maximum)));
136
+ }
137
+ }
138
+ if (policy.checkMatchMax && interaction && value !== undefined && !isNullResponse(value)) {
139
+ diagnostics.push(...matchMaxDiagnostics(declaration.identifier, interaction, value).map((diagnostic) => attachResponseIdentifier(declaration.identifier, diagnostic)));
140
+ }
141
+ }
142
+ function attachResponseIdentifier(identifier, diagnostic) {
143
+ if (!isResponseValidationDiagnosticCode(diagnostic.code)) {
144
+ throw new Error(`Unexpected response validation code: ${diagnostic.code}`);
145
+ }
146
+ return { ...diagnostic, identifier, code: diagnostic.code };
147
+ }
148
+ function isResponseValidationDiagnosticCode(code) {
149
+ return (code === "response.required" ||
150
+ code === "response.maximum" ||
151
+ code === "response.matchMax" ||
152
+ code === "response.cardinality" ||
153
+ code === "response.undeclared" ||
154
+ code === "response.identifier.required" ||
155
+ code === "response.value.invalid");
156
+ }
157
+ //# sourceMappingURL=response-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-validation.js","sourceRoot":"","sources":["../src/response-validation.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,uBAAuB,EAA8B,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,cAAc,EACd,wBAAwB,EACxB,0BAA0B,EAC1B,aAAa,EACb,wBAAwB,GACzB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAgCrD,kFAAkF;AAClF,MAAM,UAAU,4BAA4B,CAC1C,KAAiC;IAEjC,MAAM,WAAW,GAAsC,EAAE,CAAC;IAC1D,MAAM,mBAAmB,GAAG,IAAI,GAAG,CACjC,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAC7E,CAAC;IACF,MAAM,4BAA4B,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAC;IAC/F,MAAM,SAAS,GAAG,wBAAwB,CACxC,KAAK,CAAC,SAAS,EACf,mBAAmB,EACnB,4BAA4B,EAC5B,WAAW,CACZ,CAAC;IACF,MAAM,yBAAyB,GAAG,KAAK,CAAC,mBAAmB;QACzD,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,mBAAmB,CAAC;QACpC,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,sBAAsB,GAAG,2BAA2B,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAEpF,KAAK,MAAM,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC1D,IACE,yBAAyB,KAAK,SAAS;YACvC,CAAC,yBAAyB,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,EACtD,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,2BAA2B,CAAC,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,YAAY,GAAG,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QACxE,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,2BAA2B,CACzB,WAAW,EACX,SAAS,EACT,KAAK,EACL,KAAK,CAAC,wBAAwB,EAC9B,WAAW,CACZ,CAAC;YACF,SAAS;QACX,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;YACvC,2BAA2B,CACzB,WAAW,EACX,WAAW,EACX,KAAK,EACL,KAAK,CAAC,wBAAwB,EAC9B,WAAW,CACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC;QACtE,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,KAAgC,EAChC,mBAAwC,EACxC,4BAAiD,EACjD,WAA8C;IAE9C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,uBAAuB,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,8BAA8B;gBACpC,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,iDAAiD;aAC3D,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,wBAAwB;gBAC9B,QAAQ,EAAE,OAAO;gBACjB,UAAU;gBACV,OAAO,EAAE,YAAY,UAAU,gCAAgC;gBAC/D,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,IAAI,4BAA4B,CAAC,GAAG,CAAC,UAAU,CAAC;gBAAE,SAAS;YAC3D,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,qBAAqB;gBAC3B,QAAQ,EAAE,OAAO;gBACjB,UAAU;gBACV,OAAO,EAAE,YAAY,UAAU,0CAA0C;gBACzE,IAAI,EAAE,UAAU;aACjB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,SAAS,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,2BAA2B,CAClC,YAAuC;IAEvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;IACpD,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,WAAW,CAAC,kBAAkB,CAAC;QAClD,IAAI,CAAC,UAAU;YAAE,SAAS;QAC1B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,2BAA2B,CAClC,WAAmC,EACnC,KAAe,EACf,WAA8C;IAE9C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO;IAE3B,IAAI,kBAAkB,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC;QAAE,OAAO;IAC/D,WAAW,CAAC,IAAI,CAAC;QACf,IAAI,EAAE,sBAAsB;QAC5B,QAAQ,EAAE,OAAO;QACjB,UAAU,EAAE,WAAW,CAAC,UAAU;QAClC,OAAO,EAAE,YAAY,WAAW,CAAC,UAAU,eAAe,WAAW,CAAC,WAAW,eAAe;QAChG,IAAI,EAAE,WAAW,CAAC,UAAU;QAC5B,MAAM,EAAE,WAAW,CAAC,MAAM;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,kBAAkB,CAAC,WAA2B,EAAE,KAAe;IACtE,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,QAAQ;YACX,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACxD,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,KAAK,QAAQ;YACX,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,2BAA2B,CAClC,WAAmC,EACnC,WAAuC,EACvC,KAA2B,EAC3B,wBAA6C,EAC7C,WAA8C;IAE9C,MAAM,MAAM,GAAG,wBAAwB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAClE,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,CAAC,aAAa;QAAE,OAAO;IAElF,MAAM,cAAc,GAAG,KAAK,IAAI,IAAI,CAAC;IACrC,MAAM,KAAK,GACT,WAAW,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;IAEjG,IAAI,MAAM,CAAC,YAAY,IAAI,CAAC,wBAAwB,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;YACpB,WAAW,CAAC,IAAI,CACd,wBAAwB,CACtB,WAAW,CAAC,UAAU,EACtB,0BAA0B,CAAC,WAAW,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CACzE,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACzE,MAAM,OAAO,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,GAAG,OAAO,EAAE,CAAC;YAC7C,WAAW,CAAC,IAAI,CACd,wBAAwB,CACtB,WAAW,CAAC,UAAU,EACtB,yBAAyB,CAAC,WAAW,CAAC,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,CACxE,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,MAAM,CAAC,aAAa,IAAI,WAAW,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,WAAW,CAAC,IAAI,CACd,GAAG,mBAAmB,CAAC,WAAW,CAAC,UAAU,EAAE,WAAW,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CACpF,wBAAwB,CAAC,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,CAC7D,CACF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAC/B,UAAkB,EAClB,UAAyB;IAEzB,IAAI,CAAC,kCAAkC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,wCAAwC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,EAAE,GAAG,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,kCAAkC,CACzC,IAAY;IAEZ,OAAO,CACL,IAAI,KAAK,mBAAmB;QAC5B,IAAI,KAAK,kBAAkB;QAC3B,IAAI,KAAK,mBAAmB;QAC5B,IAAI,KAAK,sBAAsB;QAC/B,IAAI,KAAK,qBAAqB;QAC9B,IAAI,KAAK,8BAA8B;QACvC,IAAI,KAAK,wBAAwB,CAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { QtiAssessmentItem } from "./types.js";
2
+ export type QtiItemSubmissionScoringDisposition = "scored" | "manual-scoring-required" | "unscored-reference" | "invalid";
3
+ /** Interaction types that typically require human review even without response processing. */
4
+ export declare function itemHasManuallyScoredInteractions(item: QtiAssessmentItem): boolean;
5
+ export declare function itemNeedsScoringFollowUpWhenUnscored(item: QtiAssessmentItem): boolean;
6
+ /** Default generic disposition taxonomy shipped by qti3-core for submission materialization. */
7
+ export declare function classifyQtiItemScoringDisposition(item: QtiAssessmentItem, score: number | null): Exclude<QtiItemSubmissionScoringDisposition, "invalid">;
8
+ //# sourceMappingURL=scoring-disposition-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring-disposition-policy.d.ts","sourceRoot":"","sources":["../src/scoring-disposition-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAGpD,MAAM,MAAM,mCAAmC,GAC3C,QAAQ,GACR,yBAAyB,GACzB,oBAAoB,GACpB,SAAS,CAAC;AAUd,8FAA8F;AAC9F,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAIlF;AAED,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAErF;AAED,gGAAgG;AAChG,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,mCAAmC,EAAE,SAAS,CAAC,CAIzD"}
@@ -0,0 +1,24 @@
1
+ import { itemExpectsAutomatedScore } from "./item-scoring-expectation.js";
2
+ const MANUALLY_SCORED_INTERACTION_TYPES = new Set([
3
+ "custom",
4
+ "drawing",
5
+ "extendedText",
6
+ "portableCustom",
7
+ "upload",
8
+ ]);
9
+ /** Interaction types that typically require human review even without response processing. */
10
+ export function itemHasManuallyScoredInteractions(item) {
11
+ return item.interactions.some((interaction) => MANUALLY_SCORED_INTERACTION_TYPES.has(interaction.type));
12
+ }
13
+ export function itemNeedsScoringFollowUpWhenUnscored(item) {
14
+ return itemExpectsAutomatedScore(item) || itemHasManuallyScoredInteractions(item);
15
+ }
16
+ /** Default generic disposition taxonomy shipped by qti3-core for submission materialization. */
17
+ export function classifyQtiItemScoringDisposition(item, score) {
18
+ if (score !== null)
19
+ return "scored";
20
+ if (itemNeedsScoringFollowUpWhenUnscored(item))
21
+ return "manual-scoring-required";
22
+ return "unscored-reference";
23
+ }
24
+ //# sourceMappingURL=scoring-disposition-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scoring-disposition-policy.js","sourceRoot":"","sources":["../src/scoring-disposition-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAQ1E,MAAM,iCAAiC,GAAG,IAAI,GAAG,CAAS;IACxD,QAAQ;IACR,SAAS;IACT,cAAc;IACd,gBAAgB;IAChB,QAAQ;CACT,CAAC,CAAC;AAEH,8FAA8F;AAC9F,MAAM,UAAU,iCAAiC,CAAC,IAAuB;IACvE,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAC5C,iCAAiC,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CACxD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oCAAoC,CAAC,IAAuB;IAC1E,OAAO,yBAAyB,CAAC,IAAI,CAAC,IAAI,iCAAiC,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,iCAAiC,CAC/C,IAAuB,EACvB,KAAoB;IAEpB,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,QAAQ,CAAC;IACpC,IAAI,oCAAoC,CAAC,IAAI,CAAC;QAAE,OAAO,yBAAyB,CAAC;IACjF,OAAO,oBAAoB,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { QtiDiagnostic, QtiValue } from "./types.js";
2
+ export interface QtiTemplatePresentationVariables {
3
+ readonly outcomes: Record<string, QtiValue>;
4
+ readonly templateValues: Record<string, QtiValue>;
5
+ readonly responses?: Record<string, QtiValue> | undefined;
6
+ }
7
+ export declare function materializeTemplatePresentation(xml: string, variables: QtiTemplatePresentationVariables): {
8
+ ok: true;
9
+ xml: string;
10
+ } | {
11
+ ok: false;
12
+ diagnostics: QtiDiagnostic[];
13
+ };
14
+ //# sourceMappingURL=template-presentation-materializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-presentation-materializer.d.ts","sourceRoot":"","sources":["../src/template-presentation-materializer.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAU1D,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC;CAC3D;AAED,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,gCAAgC,GAC1C;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,WAAW,EAAE,aAAa,EAAE,CAAA;CAAE,CA4BzE"}
@@ -0,0 +1,143 @@
1
+ import { applySourceRangeEdits, parseDeliveryXml, sourceRangeFor, } from "./delivery-redaction.js";
2
+ import { qtiValueToString } from "./value-format.js";
3
+ import { descendants, escapeXmlText } from "./xml.js";
4
+ export function materializeTemplatePresentation(xml, variables) {
5
+ const parsed = parseDeliveryXml(xml);
6
+ if (!parsed.root)
7
+ return { ok: true, xml };
8
+ const diagnostics = [];
9
+ const edits = [];
10
+ const nodes = [parsed.root, ...descendants(parsed.root, () => true)];
11
+ for (const node of nodes) {
12
+ if (isInsideTemplateContentNode(node))
13
+ continue;
14
+ if (node.localName === "qti-printed-variable") {
15
+ const edit = printedVariableEdit(node, variables, diagnostics);
16
+ if (edit)
17
+ edits.push(edit);
18
+ continue;
19
+ }
20
+ if (node.localName === "qti-template-block" || node.localName === "qti-template-inline") {
21
+ const edit = templateContentEdit(xml, node, variables, diagnostics);
22
+ if (edit)
23
+ edits.push(edit);
24
+ }
25
+ }
26
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
27
+ return { ok: false, diagnostics };
28
+ }
29
+ return { ok: true, xml: applySourceRangeEdits(xml, edits) };
30
+ }
31
+ function printedVariableEdit(node, variables, diagnostics) {
32
+ const text = resolvePrintedVariableText(node, variables, diagnostics);
33
+ if (text === undefined)
34
+ return undefined;
35
+ const range = sourceRangeFor(node)[0];
36
+ if (!range) {
37
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
38
+ return undefined;
39
+ }
40
+ return { ...range, text };
41
+ }
42
+ function templateContentEdit(xml, node, variables, diagnostics) {
43
+ const binding = resolveTemplateBlockBinding(node, variables.templateValues, diagnostics);
44
+ if (!binding)
45
+ return undefined;
46
+ const range = sourceRangeFor(node)[0];
47
+ if (!range) {
48
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
49
+ return undefined;
50
+ }
51
+ return {
52
+ ...range,
53
+ text: isTemplateContentVisible(node, binding.value)
54
+ ? materializedContentXml(xml, node, variables, diagnostics)
55
+ : "",
56
+ };
57
+ }
58
+ function resolvePrintedVariableText(node, variables, diagnostics) {
59
+ const identifier = node.attributes.identifier;
60
+ if (!identifier) {
61
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
62
+ return undefined;
63
+ }
64
+ const value = resolvePresentationVariable(identifier, variables);
65
+ if (value === undefined) {
66
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
67
+ return undefined;
68
+ }
69
+ return escapeXmlText(qtiValueToString(value));
70
+ }
71
+ function resolvePresentationVariable(identifier, variables) {
72
+ return (variables.outcomes[identifier] ??
73
+ variables.templateValues[identifier] ??
74
+ variables.responses?.[identifier]);
75
+ }
76
+ function resolveTemplateBlockBinding(node, templateValues, diagnostics) {
77
+ const templateIdentifier = node.attributes["template-identifier"];
78
+ const identifier = node.attributes.identifier;
79
+ if (!templateIdentifier || !identifier || templateValues[templateIdentifier] === undefined) {
80
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
81
+ return undefined;
82
+ }
83
+ return { templateIdentifier, identifier, value: templateValues[templateIdentifier] };
84
+ }
85
+ function materializedContentXml(xml, node, variables, diagnostics) {
86
+ return node.content
87
+ .map((child) => {
88
+ if (typeof child === "string")
89
+ return escapeXmlText(child);
90
+ if (child.localName === "qti-printed-variable") {
91
+ return resolvePrintedVariableText(child, variables, diagnostics) ?? "";
92
+ }
93
+ if (child.localName === "qti-template-block" || child.localName === "qti-template-inline") {
94
+ return materializedNestedTemplateContentXml(xml, child, variables, diagnostics);
95
+ }
96
+ return materializedElementXml(xml, child, variables, diagnostics);
97
+ })
98
+ .join("");
99
+ }
100
+ function materializedNestedTemplateContentXml(xml, node, variables, diagnostics) {
101
+ const binding = resolveTemplateBlockBinding(node, variables.templateValues, diagnostics);
102
+ if (!binding)
103
+ return "";
104
+ return isTemplateContentVisible(node, binding.value)
105
+ ? materializedContentXml(xml, node, variables, diagnostics)
106
+ : "";
107
+ }
108
+ function isTemplateContentVisible(node, value) {
109
+ const identifier = node.attributes.identifier;
110
+ const hasIdentifier = Array.isArray(value)
111
+ ? value.map(String).includes(identifier ?? "")
112
+ : qtiValueToString(value) === identifier;
113
+ return node.attributes["show-hide"] === "hide" ? !hasIdentifier : hasIdentifier;
114
+ }
115
+ function materializedElementXml(xml, node, variables, diagnostics) {
116
+ if (node.sourceRange.endOffset === undefined || node.endSource === undefined) {
117
+ diagnostics.push(unsupportedTemplateMaterializationDiagnostic(node));
118
+ return "";
119
+ }
120
+ const startTag = xml.slice(node.sourceRange.startOffset, node.sourceRange.startTagEndOffset + 1);
121
+ const endTag = xml.slice(node.endSource.offset, node.sourceRange.endOffset);
122
+ return `${startTag}${materializedContentXml(xml, node, variables, diagnostics)}${endTag}`;
123
+ }
124
+ function isInsideTemplateContentNode(node) {
125
+ let parent = node.parent;
126
+ while (parent) {
127
+ if (parent.localName === "qti-template-block" || parent.localName === "qti-template-inline") {
128
+ return true;
129
+ }
130
+ parent = parent.parent;
131
+ }
132
+ return false;
133
+ }
134
+ function unsupportedTemplateMaterializationDiagnostic(node) {
135
+ return {
136
+ code: "adaptiveTurn.materialization.unsupported",
137
+ severity: "error",
138
+ message: `${node.name} could not be materialized from authoritative template state.`,
139
+ path: node.source.path,
140
+ source: node.source,
141
+ };
142
+ }
143
+ //# sourceMappingURL=template-presentation-materializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-presentation-materializer.js","sourceRoot":"","sources":["../src/template-presentation-materializer.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,9 @@
1
- import type { QtiAttemptStateV1, QtiAttemptStatus, QtiDiagnostic, QtiPortableCustomStateValue, QtiValue } from "./types.js";
2
- export type QtiTrustedInputDiagnosticPrefix = "serverScoring" | "adaptiveTurn";
3
- export interface QtiTrustedResponseInput {
4
- identifier: string;
5
- value: unknown;
6
- }
7
- export type QtiTrustedResponsesInput = Record<string, unknown> | readonly QtiTrustedResponseInput[] | undefined;
8
- /** Trusted candidate responses and portable custom interaction state for one application. */
1
+ import type { QtiAttemptStateV1, QtiAttemptStatus, QtiDiagnostic, QtiDocument, QtiPortableCustomStateValue, QtiValue } from "./types.js";
2
+ import { type QtiNamedResponseInput, type QtiNamedResponsesInput } from "./response-input.js";
3
+ export type QtiTrustedResponseInput = QtiNamedResponseInput;
4
+ export type QtiTrustedResponsesInput = QtiNamedResponsesInput;
5
+ export type QtiTrustedInputDiagnosticPrefix = "serverScoring" | "adaptiveTurn" | "itemSubmission";
6
+ export type QtiTrustedSubmissionValidation = "trusted" | "strict";
9
7
  export interface QtiTrustedResponseApplication {
10
8
  trustedResponses?: QtiTrustedResponsesInput;
11
9
  trustedInteractionStates?: Record<string, QtiPortableCustomStateValue> | undefined;
@@ -13,9 +11,15 @@ export interface QtiTrustedResponseApplication {
13
11
  export interface QtiTrustedItemParseOptions {
14
12
  allowedUndeclaredResponseIdentifiers?: readonly string[] | undefined;
15
13
  }
14
+ export interface QtiTrustedItemDocument {
15
+ document: QtiDocument;
16
+ diagnostics: QtiDiagnostic[];
17
+ responseIdentifiers: Set<string>;
18
+ }
16
19
  export type QtiTrustedItemScoringPolicy = "always" | "onSubmission";
17
20
  export interface RunTrustedItemSessionInput extends QtiTrustedItemParseOptions {
18
21
  itemXml: string;
22
+ parsedItem?: QtiTrustedItemDocument | undefined;
19
23
  diagnosticPrefix: QtiTrustedInputDiagnosticPrefix;
20
24
  submission: QtiTrustedResponseApplication;
21
25
  priorState?: QtiAttemptStateV1 | null | undefined;
@@ -23,6 +27,12 @@ export interface RunTrustedItemSessionInput extends QtiTrustedItemParseOptions {
23
27
  attemptStatus?: QtiAttemptStatus | undefined;
24
28
  scoring: QtiTrustedItemScoringPolicy;
25
29
  requireNumericScore: boolean;
30
+ /**
31
+ * `strict` rejects undeclared and malformed responses before trusted application.
32
+ * `trusted` keeps the legacy warning path for undeclared identifiers.
33
+ */
34
+ submissionValidation?: QtiTrustedSubmissionValidation | undefined;
35
+ allowIncompleteResponses?: boolean | undefined;
26
36
  }
27
37
  export interface RunTrustedItemSessionSuccess {
28
38
  ok: true;
@@ -45,5 +55,12 @@ export declare function readPriorAttemptState(priorState: unknown, diagnosticPre
45
55
  state: QtiAttemptStateV1 | null;
46
56
  diagnostics: QtiDiagnostic[];
47
57
  };
58
+ export declare function parseTrustedItemDocument(itemXml: string, allowedUndeclaredResponseIdentifiers?: readonly string[]): {
59
+ ok: true;
60
+ parsed: QtiTrustedItemDocument;
61
+ } | {
62
+ ok: false;
63
+ diagnostics: QtiDiagnostic[];
64
+ };
48
65
  export declare function runTrustedItemSession(input: RunTrustedItemSessionInput): RunTrustedItemSessionResult;
49
66
  //# sourceMappingURL=trusted-item-session.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"trusted-item-session.d.ts","sourceRoot":"","sources":["../src/trusted-item-session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EAEb,2BAA2B,EAC3B,QAAQ,EACT,MAAM,YAAY,CAAC;AAGpB,MAAM,MAAM,+BAA+B,GAAG,eAAe,GAAG,cAAc,CAAC;AAE/E,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,wBAAwB,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,SAAS,uBAAuB,EAAE,GAClC,SAAS,CAAC;AAEd,6FAA6F;AAC7F,MAAM,WAAW,6BAA6B;IAC5C,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,GAAG,SAAS,CAAC;CACpF;AAED,MAAM,WAAW,0BAA0B;IACzC,oCAAoC,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACtE;AAQD,MAAM,MAAM,2BAA2B,GAAG,QAAQ,GAAG,cAAc,CAAC;AAEpE,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,UAAU,EAAE,6BAA6B,CAAC;IAC1C,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,uFAAuF;IACvF,aAAa,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,2BAA2B,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,KAAK,EAAE,iBAAiB,CAAC;IACzB,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;CACtB;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,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;CACtB;AAED,MAAM,MAAM,2BAA2B,GACnC,4BAA4B,GAC5B,4BAA4B,CAAC;AAEjC,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,OAAO,EACnB,gBAAgB,EAAE,+BAA+B,GAChD;IAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,aAAa,EAAE,CAAA;CAAE,CAanE;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,0BAA0B,GAChC,2BAA2B,CAiG7B"}
1
+ {"version":3,"file":"trusted-item-session.d.ts","sourceRoot":"","sources":["../src/trusted-item-session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACb,WAAW,EACX,2BAA2B,EAC3B,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC5B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAE5D,MAAM,MAAM,wBAAwB,GAAG,sBAAsB,CAAC;AAE9D,MAAM,MAAM,+BAA+B,GAAG,eAAe,GAAG,cAAc,GAAG,gBAAgB,CAAC;AAElG,MAAM,MAAM,8BAA8B,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElE,MAAM,WAAW,6BAA6B;IAC5C,gBAAgB,CAAC,EAAE,wBAAwB,CAAC;IAC5C,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,2BAA2B,CAAC,GAAG,SAAS,CAAC;CACpF;AAED,MAAM,WAAW,0BAA0B;IACzC,oCAAoC,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CACtE;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,mBAAmB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,MAAM,2BAA2B,GAAG,QAAQ,GAAG,cAAc,CAAC;AAEpE,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAChD,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,UAAU,EAAE,6BAA6B,CAAC;IAC1C,UAAU,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,uFAAuF;IACvF,aAAa,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7C,OAAO,EAAE,2BAA2B,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,8BAA8B,GAAG,SAAS,CAAC;IAClE,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChD;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,IAAI,CAAC;IACT,WAAW,EAAE,aAAa,EAAE,CAAC;IAC7B,KAAK,EAAE,iBAAiB,CAAC;IACzB,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;CACtB;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,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;CACtB;AAED,MAAM,MAAM,2BAA2B,GACnC,4BAA4B,GAC5B,4BAA4B,CAAC;AAEjC,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,OAAO,EACnB,gBAAgB,EAAE,+BAA+B,GAChD;IAAE,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAAC,WAAW,EAAE,aAAa,EAAE,CAAA;CAAE,CAanE;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,oCAAoC,GAAE,SAAS,MAAM,EAAO,GAC3D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,WAAW,EAAE,aAAa,EAAE,CAAA;CAAE,CAmC5F;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,0BAA0B,GAChC,2BAA2B,CA4G7B"}
@@ -1,6 +1,9 @@
1
1
  import { parseQtiXml } from "./parser.js";
2
+ import { itemExpectsAutomatedScore } from "./item-scoring-expectation.js";
3
+ import { validateQtiResponseVariables } from "./response-validation.js";
2
4
  import { createItemSession, isQtiAttemptStateV1 } from "./session.js";
3
5
  import { isQtiPortableCustomStateValue, readQtiJsonValue } from "./value-format.js";
6
+ import { listNamedResponseInputs, } from "./response-input.js";
4
7
  export function readPriorAttemptState(priorState, diagnosticPrefix) {
5
8
  if (priorState === undefined || priorState === null)
6
9
  return { state: null, diagnostics: [] };
@@ -12,13 +15,46 @@ export function readPriorAttemptState(priorState, diagnosticPrefix) {
12
15
  {
13
16
  code: `${diagnosticPrefix}.state.value`,
14
17
  severity: "error",
15
- message: "Prior adaptive turn state is not a valid qti3.attempt-state.v1 value.",
18
+ message: "Prior attempt state is not a valid qti3.attempt-state.v1 value.",
16
19
  },
17
20
  ],
18
21
  };
19
22
  }
23
+ export function parseTrustedItemDocument(itemXml, allowedUndeclaredResponseIdentifiers = []) {
24
+ let parsed;
25
+ try {
26
+ parsed = parseQtiXml(itemXml);
27
+ }
28
+ catch (error) {
29
+ return {
30
+ ok: false,
31
+ diagnostics: [
32
+ {
33
+ code: "xml.parse",
34
+ severity: "error",
35
+ message: error instanceof Error ? error.message : String(error),
36
+ },
37
+ ],
38
+ };
39
+ }
40
+ const allowedUndeclared = new Set(allowedUndeclaredResponseIdentifiers);
41
+ const diagnostics = parsed.diagnostics.filter((diagnostic) => !isAllowedUndeclaredVariableReference(diagnostic, allowedUndeclared));
42
+ if (!parsed.document || diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
43
+ return { ok: false, diagnostics };
44
+ }
45
+ return {
46
+ ok: true,
47
+ parsed: {
48
+ document: parsed.document,
49
+ diagnostics,
50
+ responseIdentifiers: new Set(parsed.document.item.responseDeclarations.map((declaration) => declaration.identifier)),
51
+ },
52
+ };
53
+ }
20
54
  export function runTrustedItemSession(input) {
21
- const parsedResult = parseTrustedItemXml(input.itemXml, input.allowedUndeclaredResponseIdentifiers);
55
+ const parsedResult = input.parsedItem
56
+ ? { ok: true, parsed: input.parsedItem }
57
+ : parseTrustedItemDocument(input.itemXml, input.allowedUndeclaredResponseIdentifiers);
22
58
  if (!parsedResult.ok) {
23
59
  return emptyTrustedItemSessionFailure(parsedResult.diagnostics);
24
60
  }
@@ -28,6 +64,17 @@ export function runTrustedItemSession(input) {
28
64
  }
29
65
  if (input.attemptStatus)
30
66
  sessionResult.session.setStatus(input.attemptStatus);
67
+ if (input.submissionValidation === "strict") {
68
+ const validation = validateQtiResponseVariables({
69
+ item: parsedResult.parsed.document.item,
70
+ responses: input.submission.trustedResponses ?? {},
71
+ allowIncompleteResponses: input.allowIncompleteResponses,
72
+ allowedUndeclaredResponseIdentifiers: input.allowedUndeclaredResponseIdentifiers,
73
+ });
74
+ if (!validation.ok) {
75
+ return emptyTrustedItemSessionFailure(validation.diagnostics);
76
+ }
77
+ }
31
78
  const applicationResult = applyTrustedResponseApplication(sessionResult.session, parsedResult.parsed, input.submission, input.allowedUndeclaredResponseIdentifiers, input.diagnosticPrefix);
32
79
  const diagnostics = applicationResult.diagnostics;
33
80
  if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
@@ -56,7 +103,7 @@ export function runTrustedItemSession(input) {
56
103
  });
57
104
  }
58
105
  if (input.requireNumericScore &&
59
- shouldRequireNumericScore(parsedResult.parsed.document.item) &&
106
+ itemExpectsAutomatedScore(parsedResult.parsed.document.item) &&
60
107
  score === null) {
61
108
  return emptyTrustedItemSessionFailure([...scoredDiagnostics, missingNumericScoreDiagnostic(input.diagnosticPrefix)], {
62
109
  state,
@@ -90,37 +137,6 @@ function emptyTrustedItemSessionFailure(diagnostics, partial = {}) {
90
137
  score: partial.score ?? null,
91
138
  };
92
139
  }
93
- function parseTrustedItemXml(itemXml, allowedUndeclaredResponseIdentifiers = []) {
94
- let parsed;
95
- try {
96
- parsed = parseQtiXml(itemXml);
97
- }
98
- catch (error) {
99
- return {
100
- ok: false,
101
- diagnostics: [
102
- {
103
- code: "xml.parse",
104
- severity: "error",
105
- message: error instanceof Error ? error.message : String(error),
106
- },
107
- ],
108
- };
109
- }
110
- const allowedUndeclared = new Set(allowedUndeclaredResponseIdentifiers);
111
- const diagnostics = parsed.diagnostics.filter((diagnostic) => !isAllowedUndeclaredVariableReference(diagnostic, allowedUndeclared));
112
- if (!parsed.document || diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
113
- return { ok: false, diagnostics };
114
- }
115
- return {
116
- ok: true,
117
- parsed: {
118
- document: parsed.document,
119
- diagnostics,
120
- responseIdentifiers: new Set(parsed.document.item.responseDeclarations.map((declaration) => declaration.identifier)),
121
- },
122
- };
123
- }
124
140
  function createTrustedItemSession(parsed, priorState, diagnosticPrefix, allowedUndeclaredResponseIdentifiers, existingDiagnostics = []) {
125
141
  try {
126
142
  return {
@@ -148,7 +164,7 @@ function applyTrustedResponseApplication(session, parsed, submission, allowedUnd
148
164
  const diagnostics = [...parsed.diagnostics];
149
165
  const allowedUndeclared = new Set(allowedUndeclaredResponseIdentifiers ?? []);
150
166
  let appliedSubmission = false;
151
- for (const response of normalizeResponseInputs(submission.trustedResponses)) {
167
+ for (const response of listNamedResponseInputs(submission.trustedResponses)) {
152
168
  const identifier = response.identifier.trim();
153
169
  if (!identifier) {
154
170
  diagnostics.push({
@@ -219,10 +235,6 @@ function scoreTrustedItemSession(session, diagnostics, diagnosticPrefix) {
219
235
  };
220
236
  }
221
237
  }
222
- function shouldRequireNumericScore(item) {
223
- return (Boolean(item.responseProcessing) ||
224
- item.outcomeDeclarations.some((declaration) => declaration.identifier === "SCORE"));
225
- }
226
238
  function readNumericScore(value) {
227
239
  if (typeof value === "number" && Number.isFinite(value))
228
240
  return value;
@@ -245,13 +257,6 @@ function stripUndeclaredResponses(state, declaredResponseIdentifiers) {
245
257
  responses: Object.fromEntries(Object.entries(state.responses).filter(([identifier]) => declaredResponseIdentifiers.has(identifier))),
246
258
  };
247
259
  }
248
- function normalizeResponseInputs(responses) {
249
- if (!responses)
250
- return [];
251
- if (Array.isArray(responses))
252
- return [...responses];
253
- return Object.entries(responses).map(([identifier, value]) => ({ identifier, value }));
254
- }
255
260
  function isAllowedUndeclaredVariableReference(diagnostic, allowedUndeclaredResponseIdentifiers) {
256
261
  if (diagnostic.code !== "processing.variable.reference")
257
262
  return false;