@conform-ed/qti-react 0.0.15 → 0.0.16

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.
@@ -118,6 +118,19 @@ export function executeTemplateProcessing(
118
118
  lookupVariable: (identifier) => templateValues.get(identifier) ?? null,
119
119
  responseDeclaration: (identifier) => responseDeclarationsById.get(identifier),
120
120
  responseValue: () => null, // no candidate responses exist at template-processing time
121
+ variableDefault: (identifier) => {
122
+ const declaration = declarationsById.get(identifier) ?? responseDeclarationsById.get(identifier);
123
+
124
+ if (!declaration?.defaultValue) {
125
+ return null; // "NULL if no default value was declared" (§2.11.1.3)
126
+ }
127
+
128
+ return rpValue(
129
+ declaration.cardinality,
130
+ declaration.defaultValue.values.map((entry) => coerceScalar(entry.value, declaration.baseType)),
131
+ declaration.baseType,
132
+ );
133
+ },
121
134
  random: mulberry32(context.seed),
122
135
  customOperators: context.customOperators,
123
136
  };
@@ -229,6 +242,34 @@ export function executeTemplateProcessing(
229
242
  };
230
243
  }
231
244
 
245
+ /**
246
+ * The effective template declarations for a clone: test-level `templateDefault`
247
+ * values (§5.152) replace the declared defaults. A NULL value clears the default.
248
+ */
249
+ export function applyTemplateDefaultOverrides(
250
+ declarations: readonly TemplateDeclarationView[],
251
+ overrides: Readonly<Record<string, OutcomeValue>>,
252
+ ): readonly TemplateDeclarationView[] {
253
+ return declarations.map((declaration) => {
254
+ if (!(declaration.identifier in overrides)) {
255
+ return declaration;
256
+ }
257
+
258
+ const value = overrides[declaration.identifier];
259
+
260
+ if (value === null || value === undefined) {
261
+ const { defaultValue: _cleared, ...rest } = declaration;
262
+
263
+ return rest;
264
+ }
265
+
266
+ return {
267
+ ...declaration,
268
+ defaultValue: { values: (Array.isArray(value) ? value : [value]).map((member) => ({ value: member })) },
269
+ };
270
+ });
271
+ }
272
+
232
273
  /** The effective response declarations for a clone: setCorrectResponse overrides applied. */
