@longsightgroup/qti3-core 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -8
- 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/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 +26 -5
- 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 +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -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/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 +166 -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 +170 -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/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 +51 -46
- 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/validation-interactions.js +1 -0
- package/dist/validation-interactions.js.map +1 -1
- package/dist/validation-primitives.d.ts +1 -0
- package/dist/validation-primitives.d.ts.map +1 -1
- package/dist/validation-primitives.js +5 -0
- package/dist/validation-primitives.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/package.json +1 -1
- package/src/adaptive-turn-materializer.ts +29 -16
- package/src/adaptive-turn.ts +29 -6
- package/src/delivery-preparation.ts +121 -0
- package/src/delivery-redaction.ts +36 -5
- package/src/delivery-security.ts +2 -2
- package/src/index.ts +65 -0
- package/src/item-scoring-expectation.ts +9 -0
- package/src/item-submission-materialization.ts +120 -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 +226 -0
- package/src/response-validation.ts +298 -0
- package/src/scoring-disposition-policy.ts +37 -0
- package/src/template-presentation-materializer.ts +219 -0
- package/src/trusted-item-session.ts +80 -74
- package/src/trusted-session-materialization.ts +67 -0
- package/src/validation-interactions.ts +1 -0
- package/src/validation-primitives.ts +5 -0
- package/src/variable-snapshot.ts +20 -0
|
@@ -0,0 +1,170 @@
|
|
|
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, responseLimitAttribute, 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 = effectiveMinimumRequiredResponses(declaration, 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 effectiveMinimumRequiredResponses(declaration, interaction) {
|
|
143
|
+
const minimum = minimumRequiredResponses(interaction);
|
|
144
|
+
if (declaration.correctResponse === null || hasAuthoredMinimum(interaction))
|
|
145
|
+
return minimum;
|
|
146
|
+
return Math.max(minimum, 1);
|
|
147
|
+
}
|
|
148
|
+
function hasAuthoredMinimum(interaction) {
|
|
149
|
+
if (!interaction)
|
|
150
|
+
return false;
|
|
151
|
+
if (interaction.type === "media")
|
|
152
|
+
return interaction.attributes["min-plays"] !== undefined;
|
|
153
|
+
return responseLimitAttribute(interaction, "min-choices", "min-associations") !== undefined;
|
|
154
|
+
}
|
|
155
|
+
function attachResponseIdentifier(identifier, diagnostic) {
|
|
156
|
+
if (!isResponseValidationDiagnosticCode(diagnostic.code)) {
|
|
157
|
+
throw new Error(`Unexpected response validation code: ${diagnostic.code}`);
|
|
158
|
+
}
|
|
159
|
+
return { ...diagnostic, identifier, code: diagnostic.code };
|
|
160
|
+
}
|
|
161
|
+
function isResponseValidationDiagnosticCode(code) {
|
|
162
|
+
return (code === "response.required" ||
|
|
163
|
+
code === "response.maximum" ||
|
|
164
|
+
code === "response.matchMax" ||
|
|
165
|
+
code === "response.cardinality" ||
|
|
166
|
+
code === "response.undeclared" ||
|
|
167
|
+
code === "response.identifier.required" ||
|
|
168
|
+
code === "response.value.invalid");
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=response-validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-validation.js","sourceRoot":"","sources":["../src/response-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"trusted-item-session.d.ts","sourceRoot":"","sources":["../src/trusted-item-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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;
|