@longsightgroup/qti3-core 0.11.0 → 0.12.0
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/dist/binary-choice.d.ts +9 -0
- package/dist/binary-choice.d.ts.map +1 -0
- package/dist/binary-choice.js +81 -0
- package/dist/binary-choice.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/qti-identifier.d.ts +3 -0
- package/dist/qti-identifier.d.ts.map +1 -0
- package/dist/qti-identifier.js +5 -0
- package/dist/qti-identifier.js.map +1 -0
- package/dist/qti-package-paths.d.ts +2 -0
- package/dist/qti-package-paths.d.ts.map +1 -1
- package/dist/qti-package-paths.js +6 -0
- package/dist/qti-package-paths.js.map +1 -1
- package/dist/support.d.ts.map +1 -1
- package/dist/support.js +2 -0
- package/dist/support.js.map +1 -1
- package/dist/test-expression-parser.d.ts +6 -0
- package/dist/test-expression-parser.d.ts.map +1 -0
- package/dist/test-expression-parser.js +76 -0
- package/dist/test-expression-parser.js.map +1 -0
- package/dist/test-expression-serializer.d.ts +5 -0
- package/dist/test-expression-serializer.d.ts.map +1 -0
- package/dist/test-expression-serializer.js +54 -0
- package/dist/test-expression-serializer.js.map +1 -0
- package/dist/test-expression-validation.d.ts +14 -0
- package/dist/test-expression-validation.d.ts.map +1 -0
- package/dist/test-expression-validation.js +101 -0
- package/dist/test-expression-validation.js.map +1 -0
- package/dist/test-expression.d.ts +99 -0
- package/dist/test-expression.d.ts.map +1 -0
- package/dist/test-expression.js +28 -0
- package/dist/test-expression.js.map +1 -0
- package/dist/test-model.d.ts +62 -0
- package/dist/test-model.d.ts.map +1 -0
- package/dist/test-model.js +5 -0
- package/dist/test-model.js.map +1 -0
- package/dist/test-parser.d.ts +13 -0
- package/dist/test-parser.d.ts.map +1 -0
- package/dist/test-parser.js +144 -0
- package/dist/test-parser.js.map +1 -0
- package/dist/test-processing.d.ts +22 -0
- package/dist/test-processing.d.ts.map +1 -0
- package/dist/test-processing.js +84 -0
- package/dist/test-processing.js.map +1 -0
- package/dist/test-session.d.ts +31 -0
- package/dist/test-session.d.ts.map +1 -0
- package/dist/test-session.js +102 -0
- package/dist/test-session.js.map +1 -0
- package/dist/test-support.d.ts +4 -0
- package/dist/test-support.d.ts.map +1 -0
- package/dist/test-support.js +30 -0
- package/dist/test-support.js.map +1 -0
- package/dist/test-validation.d.ts +4 -0
- package/dist/test-validation.d.ts.map +1 -0
- package/dist/test-validation.js +133 -0
- package/dist/test-validation.js.map +1 -0
- package/dist/test-xml.d.ts +7 -0
- package/dist/test-xml.d.ts.map +1 -0
- package/dist/test-xml.js +23 -0
- package/dist/test-xml.js.map +1 -0
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/binary-choice.ts +96 -0
- package/src/index.ts +29 -0
- package/src/qti-identifier.ts +4 -0
- package/src/qti-package-paths.ts +9 -0
- package/src/support.ts +2 -0
- package/src/test-expression-parser.ts +89 -0
- package/src/test-expression-serializer.ts +71 -0
- package/src/test-expression-validation.ts +139 -0
- package/src/test-expression.ts +66 -0
- package/src/test-model.ts +68 -0
- package/src/test-parser.ts +201 -0
- package/src/test-processing.ts +107 -0
- package/src/test-session.ts +160 -0
- package/src/test-support.ts +31 -0
- package/src/test-validation.ts +190 -0
- package/src/test-xml.ts +37 -0
- package/src/types.ts +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-expression-validation.js","sourceRoot":"","sources":["../src/test-expression-validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { QtiSourceLocation } from "./types.js";
|
|
2
|
+
/** Scalar types supported by deterministic test outcome processing. */
|
|
3
|
+
export type QtiTestBaseType = "integer" | "float" | "boolean" | "string" | "identifier";
|
|
4
|
+
/** Test expressions form a closed language, independent of item processing. */
|
|
5
|
+
export type QtiTestExpression = ({
|
|
6
|
+
readonly type: "baseValue";
|
|
7
|
+
readonly baseType: QtiTestBaseType;
|
|
8
|
+
readonly value: number | boolean | string;
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: "variable";
|
|
11
|
+
readonly identifier: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly type: "testVariables";
|
|
14
|
+
readonly variableIdentifier: string;
|
|
15
|
+
readonly includeCategory?: string | undefined;
|
|
16
|
+
} | {
|
|
17
|
+
readonly type: "sum";
|
|
18
|
+
readonly expressions: readonly QtiTestExpression[];
|
|
19
|
+
} | {
|
|
20
|
+
readonly type: "and";
|
|
21
|
+
readonly expressions: readonly QtiTestExpression[];
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: "or";
|
|
24
|
+
readonly expressions: readonly QtiTestExpression[];
|
|
25
|
+
} | {
|
|
26
|
+
readonly type: "numericCompare";
|
|
27
|
+
readonly operator: "gt" | "gte" | "lt" | "lte";
|
|
28
|
+
readonly left: QtiTestExpression;
|
|
29
|
+
readonly right: QtiTestExpression;
|
|
30
|
+
} | {
|
|
31
|
+
readonly type: "not";
|
|
32
|
+
readonly expression: QtiTestExpression;
|
|
33
|
+
}) & {
|
|
34
|
+
readonly source?: QtiSourceLocation | undefined;
|
|
35
|
+
};
|
|
36
|
+
/** XML names, AST variants, arities and attributes owned by the test language. */
|
|
37
|
+
export declare const testExpressionSyntax: readonly [{
|
|
38
|
+
readonly name: "qti-base-value";
|
|
39
|
+
readonly type: "baseValue";
|
|
40
|
+
readonly arity: 0;
|
|
41
|
+
readonly attributes: readonly ["base-type"];
|
|
42
|
+
}, {
|
|
43
|
+
readonly name: "qti-variable";
|
|
44
|
+
readonly type: "variable";
|
|
45
|
+
readonly arity: 0;
|
|
46
|
+
readonly attributes: readonly ["identifier"];
|
|
47
|
+
}, {
|
|
48
|
+
readonly name: "qti-test-variables";
|
|
49
|
+
readonly type: "testVariables";
|
|
50
|
+
readonly arity: 0;
|
|
51
|
+
readonly attributes: readonly ["variable-identifier", "include-category"];
|
|
52
|
+
}, {
|
|
53
|
+
readonly name: "qti-sum";
|
|
54
|
+
readonly type: "sum";
|
|
55
|
+
readonly arity: "oneOrMore";
|
|
56
|
+
readonly attributes: readonly [];
|
|
57
|
+
}, {
|
|
58
|
+
readonly name: "qti-gt";
|
|
59
|
+
readonly type: "numericCompare";
|
|
60
|
+
readonly operator: "gt";
|
|
61
|
+
readonly arity: 2;
|
|
62
|
+
readonly attributes: readonly [];
|
|
63
|
+
}, {
|
|
64
|
+
readonly name: "qti-gte";
|
|
65
|
+
readonly type: "numericCompare";
|
|
66
|
+
readonly operator: "gte";
|
|
67
|
+
readonly arity: 2;
|
|
68
|
+
readonly attributes: readonly [];
|
|
69
|
+
}, {
|
|
70
|
+
readonly name: "qti-lt";
|
|
71
|
+
readonly type: "numericCompare";
|
|
72
|
+
readonly operator: "lt";
|
|
73
|
+
readonly arity: 2;
|
|
74
|
+
readonly attributes: readonly [];
|
|
75
|
+
}, {
|
|
76
|
+
readonly name: "qti-lte";
|
|
77
|
+
readonly type: "numericCompare";
|
|
78
|
+
readonly operator: "lte";
|
|
79
|
+
readonly arity: 2;
|
|
80
|
+
readonly attributes: readonly [];
|
|
81
|
+
}, {
|
|
82
|
+
readonly name: "qti-and";
|
|
83
|
+
readonly type: "and";
|
|
84
|
+
readonly arity: "oneOrMore";
|
|
85
|
+
readonly attributes: readonly [];
|
|
86
|
+
}, {
|
|
87
|
+
readonly name: "qti-or";
|
|
88
|
+
readonly type: "or";
|
|
89
|
+
readonly arity: "oneOrMore";
|
|
90
|
+
readonly attributes: readonly [];
|
|
91
|
+
}, {
|
|
92
|
+
readonly name: "qti-not";
|
|
93
|
+
readonly type: "not";
|
|
94
|
+
readonly arity: 1;
|
|
95
|
+
readonly attributes: readonly [];
|
|
96
|
+
}];
|
|
97
|
+
/** Refine a supported test scalar type without accepting item-only base types. */
|
|
98
|
+
export declare function isTestBaseType(value: string | undefined): value is QtiTestBaseType;
|
|
99
|
+
//# sourceMappingURL=test-expression.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-expression.d.ts","sourceRoot":"","sources":["../src/test-expression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,uEAAuE;AACvE,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,YAAY,CAAC;AAExF,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,CAC5B;IACE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CAC3C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1D;IACE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE,GAC3E;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAA;CAAE,CACnE,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAA;CAAE,CAAC;AAExD,kFAAkF;AAClF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuB9B,CAAC;AAEJ,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,IAAI,eAAe,CAQlF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** XML names, AST variants, arities and attributes owned by the test language. */
|
|
2
|
+
export const testExpressionSyntax = [
|
|
3
|
+
{ name: "qti-base-value", type: "baseValue", arity: 0, attributes: ["base-type"] },
|
|
4
|
+
{ name: "qti-variable", type: "variable", arity: 0, attributes: ["identifier"] },
|
|
5
|
+
{
|
|
6
|
+
name: "qti-test-variables",
|
|
7
|
+
type: "testVariables",
|
|
8
|
+
arity: 0,
|
|
9
|
+
attributes: ["variable-identifier", "include-category"],
|
|
10
|
+
},
|
|
11
|
+
{ name: "qti-sum", type: "sum", arity: "oneOrMore", attributes: [] },
|
|
12
|
+
{ name: "qti-gt", type: "numericCompare", operator: "gt", arity: 2, attributes: [] },
|
|
13
|
+
{ name: "qti-gte", type: "numericCompare", operator: "gte", arity: 2, attributes: [] },
|
|
14
|
+
{ name: "qti-lt", type: "numericCompare", operator: "lt", arity: 2, attributes: [] },
|
|
15
|
+
{ name: "qti-lte", type: "numericCompare", operator: "lte", arity: 2, attributes: [] },
|
|
16
|
+
{ name: "qti-and", type: "and", arity: "oneOrMore", attributes: [] },
|
|
17
|
+
{ name: "qti-or", type: "or", arity: "oneOrMore", attributes: [] },
|
|
18
|
+
{ name: "qti-not", type: "not", arity: 1, attributes: [] },
|
|
19
|
+
];
|
|
20
|
+
/** Refine a supported test scalar type without accepting item-only base types. */
|
|
21
|
+
export function isTestBaseType(value) {
|
|
22
|
+
return (value === "integer" ||
|
|
23
|
+
value === "float" ||
|
|
24
|
+
value === "boolean" ||
|
|
25
|
+
value === "string" ||
|
|
26
|
+
value === "identifier");
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=test-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-expression.js","sourceRoot":"","sources":["../src/test-expression.ts"],"names":[],"mappings":"AA8BA,kFAAkF;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,CAAC,EAAE;IAClF,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,YAAY,CAAC,EAAE;IAChF;QACE,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,CAAC;QACR,UAAU,EAAE,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACxD;IACD,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE;IACpE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IACpF,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IACtF,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IACpF,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IACtF,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE;IACpE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE,EAAE;IAClE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;CAOzD,CAAC;AAEJ,kFAAkF;AAClF,MAAM,UAAU,cAAc,CAAC,KAAyB;IACtD,OAAO,CACL,KAAK,KAAK,SAAS;QACnB,KAAK,KAAK,OAAO;QACjB,KAAK,KAAK,SAAS;QACnB,KAAK,KAAK,QAAQ;QAClB,KAAK,KAAK,YAAY,CACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { QtiDiagnostic, QtiOutcomeDeclaration } from "./types.js";
|
|
2
|
+
import type { QtiTestBaseType, QtiTestExpression } from "./test-expression.js";
|
|
3
|
+
/** An ordered branch evaluated after the section's final item submission. */
|
|
4
|
+
export interface QtiTestBranch {
|
|
5
|
+
readonly target: string;
|
|
6
|
+
readonly expression: QtiTestExpression;
|
|
7
|
+
}
|
|
8
|
+
/** A fixed item reference; categories participate in test-variable aggregation. */
|
|
9
|
+
export interface QtiTestItemRef {
|
|
10
|
+
readonly identifier: string;
|
|
11
|
+
readonly href: string;
|
|
12
|
+
readonly categories: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/** A flat section in the supported linear, individually submitted test profile. */
|
|
15
|
+
export interface QtiTestSection {
|
|
16
|
+
readonly identifier: string;
|
|
17
|
+
readonly title: string;
|
|
18
|
+
readonly items: readonly QtiTestItemRef[];
|
|
19
|
+
readonly branches: readonly QtiTestBranch[];
|
|
20
|
+
}
|
|
21
|
+
/** Framework-neutral QTI test definition. Validation establishes executable support. */
|
|
22
|
+
export interface QtiTestDefinition {
|
|
23
|
+
readonly identifier: string;
|
|
24
|
+
readonly title: string;
|
|
25
|
+
readonly partIdentifier: string;
|
|
26
|
+
readonly sections: readonly QtiTestSection[];
|
|
27
|
+
readonly outcomeDeclarations: readonly QtiOutcomeDeclaration[];
|
|
28
|
+
readonly outcomeProcessing: readonly QtiTestOutcomeRule[];
|
|
29
|
+
}
|
|
30
|
+
/** Sequential assignment of a test expression to a declared test outcome. */
|
|
31
|
+
export interface QtiTestOutcomeRule {
|
|
32
|
+
readonly type: "setOutcomeValue";
|
|
33
|
+
readonly identifier: string;
|
|
34
|
+
readonly expression: QtiTestExpression;
|
|
35
|
+
}
|
|
36
|
+
type ExecutableSection = QtiTestSection & {
|
|
37
|
+
readonly items: readonly [QtiTestItemRef, ...QtiTestItemRef[]];
|
|
38
|
+
};
|
|
39
|
+
type ExecutableOutcome = Readonly<QtiOutcomeDeclaration> & {
|
|
40
|
+
readonly cardinality: "single";
|
|
41
|
+
readonly baseType: QtiTestBaseType;
|
|
42
|
+
readonly defaultValue: number | boolean | string | null;
|
|
43
|
+
};
|
|
44
|
+
declare const executableTest: unique symbol;
|
|
45
|
+
/** A test checked for supported expressions, unique identifiers and forward routing. */
|
|
46
|
+
export type QtiExecutableTest = Omit<QtiTestDefinition, "sections" | "outcomeDeclarations"> & {
|
|
47
|
+
readonly [executableTest]: true;
|
|
48
|
+
readonly sections: readonly [ExecutableSection, ...ExecutableSection[]];
|
|
49
|
+
readonly outcomeDeclarations: readonly ExecutableOutcome[];
|
|
50
|
+
};
|
|
51
|
+
/** Typed failure channel shared by test construction, parsing and transitions. */
|
|
52
|
+
export type QtiTestResult<T> = {
|
|
53
|
+
readonly ok: true;
|
|
54
|
+
readonly value: T;
|
|
55
|
+
} | {
|
|
56
|
+
readonly ok: false;
|
|
57
|
+
readonly diagnostics: readonly QtiDiagnostic[];
|
|
58
|
+
};
|
|
59
|
+
/** Construct a safe test diagnostic without including submitted content. */
|
|
60
|
+
export declare function testFailure(code: string, message: string): QtiTestResult<never>;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=test-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-model.d.ts","sourceRoot":"","sources":["../src/test-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACvE,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE/E,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;CACxC;AAED,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;CAC7C;AAED,wFAAwF;AACxF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,mBAAmB,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC/D,QAAQ,CAAC,iBAAiB,EAAE,SAAS,kBAAkB,EAAE,CAAC;CAC3D;AAED,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;CACxC;AAED,KAAK,iBAAiB,GAAG,cAAc,GAAG;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,cAAc,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC;CAChE,CAAC;AACF,KAAK,iBAAiB,GAAG,QAAQ,CAAC,qBAAqB,CAAC,GAAG;IACzD,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;CACzD,CAAC;AAEF,OAAO,CAAC,MAAM,cAAc,EAAE,OAAO,MAAM,CAAC;AAE5C,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,GAAG,qBAAqB,CAAC,GAAG;IAC5F,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,IAAI,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,iBAAiB,EAAE,GAAG,iBAAiB,EAAE,CAAC,CAAC;IACxE,QAAQ,CAAC,mBAAmB,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC5D,CAAC;AAEF,kFAAkF;AAClF,MAAM,MAAM,aAAa,CAAC,CAAC,IACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACxC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,CAAC;AAE3E,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,CAE/E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-model.js","sourceRoot":"","sources":["../src/test-model.ts"],"names":[],"mappings":"AAgEA,4EAA4E;AAC5E,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,OAAe;IACvD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAC5F,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type QtiExecutableTest, type QtiTestResult } from "./test-model.js";
|
|
2
|
+
/** Whether a test requires runtime selection rather than ordinary fixed navigation. */
|
|
3
|
+
export type QtiTestExecution = {
|
|
4
|
+
readonly kind: "fixed";
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: "sequenced";
|
|
7
|
+
readonly test: QtiExecutableTest;
|
|
8
|
+
};
|
|
9
|
+
/** Classify sequencing on the parsed tree and reject unsupported executable routes. */
|
|
10
|
+
export declare function parseQtiTestExecution(xml: string): QtiTestResult<QtiTestExecution>;
|
|
11
|
+
/** Parse the supported executable test profile; never silently ignore routing features. */
|
|
12
|
+
export declare function parseQtiTest(xml: string): QtiTestResult<QtiExecutableTest>;
|
|
13
|
+
//# sourceMappingURL=test-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-parser.d.ts","sourceRoot":"","sources":["../src/test-parser.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,iBAAiB,EAGtB,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AAGzB,uFAAuF;AACvF,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAErE,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAMlF;AAED,2FAA2F;AAC3F,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAG1E"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { parseOutcomeDeclaration } from "./parser-declarations.js";
|
|
2
|
+
import { QTI_ASI_NAMESPACE } from "./qti-namespaces.js";
|
|
3
|
+
import { parseXmlTree } from "./xml.js";
|
|
4
|
+
import { validateQtiTest } from "./test-validation.js";
|
|
5
|
+
import { parseTestExpressionChild } from "./test-expression-parser.js";
|
|
6
|
+
import { testExpressionSyntax } from "./test-expression.js";
|
|
7
|
+
import { checkTestXml, rejectTestXml } from "./test-xml.js";
|
|
8
|
+
import { testFailure, } from "./test-model.js";
|
|
9
|
+
/** Classify sequencing on the parsed tree and reject unsupported executable routes. */
|
|
10
|
+
export function parseQtiTestExecution(xml) {
|
|
11
|
+
const parsed = parseTestRoot(xml);
|
|
12
|
+
if (!parsed.ok)
|
|
13
|
+
return parsed;
|
|
14
|
+
if (!requiresSequence(parsed.value))
|
|
15
|
+
return { ok: true, value: { kind: "fixed" } };
|
|
16
|
+
const result = parseTestDefinition(parsed.value);
|
|
17
|
+
return result.ok ? { ok: true, value: { kind: "sequenced", test: result.value } } : result;
|
|
18
|
+
}
|
|
19
|
+
/** Parse the supported executable test profile; never silently ignore routing features. */
|
|
20
|
+
export function parseQtiTest(xml) {
|
|
21
|
+
const parsed = parseTestRoot(xml);
|
|
22
|
+
return parsed.ok ? parseTestDefinition(parsed.value) : parsed;
|
|
23
|
+
}
|
|
24
|
+
function parseTestRoot(xml) {
|
|
25
|
+
const { root, errors } = parseXmlTree(xml);
|
|
26
|
+
if (errors.length ||
|
|
27
|
+
!root ||
|
|
28
|
+
root.localName !== "qti-assessment-test" ||
|
|
29
|
+
root.uri !== QTI_ASI_NAMESPACE)
|
|
30
|
+
return testFailure("xml", "Expected a well-formed QTI 3 assessment test.");
|
|
31
|
+
return { ok: true, value: root };
|
|
32
|
+
}
|
|
33
|
+
function requiresSequence(node) {
|
|
34
|
+
return ((node.uri === QTI_ASI_NAMESPACE &&
|
|
35
|
+
[
|
|
36
|
+
"qti-branch-rule",
|
|
37
|
+
"qti-pre-condition",
|
|
38
|
+
"qti-selection",
|
|
39
|
+
"qti-ordering",
|
|
40
|
+
"qti-adaptive-selection",
|
|
41
|
+
].includes(node.localName)) ||
|
|
42
|
+
node.children.some(requiresSequence));
|
|
43
|
+
}
|
|
44
|
+
function parseTestDefinition(root) {
|
|
45
|
+
const diagnostics = [];
|
|
46
|
+
checkTestXml(root, ["qti-outcome-declaration", "qti-test-part", "qti-outcome-processing"], ["identifier", "title", "tool-name", "tool-version"], diagnostics);
|
|
47
|
+
const part = parseTestPart(root, diagnostics);
|
|
48
|
+
if (!part.ok)
|
|
49
|
+
return part;
|
|
50
|
+
const declarations = root.children
|
|
51
|
+
.filter((node) => node.localName === "qti-outcome-declaration")
|
|
52
|
+
.map((node) => parseTestDeclaration(node, diagnostics));
|
|
53
|
+
const sections = part.value.children
|
|
54
|
+
.filter((node) => node.localName === "qti-assessment-section")
|
|
55
|
+
.map((node) => parseTestSection(node, diagnostics));
|
|
56
|
+
const rules = parseTestOutcomeProcessing(root, diagnostics);
|
|
57
|
+
if (diagnostics.length)
|
|
58
|
+
return { ok: false, diagnostics };
|
|
59
|
+
return validateQtiTest({
|
|
60
|
+
identifier: root.attributes.identifier ?? "",
|
|
61
|
+
title: root.attributes.title ?? "",
|
|
62
|
+
partIdentifier: part.value.attributes.identifier ?? "",
|
|
63
|
+
sections,
|
|
64
|
+
outcomeDeclarations: declarations,
|
|
65
|
+
outcomeProcessing: rules,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function parseTestPart(root, diagnostics) {
|
|
69
|
+
const parts = root.children.filter((node) => node.localName === "qti-test-part");
|
|
70
|
+
const part = parts[0];
|
|
71
|
+
if (parts.length !== 1 ||
|
|
72
|
+
!part ||
|
|
73
|
+
part.attributes["navigation-mode"] !== "linear" ||
|
|
74
|
+
part.attributes["submission-mode"] !== "individual")
|
|
75
|
+
return testFailure("mode", "Executable tests require one linear part with individual submission.");
|
|
76
|
+
checkTestXml(part, ["qti-assessment-section"], ["identifier", "navigation-mode", "submission-mode"], diagnostics);
|
|
77
|
+
return { ok: true, value: part };
|
|
78
|
+
}
|
|
79
|
+
function parseTestDeclaration(node, diagnostics) {
|
|
80
|
+
if (node.attributes.cardinality !== "single" || node.children.length > 1)
|
|
81
|
+
rejectTestXml(node, diagnostics);
|
|
82
|
+
checkTestXml(node, ["qti-default-value"], ["identifier", "cardinality", "base-type"], diagnostics);
|
|
83
|
+
for (const value of node.children) {
|
|
84
|
+
checkTestXml(value, ["qti-value"], [], diagnostics);
|
|
85
|
+
if (value.children.length !== 1)
|
|
86
|
+
rejectTestXml(value, diagnostics);
|
|
87
|
+
for (const scalar of value.children)
|
|
88
|
+
checkTestXml(scalar, [], [], diagnostics, true);
|
|
89
|
+
}
|
|
90
|
+
return parseOutcomeDeclaration(node, diagnostics);
|
|
91
|
+
}
|
|
92
|
+
function parseTestSection(section, diagnostics) {
|
|
93
|
+
checkTestXml(section, ["qti-assessment-item-ref", "qti-branch-rule"], ["identifier", "title", "visible", "keep-together"], diagnostics);
|
|
94
|
+
if (!["true", "1"].includes(section.attributes.visible ?? "") ||
|
|
95
|
+
(section.attributes["keep-together"] !== undefined &&
|
|
96
|
+
!["true", "1"].includes(section.attributes["keep-together"])))
|
|
97
|
+
rejectTestXml(section, diagnostics);
|
|
98
|
+
const branches = [];
|
|
99
|
+
for (const branch of section.children.filter((node) => node.localName === "qti-branch-rule")) {
|
|
100
|
+
checkTestXml(branch, testExpressionSyntax.map((syntax) => syntax.name), ["target"], diagnostics);
|
|
101
|
+
const expression = parseTestExpressionChild(branch, diagnostics);
|
|
102
|
+
if (expression)
|
|
103
|
+
branches.push({ target: branch.attributes.target ?? "", expression });
|
|
104
|
+
}
|
|
105
|
+
const items = section.children
|
|
106
|
+
.filter((node) => node.localName === "qti-assessment-item-ref")
|
|
107
|
+
.map((item) => {
|
|
108
|
+
checkTestXml(item, [], ["identifier", "href", "category"], diagnostics);
|
|
109
|
+
return {
|
|
110
|
+
identifier: item.attributes.identifier ?? "",
|
|
111
|
+
href: item.attributes.href ?? "",
|
|
112
|
+
categories: (item.attributes.category ?? "")
|
|
113
|
+
.split(/\s+/)
|
|
114
|
+
.filter((category) => category.length > 0),
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
identifier: section.attributes.identifier ?? "",
|
|
119
|
+
title: section.attributes.title ?? "",
|
|
120
|
+
branches,
|
|
121
|
+
items,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function parseTestOutcomeProcessing(root, diagnostics) {
|
|
125
|
+
const processing = root.children.filter((node) => node.localName === "qti-outcome-processing");
|
|
126
|
+
if (processing.length > 1)
|
|
127
|
+
rejectTestXml(root, diagnostics);
|
|
128
|
+
const rules = [];
|
|
129
|
+
for (const node of processing) {
|
|
130
|
+
checkTestXml(node, ["qti-set-outcome-value"], [], diagnostics);
|
|
131
|
+
for (const rule of node.children) {
|
|
132
|
+
checkTestXml(rule, testExpressionSyntax.map((syntax) => syntax.name), ["identifier"], diagnostics);
|
|
133
|
+
const expression = parseTestExpressionChild(rule, diagnostics);
|
|
134
|
+
if (expression)
|
|
135
|
+
rules.push({
|
|
136
|
+
type: "setOutcomeValue",
|
|
137
|
+
identifier: rule.attributes.identifier ?? "",
|
|
138
|
+
expression,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return rules;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=test-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-parser.js","sourceRoot":"","sources":["../src/test-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QtiTestExpression } from "./test-expression.js";
|
|
2
|
+
import type { QtiExecutableTest } from "./test-model.js";
|
|
3
|
+
/** Trusted server-scored submission. Item scores are finite scalar numbers. */
|
|
4
|
+
export interface QtiTestSubmission {
|
|
5
|
+
readonly itemRef: string;
|
|
6
|
+
readonly score: number;
|
|
7
|
+
}
|
|
8
|
+
type TestValue = number | boolean | string | number[] | null;
|
|
9
|
+
/** Data used by the deterministic test interpreter; no item session state or hooks. */
|
|
10
|
+
export interface TestExpressionValues {
|
|
11
|
+
readonly outcomes: Readonly<Record<string, TestValue>>;
|
|
12
|
+
readonly submissions: readonly QtiTestSubmission[];
|
|
13
|
+
readonly categories: ReadonlyMap<string, readonly string[]>;
|
|
14
|
+
}
|
|
15
|
+
/** Evaluate a validated test expression using QTI null and three-valued boolean semantics. */
|
|
16
|
+
export declare function evaluateTestExpression(expression: QtiTestExpression, values: TestExpressionValues): TestValue;
|
|
17
|
+
/** Recompute outcomes from defaults and committed scores, in assignment order. */
|
|
18
|
+
export declare function processTestOutcomes(test: QtiExecutableTest, submissions: readonly QtiTestSubmission[]): {
|
|
19
|
+
readonly outcomes: Record<string, TestValue>;
|
|
20
|
+
};
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=test-processing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-processing.d.ts","sourceRoot":"","sources":["../src/test-processing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,KAAK,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;AAE7D,uFAAuF;AACvF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;CAC7D;AAED,8FAA8F;AAC9F,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,iBAAiB,EAC7B,MAAM,EAAE,oBAAoB,GAC3B,SAAS,CA+DX;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,SAAS,iBAAiB,EAAE,GACxC;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;CAAE,CAclD"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { assertNever } from "./assert-never.js";
|
|
2
|
+
/** Evaluate a validated test expression using QTI null and three-valued boolean semantics. */
|
|
3
|
+
export function evaluateTestExpression(expression, values) {
|
|
4
|
+
const evaluate = (child) => evaluateTestExpression(child, values);
|
|
5
|
+
switch (expression.type) {
|
|
6
|
+
case "baseValue":
|
|
7
|
+
return expression.value;
|
|
8
|
+
case "variable":
|
|
9
|
+
return values.outcomes[expression.identifier] ?? null;
|
|
10
|
+
case "testVariables": {
|
|
11
|
+
const scores = values.submissions
|
|
12
|
+
.filter((submission) => expression.includeCategory === undefined ||
|
|
13
|
+
values.categories.get(submission.itemRef)?.includes(expression.includeCategory))
|
|
14
|
+
.map((submission) => submission.score);
|
|
15
|
+
return scores.length ? scores : null;
|
|
16
|
+
}
|
|
17
|
+
case "sum": {
|
|
18
|
+
let sum = 0;
|
|
19
|
+
for (const child of expression.expressions) {
|
|
20
|
+
const value = evaluate(child);
|
|
21
|
+
if (value === null)
|
|
22
|
+
return null;
|
|
23
|
+
if (typeof value === "number")
|
|
24
|
+
sum += value;
|
|
25
|
+
else if (typeof value === "object")
|
|
26
|
+
for (const number of value)
|
|
27
|
+
sum += number;
|
|
28
|
+
else
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return sum;
|
|
32
|
+
}
|
|
33
|
+
case "numericCompare": {
|
|
34
|
+
const left = evaluate(expression.left);
|
|
35
|
+
const right = evaluate(expression.right);
|
|
36
|
+
if (typeof left !== "number" || typeof right !== "number")
|
|
37
|
+
return null;
|
|
38
|
+
switch (expression.operator) {
|
|
39
|
+
case "gt":
|
|
40
|
+
return left > right;
|
|
41
|
+
case "gte":
|
|
42
|
+
return left >= right;
|
|
43
|
+
case "lt":
|
|
44
|
+
return left < right;
|
|
45
|
+
case "lte":
|
|
46
|
+
return left <= right;
|
|
47
|
+
default:
|
|
48
|
+
return assertNever(expression.operator);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
case "and":
|
|
52
|
+
case "or": {
|
|
53
|
+
const decisive = expression.type === "or";
|
|
54
|
+
let sawNull = false;
|
|
55
|
+
for (const child of expression.expressions) {
|
|
56
|
+
const value = evaluate(child);
|
|
57
|
+
if (value === decisive)
|
|
58
|
+
return decisive;
|
|
59
|
+
if (value === null)
|
|
60
|
+
sawNull = true;
|
|
61
|
+
}
|
|
62
|
+
return sawNull ? null : !decisive;
|
|
63
|
+
}
|
|
64
|
+
case "not": {
|
|
65
|
+
const value = evaluate(expression.expression);
|
|
66
|
+
return value === null ? null : !value;
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
return assertNever(expression);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Recompute outcomes from defaults and committed scores, in assignment order. */
|
|
73
|
+
export function processTestOutcomes(test, submissions) {
|
|
74
|
+
const outcomes = Object.fromEntries(test.outcomeDeclarations.map((d) => [d.identifier, d.defaultValue]));
|
|
75
|
+
const categories = new Map(test.sections.flatMap((s) => s.items.map((i) => [i.identifier, i.categories])));
|
|
76
|
+
for (const rule of test.outcomeProcessing)
|
|
77
|
+
outcomes[rule.identifier] = evaluateTestExpression(rule.expression, {
|
|
78
|
+
outcomes,
|
|
79
|
+
submissions,
|
|
80
|
+
categories,
|
|
81
|
+
});
|
|
82
|
+
return { outcomes };
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=test-processing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-processing.js","sourceRoot":"","sources":["../src/test-processing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type QtiTestSubmission } from "./test-processing.js";
|
|
2
|
+
import { type QtiExecutableTest, type QtiTestResult } from "./test-model.js";
|
|
3
|
+
import type { QtiValue } from "./types.js";
|
|
4
|
+
interface TestSessionBase {
|
|
5
|
+
readonly testIdentifier: string;
|
|
6
|
+
readonly submissions: readonly QtiTestSubmission[];
|
|
7
|
+
readonly outcomes: Readonly<Record<string, QtiValue>>;
|
|
8
|
+
}
|
|
9
|
+
/** Live route state. Only an active session has a current item. */
|
|
10
|
+
export type QtiTestSession = TestSessionBase & ({
|
|
11
|
+
readonly status: "active";
|
|
12
|
+
readonly currentItemRef: string;
|
|
13
|
+
} | {
|
|
14
|
+
readonly status: "completed";
|
|
15
|
+
});
|
|
16
|
+
/** Stable JSON contract: computed frontier/outcomes are reconstructed, never trusted. */
|
|
17
|
+
export interface QtiTestSessionSnapshot {
|
|
18
|
+
readonly version: 1;
|
|
19
|
+
readonly testIdentifier: string;
|
|
20
|
+
readonly submissions: readonly QtiTestSubmission[];
|
|
21
|
+
}
|
|
22
|
+
/** Start a validated test at its first item. */
|
|
23
|
+
export declare function startQtiTest(test: QtiExecutableTest): QtiTestSession;
|
|
24
|
+
/** Accept one trusted scored answer at the frontier; does not mutate input state. */
|
|
25
|
+
export declare function submitQtiTestAnswer(test: QtiExecutableTest, session: QtiTestSession, submission: QtiTestSubmission): QtiTestResult<QtiTestSession>;
|
|
26
|
+
/** Project session state into its minimal persisted contract. */
|
|
27
|
+
export declare function snapshotQtiTestSession(session: QtiTestSession): QtiTestSessionSnapshot;
|
|
28
|
+
/** Parse persisted state and replay it against the pinned test to reject impossible routes. */
|
|
29
|
+
export declare function restoreQtiTestSession(test: QtiExecutableTest, input: unknown): QtiTestResult<QtiTestSession>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=test-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-session.d.ts","sourceRoot":"","sources":["../src/test-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,KAAK,iBAAiB,EAEtB,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,UAAU,eAAe;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;CACvD;AAED,mEAAmE;AACnE,MAAM,MAAM,cAAc,GAAG,eAAe,GAC1C,CACI;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CACnC,CAAC;AAEJ,yFAAyF;AACzF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACpD;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CASpE;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,iBAAiB,GAC5B,aAAa,CAAC,cAAc,CAAC,CAuC/B;AAqBD,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,GAAG,sBAAsB,CAMtF;AAED,+FAA+F;AAC/F,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,OAAO,GACb,aAAa,CAAC,cAAc,CAAC,CAoC/B"}
|