233
274
  export function applyCorrectResponseOverrides(
234
275
  declarations: readonly ResponseDeclarationView[],
package/src/rp/types.ts CHANGED
@@ -32,11 +32,53 @@ export interface RpValue {
32
32
 
33
33
  export type MaybeRpValue = RpValue | null;
34
34
 
35
+ /** One matchTable row: exact integer source → target (§7.23). */
36
+ export interface MatchTableEntryView {
37
+ readonly sourceValue: number;
38
+ readonly targetValue: RpScalar;
39
+ }
40
+
41
+ /**
42
+ * "A matchTable transforms a source integer by finding the first
43
+ * qti-match-table-entry with an exact match to the source." (§5.90)
44
+ */
45
+ export interface MatchTableView {
46
+ /** "The default outcome value to be used when no matching table entry is found.
47
+ * If omitted, the NULL value is used." (§5.90.1) */
48
+ readonly defaultValue?: RpScalar;
49
+ readonly matchTableEntries: readonly MatchTableEntryView[];
50
+ }
51
+
52
+ /** One interpolationTable row; sourceValue is "the lower bound … to match this entry" (§7.18.1). */
53
+ export interface InterpolationTableEntryView {
54
+ readonly sourceValue: number;
55
+ readonly targetValue: RpScalar;
56
+ /** "If 'true', the default, then an exact match of the value is considered a match
57
+ * of this entry." (§7.18.2) */
58
+ readonly includeBoundary?: boolean;
59
+ }
60
+
61
+ /**
62
+ * "An interpolationTable transforms a source float (or integer) by finding the first
63
+ * interpolationTableEntry with a sourceValue that is less than or equal to (subject
64
+ * to includeBoundary) the source value." (§5.78)
65
+ */
66
+ export interface InterpolationTableView {
67
+ readonly defaultValue?: RpScalar;
68
+ readonly interpolationTableEntries: readonly InterpolationTableEntryView[];
69
+ }
70
+
35
71
  export interface OutcomeDeclarationView {
36
72
  readonly identifier: string;
37
73
  readonly cardinality: Cardinality;
38
74
  readonly baseType?: string;
39
75
  readonly defaultValue?: { readonly values: ReadonlyArray<{ readonly value: RpScalar }> };
76
+ /** The declaration's lookupTable (at most one of the two), read by `lookupOutcomeValue` (§5.87). */
77
+ readonly matchTable?: MatchTableView;
78
+ readonly interpolationTable?: InterpolationTableView;
79
+ /** Declared score bounds, aggregated by `outcomeMaximum`/`outcomeMinimum` (§2.11.2.6-7). */
80
+ readonly normalMaximum?: number;
81
+ readonly normalMinimum?: number;
40
82
  }
41
83
 
42
84
  /**
@@ -49,11 +91,14 @@ export interface RpExpressionView {
49
91
  readonly baseType?: string;
50
92
  readonly value?: RpScalar;
51
93
  readonly expressions?: readonly RpExpressionView[];
52
- /** Bounds/step for the random operators (template processing). */
53
- readonly min?: number;
54
- readonly max?: number;
55
- readonly step?: number;
56
- /** `equal` tolerance window; string entries are template references (unsupported). */
94
+ /**
95
+ * Bounds/step for the random operators and `anyN`; string values are variable
96
+ * references resolved at runtime (§2.11.3.6), bare or brace-enclosed (§7.13).
97
+ */
98
+ readonly min?: number | string;
99
+ readonly max?: number | string;
100
+ readonly step?: number | string;
101
+ /** `equal` tolerance window; string entries are variable references. */
57
102
  readonly toleranceMode?: "exact" | "absolute" | "relative";
58
103
  readonly tolerance?: ReadonlyArray<number | string>;
59
104
  readonly includeLowerBound?: boolean;
@@ -67,14 +112,18 @@ export interface RpExpressionView {
67
112
  readonly figures?: number | string;
68
113
  /** Pass count for `repeat`. */
69
114
  readonly numberRepeats?: number | string;
115
+ /** XSD-dialect pattern for `patternMatch`; "{ref}" resolves from a variable (§7.13). */
116
+ readonly pattern?: string;
70
117
  /** String comparison controls for `stringMatch` and `substring`. */
71
118
  readonly caseSensitive?: boolean;
72
119
  readonly substring?: boolean;
73
120
  /** Area for `inside` (QTI shape + coords string). */
74
121
  readonly shape?: string;
75
122
  readonly coords?: string;
76
- /** Test-level subset selection (`testVariables` and the `number*` aggregates). */
123
+ /** Test-level subset selection (`testVariables`, `outcomeMinimum`/`outcomeMaximum`, `number*`). */
77
124
  readonly variableIdentifier?: string;
125
+ /** Outcome variable whose declared bounds `outcomeMinimum`/`outcomeMaximum` look up (§7.28.4). */
126
+ readonly outcomeIdentifier?: string;
78
127
  readonly weightIdentifier?: string;
79
128
  readonly sectionIdentifier?: string;
80
129
  readonly includeCategory?: string | readonly string[];
@@ -103,11 +152,16 @@ export interface RpConditionBranch {
103
152
  readonly rules: readonly RpRuleView[];
104
153
  }
105
154
 
106
- /** One response rule: responseCondition, setOutcomeValue, or exitResponse. */
155
+ /**
156
+ * One response rule: responseCondition, setOutcomeValue, lookupOutcomeValue,
157
+ * responseProcessingFragment, or exitResponse.
158
+ */
107
159
  export interface RpRuleView {
108
160
  readonly kind: string;
109
161
  readonly identifier?: string;
110
162
  readonly expression?: RpExpressionView;
163
+ /** Nested rules of a `responseProcessingFragment` (§5.118). */
164
+ readonly rules?: readonly RpRuleView[];
111
165
  readonly responseIf?: RpConditionBranch;
112
166
  readonly responseElseIfs?: readonly RpConditionBranch[];
113
167
  readonly responseElse?: { readonly rules: readonly RpRuleView[] };
@@ -156,6 +210,20 @@ export interface ResponseProcessingContext {
156
210
  * submission sequence then replays the exact same outcomes (ADR-0004 determinism).
157
211
  */
158
212
  readonly random?: (() => number) | undefined;
213
+ /**
214
+ * Built-in session variables (reserved identifiers; items must not declare them).
215
+ * `duration` is the item session's elapsed seconds; `numAttempts` "increases by 1
216
+ * at the start of each attempt", so it includes the attempt being scored.
217
+ */
218
+ readonly duration?: number | undefined;
219
+ readonly numAttempts?: number | undefined;
220
+ /**
221
+ * The session's current `completionStatus` — the third built-in, "declared
222
+ * implicitly"; it enters the outcome map (defaulting to "not_attempted") so
223
+ * `setOutcomeValue` can change it (§2.2.2.3). An explicit declaration (legacy
224
+ * content) wins over this value.
225
+ */
226
+ readonly completionStatus?: string | undefined;
159
227
  /** Registered vendor `customOperator` implementations by class (opt-in). */
160
228
  readonly customOperators?: Readonly<Record<string, CustomOperatorImplementation>> | undefined;
161
229
  }