@longsightgroup/qti3-core 0.10.1 → 0.10.3
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 +21 -2
- package/dist/attempt-state.d.ts.map +1 -1
- package/dist/attempt-state.js +18 -0
- package/dist/attempt-state.js.map +1 -1
- package/dist/content-text.d.ts +4 -0
- package/dist/content-text.d.ts.map +1 -1
- package/dist/content-text.js +112 -9
- package/dist/content-text.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/media-definition.d.ts +55 -0
- package/dist/media-definition.d.ts.map +1 -0
- package/dist/media-definition.js +97 -0
- package/dist/media-definition.js.map +1 -0
- package/dist/parser-declarations.d.ts +4 -4
- package/dist/parser-declarations.d.ts.map +1 -1
- package/dist/parser-declarations.js +87 -46
- package/dist/parser-declarations.js.map +1 -1
- package/dist/parser-item-metadata.d.ts.map +1 -1
- package/dist/parser-item-metadata.js +4 -3
- package/dist/parser-item-metadata.js.map +1 -1
- package/dist/parser-processing.js +10 -9
- package/dist/parser-processing.js.map +1 -1
- package/dist/parser-values.d.ts +2 -0
- package/dist/parser-values.d.ts.map +1 -1
- package/dist/parser-values.js +7 -0
- package/dist/parser-values.js.map +1 -1
- package/dist/parser.js +16 -13
- package/dist/parser.js.map +1 -1
- package/dist/processing-evaluator-collection.d.ts.map +1 -1
- package/dist/processing-evaluator-collection.js +8 -2
- package/dist/processing-evaluator-collection.js.map +1 -1
- package/dist/processing-evaluator-comparison.d.ts.map +1 -1
- package/dist/processing-evaluator-comparison.js +6 -0
- package/dist/processing-evaluator-comparison.js.map +1 -1
- package/dist/processing-evaluator-numeric.d.ts.map +1 -1
- package/dist/processing-evaluator-numeric.js +8 -1
- package/dist/processing-evaluator-numeric.js.map +1 -1
- package/dist/processing-expression-children.d.ts.map +1 -1
- package/dist/processing-expression-children.js +2 -1
- package/dist/processing-expression-children.js.map +1 -1
- package/dist/processing-limits.d.ts +3 -0
- package/dist/processing-limits.d.ts.map +1 -0
- package/dist/processing-limits.js +3 -0
- package/dist/processing-limits.js.map +1 -0
- package/dist/processing-mapping.d.ts.map +1 -1
- package/dist/processing-mapping.js +13 -2
- package/dist/processing-mapping.js.map +1 -1
- package/dist/processing-operators.d.ts +2 -0
- package/dist/processing-operators.d.ts.map +1 -1
- package/dist/processing-operators.js +34 -5
- package/dist/processing-operators.js.map +1 -1
- package/dist/response-validation-policy.d.ts.map +1 -1
- package/dist/response-validation-policy.js +5 -6
- package/dist/response-validation-policy.js.map +1 -1
- package/dist/serializer-processing-expressions.js +1 -1
- package/dist/serializer-processing-expressions.js.map +1 -1
- package/dist/slider-definition.d.ts +59 -0
- package/dist/slider-definition.d.ts.map +1 -0
- package/dist/slider-definition.js +198 -0
- package/dist/slider-definition.js.map +1 -0
- package/dist/support-evidence.d.ts +6 -2
- package/dist/support-evidence.d.ts.map +1 -1
- package/dist/support-evidence.js +15 -11
- package/dist/support-evidence.js.map +1 -1
- package/dist/support.d.ts.map +1 -1
- package/dist/support.js +96 -100
- package/dist/support.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation-declaration-entries.d.ts +13 -0
- package/dist/validation-declaration-entries.d.ts.map +1 -0
- package/dist/validation-declaration-entries.js +136 -0
- package/dist/validation-declaration-entries.js.map +1 -0
- package/dist/validation-interactions.d.ts.map +1 -1
- package/dist/validation-interactions.js +14 -46
- package/dist/validation-interactions.js.map +1 -1
- package/dist/validation-primitives.js +2 -2
- package/dist/validation-primitives.js.map +1 -1
- package/dist/validation-processing.d.ts.map +1 -1
- package/dist/validation-processing.js +125 -1
- package/dist/validation-processing.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +8 -134
- package/dist/validation.js.map +1 -1
- package/dist/xml.d.ts.map +1 -1
- package/dist/xml.js +3 -2
- package/dist/xml.js.map +1 -1
- package/package.json +1 -1
- package/src/attempt-state.ts +20 -0
- package/src/content-text.ts +135 -11
- package/src/index.ts +25 -0
- package/src/media-definition.ts +174 -0
- package/src/parser-declarations.ts +134 -40
- package/src/parser-item-metadata.ts +4 -3
- package/src/parser-processing.ts +10 -9
- package/src/parser-values.ts +7 -0
- package/src/parser.ts +16 -13
- package/src/processing-evaluator-collection.ts +7 -2
- package/src/processing-evaluator-comparison.ts +6 -0
- package/src/processing-evaluator-numeric.ts +5 -1
- package/src/processing-expression-children.ts +2 -1
- package/src/processing-limits.ts +2 -0
- package/src/processing-mapping.ts +10 -2
- package/src/processing-operators.ts +38 -5
- package/src/response-validation-policy.ts +5 -5
- package/src/serializer-processing-expressions.ts +1 -1
- package/src/slider-definition.ts +363 -0
- package/src/support-evidence.ts +15 -17
- package/src/support.ts +106 -102
- package/src/types.ts +8 -1
- package/src/validation-declaration-entries.ts +162 -0
- package/src/validation-interactions.ts +14 -64
- package/src/validation-primitives.ts +2 -2
- package/src/validation-processing.ts +145 -1
- package/src/validation.ts +11 -151
- package/src/xml.ts +3 -4
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
QtiTemplateRule,
|
|
9
9
|
} from "./types.js";
|
|
10
10
|
import { expressionChildren } from "./processing-expression-children.js";
|
|
11
|
+
import { MAX_QTI_REPEAT_RESULT_ELEMENTS } from "./processing-limits.js";
|
|
11
12
|
import { MATH_OPERATOR_NAMES, STATS_OPERATOR_NAMES } from "./processing-operators.js";
|
|
12
13
|
import {
|
|
13
14
|
responseProcessingTemplateKind,
|
|
@@ -450,6 +451,11 @@ function validateExpressionReferences(
|
|
|
450
451
|
);
|
|
451
452
|
}
|
|
452
453
|
|
|
454
|
+
if (expression.type === "anyN") {
|
|
455
|
+
validateAnyNBound("min", expression.min, responses, variables, diagnostics, expression.source);
|
|
456
|
+
validateAnyNBound("max", expression.max, responses, variables, diagnostics, expression.source);
|
|
457
|
+
}
|
|
458
|
+
|
|
453
459
|
if (expression.type === "mathConstant" && !mathConstantNames.has(expression.name)) {
|
|
454
460
|
diagnostics.push({
|
|
455
461
|
code: "processing.mathConstant.name",
|
|
@@ -480,6 +486,35 @@ function validateExpressionReferences(
|
|
|
480
486
|
});
|
|
481
487
|
}
|
|
482
488
|
|
|
489
|
+
if (
|
|
490
|
+
(expression.type === "sum" ||
|
|
491
|
+
expression.type === "product" ||
|
|
492
|
+
expression.type === "min" ||
|
|
493
|
+
expression.type === "max") &&
|
|
494
|
+
expression.expressions.length === 0
|
|
495
|
+
) {
|
|
496
|
+
diagnostics.push({
|
|
497
|
+
code: "processing.numeric.arity",
|
|
498
|
+
severity: "error",
|
|
499
|
+
message: `qti-${expression.type} requires at least one child expression.`,
|
|
500
|
+
path: expression.source?.path,
|
|
501
|
+
source: expression.source,
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (
|
|
506
|
+
expression.type === "statsOperator" &&
|
|
507
|
+
(expression.expressions ?? [expression.expression]).length !== 1
|
|
508
|
+
) {
|
|
509
|
+
diagnostics.push({
|
|
510
|
+
code: "processing.statsOperator.arity",
|
|
511
|
+
severity: "error",
|
|
512
|
+
message: "qti-stats-operator requires exactly one child expression.",
|
|
513
|
+
path: expression.source?.path,
|
|
514
|
+
source: expression.source,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
|
|
483
518
|
if (expression.type === "repeat") {
|
|
484
519
|
validateRepeatExpression(expression, variables, diagnostics);
|
|
485
520
|
}
|
|
@@ -540,7 +575,31 @@ function validateRepeatExpression(
|
|
|
540
575
|
variables: Set<string>,
|
|
541
576
|
diagnostics: QtiDiagnostic[],
|
|
542
577
|
): void {
|
|
543
|
-
if (isInteger(expression.numberRepeats))
|
|
578
|
+
if (isInteger(expression.numberRepeats)) {
|
|
579
|
+
const repeats = BigInt(expression.numberRepeats);
|
|
580
|
+
if (repeats < 0n) {
|
|
581
|
+
diagnostics.push(repeatCountDiagnostic(expression));
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const minimumPerRepeat = minimumProducedElements(expression.expressions);
|
|
585
|
+
if (
|
|
586
|
+
repeats > BigInt(MAX_QTI_REPEAT_RESULT_ELEMENTS) ||
|
|
587
|
+
repeats * BigInt(minimumPerRepeat) > BigInt(MAX_QTI_REPEAT_RESULT_ELEMENTS)
|
|
588
|
+
) {
|
|
589
|
+
diagnostics.push({
|
|
590
|
+
code: "processing.repeat.limit",
|
|
591
|
+
severity: "error",
|
|
592
|
+
message: `qti-repeat cannot exceed ${MAX_QTI_REPEAT_RESULT_ELEMENTS} iterations or produced elements.`,
|
|
593
|
+
path: expression.source?.path,
|
|
594
|
+
source: expression.source,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (Number.isFinite(Number(expression.numberRepeats))) {
|
|
600
|
+
diagnostics.push(repeatCountDiagnostic(expression));
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
544
603
|
validateProcessingIdentifier(
|
|
545
604
|
expression.numberRepeats,
|
|
546
605
|
"processing.repeat.numberRepeats",
|
|
@@ -558,6 +617,91 @@ function validateRepeatExpression(
|
|
|
558
617
|
}
|
|
559
618
|
}
|
|
560
619
|
|
|
620
|
+
function validateAnyNBound(
|
|
621
|
+
attribute: "min" | "max",
|
|
622
|
+
value: string,
|
|
623
|
+
responses: Set<string>,
|
|
624
|
+
variables: Set<string>,
|
|
625
|
+
diagnostics: QtiDiagnostic[],
|
|
626
|
+
source: QtiDiagnostic["source"],
|
|
627
|
+
): void {
|
|
628
|
+
if (value.length === 0) {
|
|
629
|
+
diagnostics.push({
|
|
630
|
+
code: `processing.anyN.${attribute}.required`,
|
|
631
|
+
severity: "error",
|
|
632
|
+
message: `qti-any-n requires ${attribute}.`,
|
|
633
|
+
path: source?.path,
|
|
634
|
+
source,
|
|
635
|
+
});
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
if (isInteger(value)) return;
|
|
639
|
+
if (Number.isFinite(Number(value))) {
|
|
640
|
+
diagnostics.push({
|
|
641
|
+
code: `processing.anyN.${attribute}.integer`,
|
|
642
|
+
severity: "error",
|
|
643
|
+
message: `qti-any-n ${attribute} must be an integer or a declared variable.`,
|
|
644
|
+
path: source?.path,
|
|
645
|
+
source,
|
|
646
|
+
});
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (!variables.has(value) || responses.has(value)) {
|
|
650
|
+
diagnostics.push({
|
|
651
|
+
code: `processing.anyN.${attribute}.reference`,
|
|
652
|
+
severity: "error",
|
|
653
|
+
message: `qti-any-n ${attribute} references missing template or outcome variable ${value}.`,
|
|
654
|
+
path: source?.path,
|
|
655
|
+
source,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function repeatCountDiagnostic(
|
|
661
|
+
expression: Extract<QtiProcessingExpression, { type: "repeat" }>,
|
|
662
|
+
): QtiDiagnostic {
|
|
663
|
+
return {
|
|
664
|
+
code: "processing.repeat.numberRepeats",
|
|
665
|
+
severity: "error",
|
|
666
|
+
message: "qti-repeat requires a non-negative integer or a declared variable.",
|
|
667
|
+
path: expression.source?.path,
|
|
668
|
+
source: expression.source,
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function minimumProducedElements(expressions: QtiProcessingExpression[]): number {
|
|
673
|
+
let total = 0;
|
|
674
|
+
for (const expression of expressions) {
|
|
675
|
+
total = cappedElementCount(total + minimumExpressionElements(expression));
|
|
676
|
+
}
|
|
677
|
+
return total;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function minimumExpressionElements(expression: QtiProcessingExpression): number {
|
|
681
|
+
if (expression.type === "baseValue") return 1;
|
|
682
|
+
if (expression.type === "multiple" || expression.type === "ordered") {
|
|
683
|
+
return minimumProducedElements(expression.expressions);
|
|
684
|
+
}
|
|
685
|
+
if (expression.type === "random") {
|
|
686
|
+
if (expression.values.length === 0) return 0;
|
|
687
|
+
let minimum = MAX_QTI_REPEAT_RESULT_ELEMENTS + 1;
|
|
688
|
+
for (const value of expression.values) {
|
|
689
|
+
minimum = Math.min(minimum, minimumExpressionElements(value));
|
|
690
|
+
}
|
|
691
|
+
return minimum;
|
|
692
|
+
}
|
|
693
|
+
if (expression.type !== "repeat" || !/^\d+$/.test(expression.numberRepeats)) return 0;
|
|
694
|
+
const perRepeat = minimumProducedElements(expression.expressions);
|
|
695
|
+
const projected = BigInt(expression.numberRepeats) * BigInt(perRepeat);
|
|
696
|
+
return projected > BigInt(MAX_QTI_REPEAT_RESULT_ELEMENTS)
|
|
697
|
+
? MAX_QTI_REPEAT_RESULT_ELEMENTS + 1
|
|
698
|
+
: Number(projected);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function cappedElementCount(value: number): number {
|
|
702
|
+
return Math.min(value, MAX_QTI_REPEAT_RESULT_ELEMENTS + 1);
|
|
703
|
+
}
|
|
704
|
+
|
|
561
705
|
function validateInsideExpression(
|
|
562
706
|
expression: Extract<QtiProcessingExpression, { type: "inside" }>,
|
|
563
707
|
diagnostics: QtiDiagnostic[],
|
package/src/validation.ts
CHANGED
|
@@ -16,11 +16,10 @@ import { assertNever } from "./assert-never.js";
|
|
|
16
16
|
import { validateQtiDataSsmlMetadata } from "./tts.js";
|
|
17
17
|
import { validateCompanionMaterials } from "./validation-companion-materials.js";
|
|
18
18
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from "./validation-geometry.js";
|
|
19
|
+
validateAreaMapEntryAttributes,
|
|
20
|
+
validateLookupTableEntryAttributes,
|
|
21
|
+
validateMapEntryAttributes,
|
|
22
|
+
} from "./validation-declaration-entries.js";
|
|
24
23
|
import {
|
|
25
24
|
isBaseType,
|
|
26
25
|
isBooleanAttribute,
|
|
@@ -237,7 +236,8 @@ function isValidDeclarationBaseValue(value: string, baseType: QtiBaseType): bool
|
|
|
237
236
|
function validateMapping(declaration: QtiResponseDeclaration, diagnostics: QtiDiagnostic[]): void {
|
|
238
237
|
const mapping = declaration.mapping;
|
|
239
238
|
if (!mapping) return;
|
|
240
|
-
|
|
239
|
+
const defaultValue = mapping.attributes["default-value"];
|
|
240
|
+
if (defaultValue !== undefined && !isFiniteNumber(defaultValue)) {
|
|
241
241
|
diagnostics.push({
|
|
242
242
|
code: "mapping.defaultValue",
|
|
243
243
|
severity: "error",
|
|
@@ -249,42 +249,7 @@ function validateMapping(declaration: QtiResponseDeclaration, diagnostics: QtiDi
|
|
|
249
249
|
validateMappingBounds(declaration, mapping.attributes, mapping.source, "mapping", diagnostics);
|
|
250
250
|
|
|
251
251
|
for (const entry of mapping.entries) {
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function validateMapEntry(
|
|
257
|
-
declaration: QtiResponseDeclaration,
|
|
258
|
-
entry: NonNullable<QtiResponseDeclaration["mapping"]>["entries"][number],
|
|
259
|
-
diagnostics: QtiDiagnostic[],
|
|
260
|
-
): void {
|
|
261
|
-
if (!entry.attributes["map-key"]) {
|
|
262
|
-
diagnostics.push({
|
|
263
|
-
code: "mapEntry.mapKey.required",
|
|
264
|
-
severity: "error",
|
|
265
|
-
message: `Response declaration ${declaration.identifier} map entry requires map-key.`,
|
|
266
|
-
path: entry.source?.path,
|
|
267
|
-
source: entry.source,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const mappedValue = entry.attributes["mapped-value"];
|
|
272
|
-
if (mappedValue === undefined) {
|
|
273
|
-
diagnostics.push({
|
|
274
|
-
code: "mapEntry.mappedValue.required",
|
|
275
|
-
severity: "error",
|
|
276
|
-
message: `Response declaration ${declaration.identifier} map entry requires mapped-value.`,
|
|
277
|
-
path: entry.source?.path,
|
|
278
|
-
source: entry.source,
|
|
279
|
-
});
|
|
280
|
-
} else if (!isFiniteNumber(mappedValue)) {
|
|
281
|
-
diagnostics.push({
|
|
282
|
-
code: "mapEntry.mappedValue",
|
|
283
|
-
severity: "error",
|
|
284
|
-
message: `Response declaration ${declaration.identifier} map entry requires numeric mapped-value.`,
|
|
285
|
-
path: entry.source?.path,
|
|
286
|
-
source: entry.source,
|
|
287
|
-
});
|
|
252
|
+
validateMapEntryAttributes(declaration.identifier, entry, diagnostics);
|
|
288
253
|
}
|
|
289
254
|
}
|
|
290
255
|
|
|
@@ -294,7 +259,8 @@ function validateAreaMapping(
|
|
|
294
259
|
): void {
|
|
295
260
|
const areaMapping = declaration.areaMapping;
|
|
296
261
|
if (!areaMapping) return;
|
|
297
|
-
|
|
262
|
+
const defaultValue = areaMapping.attributes["default-value"];
|
|
263
|
+
if (defaultValue !== undefined && !isFiniteNumber(defaultValue)) {
|
|
298
264
|
diagnostics.push({
|
|
299
265
|
code: "areaMapping.defaultValue",
|
|
300
266
|
severity: "error",
|
|
@@ -312,7 +278,7 @@ function validateAreaMapping(
|
|
|
312
278
|
);
|
|
313
279
|
|
|
314
280
|
for (const entry of areaMapping.entries) {
|
|
315
|
-
|
|
281
|
+
validateAreaMapEntryAttributes(declaration.identifier, entry, diagnostics);
|
|
316
282
|
}
|
|
317
283
|
}
|
|
318
284
|
|
|
@@ -360,82 +326,6 @@ function validateMappingBounds(
|
|
|
360
326
|
}
|
|
361
327
|
}
|
|
362
328
|
|
|
363
|
-
function validateAreaMapEntry(
|
|
364
|
-
declaration: QtiResponseDeclaration,
|
|
365
|
-
entry: NonNullable<QtiResponseDeclaration["areaMapping"]>["entries"][number],
|
|
366
|
-
diagnostics: QtiDiagnostic[],
|
|
367
|
-
): void {
|
|
368
|
-
const shape = entry.attributes.shape;
|
|
369
|
-
const coords = entry.attributes.coords;
|
|
370
|
-
const mappedValue = entry.attributes["mapped-value"];
|
|
371
|
-
|
|
372
|
-
if (!shape) {
|
|
373
|
-
diagnostics.push({
|
|
374
|
-
code: "areaMapEntry.shape.required",
|
|
375
|
-
severity: "error",
|
|
376
|
-
message: `Response declaration ${declaration.identifier} area map entry requires shape.`,
|
|
377
|
-
path: entry.source?.path,
|
|
378
|
-
source: entry.source,
|
|
379
|
-
});
|
|
380
|
-
} else if (!isAreaShape(shape)) {
|
|
381
|
-
diagnostics.push({
|
|
382
|
-
code: "areaMapEntry.shape",
|
|
383
|
-
severity: "error",
|
|
384
|
-
message: `Response declaration ${declaration.identifier} area map entry has unsupported shape ${shape}.`,
|
|
385
|
-
path: entry.source?.path,
|
|
386
|
-
source: entry.source,
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
if (!coords) {
|
|
391
|
-
diagnostics.push({
|
|
392
|
-
code: "areaMapEntry.coords.required",
|
|
393
|
-
severity: "error",
|
|
394
|
-
message: `Response declaration ${declaration.identifier} area map entry requires coords.`,
|
|
395
|
-
path: entry.source?.path,
|
|
396
|
-
source: entry.source,
|
|
397
|
-
});
|
|
398
|
-
} else if (!isNumericCsv(coords)) {
|
|
399
|
-
diagnostics.push({
|
|
400
|
-
code: "areaMapEntry.coords",
|
|
401
|
-
severity: "error",
|
|
402
|
-
message: `Response declaration ${declaration.identifier} area map entry requires comma-separated numeric coords.`,
|
|
403
|
-
path: entry.source?.path,
|
|
404
|
-
source: entry.source,
|
|
405
|
-
});
|
|
406
|
-
} else if (
|
|
407
|
-
shape &&
|
|
408
|
-
isAreaShape(shape) &&
|
|
409
|
-
!hasValidShapeCoordinateCount(shape, numericCsv(coords))
|
|
410
|
-
) {
|
|
411
|
-
diagnostics.push({
|
|
412
|
-
code: "areaMapEntry.coords.shape",
|
|
413
|
-
severity: "error",
|
|
414
|
-
message: `Response declaration ${declaration.identifier} area map entry shape ${shape} has invalid coords arity.`,
|
|
415
|
-
path: entry.source?.path,
|
|
416
|
-
source: entry.source,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (mappedValue === undefined) {
|
|
421
|
-
diagnostics.push({
|
|
422
|
-
code: "areaMapEntry.mappedValue.required",
|
|
423
|
-
severity: "error",
|
|
424
|
-
message: `Response declaration ${declaration.identifier} area map entry requires mapped-value.`,
|
|
425
|
-
path: entry.source?.path,
|
|
426
|
-
source: entry.source,
|
|
427
|
-
});
|
|
428
|
-
} else if (!isFiniteNumber(mappedValue)) {
|
|
429
|
-
diagnostics.push({
|
|
430
|
-
code: "areaMapEntry.mappedValue",
|
|
431
|
-
severity: "error",
|
|
432
|
-
message: `Response declaration ${declaration.identifier} area map entry requires numeric mapped-value.`,
|
|
433
|
-
path: entry.source?.path,
|
|
434
|
-
source: entry.source,
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
329
|
function validateDeclarationRequiredAttributes(
|
|
440
330
|
declaration: QtiResponseDeclaration | QtiOutcomeDeclaration | QtiTemplateDeclaration,
|
|
441
331
|
diagnostics: QtiDiagnostic[],
|
|
@@ -502,37 +392,7 @@ function validateOutcomeLookupTables(item: QtiAssessmentItem, diagnostics: QtiDi
|
|
|
502
392
|
});
|
|
503
393
|
}
|
|
504
394
|
for (const entry of lookupTable.entries) {
|
|
505
|
-
|
|
506
|
-
diagnostics.push({
|
|
507
|
-
code: "lookupTable.entry.sourceValue",
|
|
508
|
-
severity: "error",
|
|
509
|
-
message: "Lookup table entry requires numeric source-value.",
|
|
510
|
-
path: entry.source?.path,
|
|
511
|
-
source: entry.source,
|
|
512
|
-
});
|
|
513
|
-
}
|
|
514
|
-
if (entry.attributes["target-value"] === undefined) {
|
|
515
|
-
diagnostics.push({
|
|
516
|
-
code: "lookupTable.entry.targetValue",
|
|
517
|
-
severity: "error",
|
|
518
|
-
message: "Lookup table entry requires target-value.",
|
|
519
|
-
path: entry.source?.path,
|
|
520
|
-
source: entry.source,
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
if (
|
|
524
|
-
lookupTable.type === "match" &&
|
|
525
|
-
entry.attributes["source-value"] !== undefined &&
|
|
526
|
-
!isInteger(entry.attributes["source-value"])
|
|
527
|
-
) {
|
|
528
|
-
diagnostics.push({
|
|
529
|
-
code: "lookupTable.match.sourceValue",
|
|
530
|
-
severity: "error",
|
|
531
|
-
message: "qti-match-table-entry source-value must be an integer.",
|
|
532
|
-
path: entry.source?.path,
|
|
533
|
-
source: entry.source,
|
|
534
|
-
});
|
|
535
|
-
}
|
|
395
|
+
validateLookupTableEntryAttributes(lookupTable.type, entry, diagnostics);
|
|
536
396
|
}
|
|
537
397
|
}
|
|
538
398
|
}
|
package/src/xml.ts
CHANGED
|
@@ -190,10 +190,9 @@ export function descendants(node: XmlNode, predicate: (node: XmlNode) => boolean
|
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
export function textContent(node: XmlNode): string {
|
|
193
|
-
|
|
194
|
-
typeof entry === "string" ? entry : textContent(entry)
|
|
195
|
-
|
|
196
|
-
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
193
|
+
return node.content
|
|
194
|
+
.map((entry) => (typeof entry === "string" ? entry : textContent(entry)))
|
|
195
|
+
.join("");
|
|
197
196
|
}
|
|
198
197
|
|
|
199
198
|
function openElement(
|