@conform-ed/qti-react 0.0.19 → 0.0.21
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/headless.d.ts +4 -2
- package/dist/headless.js +189 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +189 -0
- package/dist/rp/schema.d.ts +116 -0
- package/dist/test/schema.d.ts +192 -0
- package/package.json +3 -3
- package/src/headless.ts +49 -1
- package/src/index.ts +39 -0
- package/src/rp/schema.ts +143 -0
- package/src/test/schema.ts +272 -0
package/dist/headless.d.ts
CHANGED
|
@@ -16,10 +16,12 @@ export { referenceInteractionKinds, reportItemCapability, type ItemCapabilityOpt
|
|
|
16
16
|
export { foldString, mapResponse, matchCorrect, mapResponsePoint, scoreResponse } from "./response-processing";
|
|
17
17
|
export { effectiveItemScore, type EffectiveItemScore } from "./item-score";
|
|
18
18
|
export { applyCorrectResponseOverrides, collectRpIssues, collectTemplateIssues, executeResponseProcessing, executeTemplateProcessing, mulberry32, resolveTemplate, } from "./rp";
|
|
19
|
-
export type { CustomOperatorImplementation, OutcomeDeclarationView, OutcomeValue, ResponseNormalization, ResponseProcessingContext, ResponseProcessingResult, ResponseProcessingView, TemplateDeclarationView, } from "./rp";
|
|
19
|
+
export type { CustomOperatorImplementation, InterpolationTableView, MatchTableView, MaybeRpValue, OutcomeDeclarationView, OutcomeValue, ResponseNormalization, ResponseProcessingContext, ResponseProcessingResult, ResponseProcessingView, RpConditionBranch, RpExpressionView, RpRuleView, RpScalar, RpValue, TemplateDeclarationView, } from "./rp";
|
|
20
20
|
export { createAttemptStore, type AttemptSnapshot, type AttemptStore, type AttemptStoreOptions } from "./store";
|
|
21
21
|
export { createTestController, type TestController, type TestSessionState } from "./test";
|
|
22
22
|
export type { CapabilityIssue, CapabilityIssueType, CapabilityReport } from "./capability";
|
|
23
23
|
export type { AssessmentItemView, AssessmentStimulusRefView, BodyNode, InteractionNode, StimulusContentView, XmlContentNode, } from "./runtime";
|
|
24
|
-
export type { AssessmentTestView } from "./test";
|
|
24
|
+
export type { AssessmentItemRefView, AssessmentSectionView, AssessmentTestView, TestPartView } from "./test";
|
|
25
25
|
export type { Cardinality, ResponseDeclarationView, ResponseValue, ScoreResult } from "./types";
|
|
26
|
+
export { cardinalitySchema, interpolationTableSchema, matchTableSchema, outcomeDeclarationSchema, rpExpressionSchema, rpScalarSchema, type OutcomeDeclarationSchema, type RpExpressionSchema, type RpScalarSchema, } from "./rp/schema";
|
|
27
|
+
export { assessmentItemRefViewSchema, assessmentTestViewSchema, branchRuleSchema, itemSessionControlSchema, makeAssessmentTestSchema, orderingSchema, outcomeConditionBranchSchema, outcomeProcessingSchema, outcomeRuleSchema, selectionSchema, testFeedbackSchema, timeLimitsSchema, type AssessmentItemRefViewSchema, type AssessmentSectionNode, type AssessmentTestNode, type AssessmentTestSchemas, type AssessmentTestViewSchema, type BranchRuleSchema, type ItemSessionControlSchema, type OrderingSchema, type SelectionSchema, type TestPartNode, type TimeLimitsSchema, } from "./test/schema";
|
package/dist/headless.js
CHANGED
|
@@ -4780,16 +4780,201 @@ function createTestSessionStore(controller, options) {
|
|
|
4780
4780
|
})
|
|
4781
4781
|
};
|
|
4782
4782
|
}
|
|
4783
|
+
// src/rp/schema.ts
|
|
4784
|
+
import { z as z2 } from "zod";
|
|
4785
|
+
var rpScalarSchema = z2.union([z2.string(), z2.number(), z2.boolean()]);
|
|
4786
|
+
var numberOrRef = z2.union([z2.number(), z2.string()]);
|
|
4787
|
+
var categoryFilter = z2.union([z2.string(), z2.array(z2.string())]);
|
|
4788
|
+
var cardinalitySchema = z2.enum(["single", "multiple", "ordered", "record"]);
|
|
4789
|
+
var rpExpressionSchema = z2.lazy(() => z2.object({
|
|
4790
|
+
kind: z2.string(),
|
|
4791
|
+
identifier: z2.string().optional(),
|
|
4792
|
+
baseType: z2.string().optional(),
|
|
4793
|
+
value: rpScalarSchema.optional(),
|
|
4794
|
+
expressions: z2.array(rpExpressionSchema).optional(),
|
|
4795
|
+
min: numberOrRef.optional(),
|
|
4796
|
+
max: numberOrRef.optional(),
|
|
4797
|
+
step: numberOrRef.optional(),
|
|
4798
|
+
toleranceMode: z2.enum(["exact", "absolute", "relative"]).optional(),
|
|
4799
|
+
tolerance: z2.array(numberOrRef).optional(),
|
|
4800
|
+
includeLowerBound: z2.boolean().optional(),
|
|
4801
|
+
includeUpperBound: z2.boolean().optional(),
|
|
4802
|
+
n: numberOrRef.optional(),
|
|
4803
|
+
name: z2.string().optional(),
|
|
4804
|
+
roundingMode: z2.enum(["decimalPlaces", "significantFigures"]).optional(),
|
|
4805
|
+
figures: numberOrRef.optional(),
|
|
4806
|
+
numberRepeats: numberOrRef.optional(),
|
|
4807
|
+
pattern: z2.string().optional(),
|
|
4808
|
+
caseSensitive: z2.boolean().optional(),
|
|
4809
|
+
substring: z2.boolean().optional(),
|
|
4810
|
+
shape: z2.string().optional(),
|
|
4811
|
+
coords: z2.string().optional(),
|
|
4812
|
+
variableIdentifier: z2.string().optional(),
|
|
4813
|
+
outcomeIdentifier: z2.string().optional(),
|
|
4814
|
+
weightIdentifier: z2.string().optional(),
|
|
4815
|
+
sectionIdentifier: z2.string().optional(),
|
|
4816
|
+
includeCategory: categoryFilter.optional(),
|
|
4817
|
+
excludeCategory: categoryFilter.optional(),
|
|
4818
|
+
class: z2.string().optional(),
|
|
4819
|
+
definition: z2.string().optional(),
|
|
4820
|
+
fieldIdentifier: z2.string().optional()
|
|
4821
|
+
}));
|
|
4822
|
+
var matchTableSchema = z2.object({
|
|
4823
|
+
defaultValue: rpScalarSchema.optional(),
|
|
4824
|
+
matchTableEntries: z2.array(z2.object({ sourceValue: z2.number(), targetValue: rpScalarSchema }))
|
|
4825
|
+
});
|
|
4826
|
+
var interpolationTableSchema = z2.object({
|
|
4827
|
+
defaultValue: rpScalarSchema.optional(),
|
|
4828
|
+
interpolationTableEntries: z2.array(z2.object({
|
|
4829
|
+
sourceValue: z2.number(),
|
|
4830
|
+
targetValue: rpScalarSchema,
|
|
4831
|
+
includeBoundary: z2.boolean().optional()
|
|
4832
|
+
}))
|
|
4833
|
+
});
|
|
4834
|
+
var outcomeDeclarationSchema = z2.object({
|
|
4835
|
+
identifier: z2.string(),
|
|
4836
|
+
cardinality: cardinalitySchema,
|
|
4837
|
+
baseType: z2.string().optional(),
|
|
4838
|
+
defaultValue: z2.object({ values: z2.array(z2.object({ value: rpScalarSchema })) }).optional(),
|
|
4839
|
+
matchTable: matchTableSchema.optional(),
|
|
4840
|
+
interpolationTable: interpolationTableSchema.optional(),
|
|
4841
|
+
normalMaximum: z2.number().optional(),
|
|
4842
|
+
normalMinimum: z2.number().optional()
|
|
4843
|
+
});
|
|
4844
|
+
// src/test/schema.ts
|
|
4845
|
+
import { z as z3 } from "zod";
|
|
4846
|
+
var timeLimitsSchema = z3.object({
|
|
4847
|
+
minTime: z3.number().nonnegative().optional(),
|
|
4848
|
+
maxTime: z3.number().nonnegative().optional(),
|
|
4849
|
+
allowLateSubmission: z3.boolean().optional()
|
|
4850
|
+
});
|
|
4851
|
+
var itemSessionControlSchema = z3.object({
|
|
4852
|
+
maxAttempts: z3.number().int().nonnegative().optional(),
|
|
4853
|
+
showFeedback: z3.boolean().optional(),
|
|
4854
|
+
allowReview: z3.boolean().optional(),
|
|
4855
|
+
showSolution: z3.boolean().optional(),
|
|
4856
|
+
allowComment: z3.boolean().optional(),
|
|
4857
|
+
allowSkipping: z3.boolean().optional(),
|
|
4858
|
+
validateResponses: z3.boolean().optional()
|
|
4859
|
+
});
|
|
4860
|
+
var selectionSchema = z3.object({
|
|
4861
|
+
select: z3.number().int().nonnegative(),
|
|
4862
|
+
withReplacement: z3.boolean().optional()
|
|
4863
|
+
});
|
|
4864
|
+
var orderingSchema = z3.object({ shuffle: z3.boolean().optional() });
|
|
4865
|
+
var branchRuleSchema = z3.object({
|
|
4866
|
+
target: z3.string().trim().min(1),
|
|
4867
|
+
expression: rpExpressionSchema
|
|
4868
|
+
});
|
|
4869
|
+
var weightSchema = z3.object({ identifier: z3.string().trim().min(1), value: z3.number() });
|
|
4870
|
+
var templateDefaultSchema = z3.object({
|
|
4871
|
+
templateIdentifier: z3.string().trim().min(1),
|
|
4872
|
+
expression: rpExpressionSchema
|
|
4873
|
+
});
|
|
4874
|
+
var outcomeConditionBranchSchema = z3.lazy(() => z3.object({
|
|
4875
|
+
expression: rpExpressionSchema,
|
|
4876
|
+
rules: z3.array(outcomeRuleSchema)
|
|
4877
|
+
}));
|
|
4878
|
+
var outcomeRuleSchema = z3.lazy(() => z3.object({
|
|
4879
|
+
kind: z3.string(),
|
|
4880
|
+
identifier: z3.string().optional(),
|
|
4881
|
+
expression: rpExpressionSchema.optional(),
|
|
4882
|
+
rules: z3.array(outcomeRuleSchema).optional(),
|
|
4883
|
+
outcomeIf: outcomeConditionBranchSchema.optional(),
|
|
4884
|
+
outcomeElseIfs: z3.array(outcomeConditionBranchSchema).optional(),
|
|
4885
|
+
outcomeElse: z3.object({ rules: z3.array(outcomeRuleSchema) }).optional()
|
|
4886
|
+
}));
|
|
4887
|
+
var outcomeProcessingSchema = z3.object({ rules: z3.array(outcomeRuleSchema) });
|
|
4888
|
+
var testFeedbackSchema = z3.object({
|
|
4889
|
+
access: z3.enum(["atEnd", "during"]).optional(),
|
|
4890
|
+
outcomeIdentifier: z3.string().trim().min(1),
|
|
4891
|
+
identifier: z3.string().trim().min(1),
|
|
4892
|
+
showHide: z3.enum(["show", "hide"]).optional(),
|
|
4893
|
+
content: z3.array(z3.unknown()).optional()
|
|
4894
|
+
});
|
|
4895
|
+
var assessmentItemRefViewSchema = z3.object({
|
|
4896
|
+
kind: z3.literal("assessmentItemRef"),
|
|
4897
|
+
identifier: z3.string().trim().min(1),
|
|
4898
|
+
href: z3.string().optional(),
|
|
4899
|
+
categories: z3.array(z3.string()).optional(),
|
|
4900
|
+
fixed: z3.boolean().optional(),
|
|
4901
|
+
required: z3.boolean().optional(),
|
|
4902
|
+
preConditions: z3.array(rpExpressionSchema).optional(),
|
|
4903
|
+
branchRules: z3.array(branchRuleSchema).optional(),
|
|
4904
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
4905
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
4906
|
+
weights: z3.array(weightSchema).optional(),
|
|
4907
|
+
templateDefaults: z3.array(templateDefaultSchema).optional()
|
|
4908
|
+
});
|
|
4909
|
+
var assessmentSectionFlags = {
|
|
4910
|
+
title: z3.string().trim().min(1).optional(),
|
|
4911
|
+
visible: z3.boolean().optional(),
|
|
4912
|
+
fixed: z3.boolean().optional(),
|
|
4913
|
+
required: z3.boolean().optional(),
|
|
4914
|
+
keepTogether: z3.boolean().optional(),
|
|
4915
|
+
selection: selectionSchema.optional(),
|
|
4916
|
+
ordering: orderingSchema.optional(),
|
|
4917
|
+
preConditions: z3.array(rpExpressionSchema).optional(),
|
|
4918
|
+
branchRules: z3.array(branchRuleSchema).optional(),
|
|
4919
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
4920
|
+
timeLimits: timeLimitsSchema.optional()
|
|
4921
|
+
};
|
|
4922
|
+
var testPartLevel = {
|
|
4923
|
+
preConditions: z3.array(rpExpressionSchema).optional(),
|
|
4924
|
+
branchRules: z3.array(branchRuleSchema).optional(),
|
|
4925
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
4926
|
+
timeLimits: timeLimitsSchema.optional()
|
|
4927
|
+
};
|
|
4928
|
+
function makeAssessmentTestSchema(itemRefSchema) {
|
|
4929
|
+
const assessmentSectionSchema = z3.lazy(() => z3.object({
|
|
4930
|
+
kind: z3.literal("assessmentSection"),
|
|
4931
|
+
identifier: z3.string().trim().min(1),
|
|
4932
|
+
...assessmentSectionFlags,
|
|
4933
|
+
children: z3.array(z3.union([assessmentSectionSchema, itemRefSchema]))
|
|
4934
|
+
}));
|
|
4935
|
+
const testPartSchema = z3.object({
|
|
4936
|
+
identifier: z3.string().trim().min(1),
|
|
4937
|
+
navigationMode: z3.enum(["linear", "nonlinear"]),
|
|
4938
|
+
submissionMode: z3.enum(["individual", "simultaneous"]),
|
|
4939
|
+
...testPartLevel,
|
|
4940
|
+
assessmentSections: z3.array(assessmentSectionSchema)
|
|
4941
|
+
});
|
|
4942
|
+
const assessmentTestSchema = z3.object({
|
|
4943
|
+
identifier: z3.string().trim().min(1),
|
|
4944
|
+
title: z3.string().trim().min(1).optional(),
|
|
4945
|
+
outcomeDeclarations: z3.array(outcomeDeclarationSchema).optional(),
|
|
4946
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
4947
|
+
testParts: z3.array(testPartSchema),
|
|
4948
|
+
outcomeProcessing: outcomeProcessingSchema.optional(),
|
|
4949
|
+
testFeedbacks: z3.array(testFeedbackSchema).optional()
|
|
4950
|
+
});
|
|
4951
|
+
return { assessmentSectionSchema, testPartSchema, assessmentTestSchema };
|
|
4952
|
+
}
|
|
4953
|
+
var assessmentTestViewSchema = makeAssessmentTestSchema(assessmentItemRefViewSchema).assessmentTestSchema;
|
|
4783
4954
|
export {
|
|
4955
|
+
timeLimitsSchema,
|
|
4956
|
+
testFeedbackSchema,
|
|
4784
4957
|
stimulusContentFromNormalized,
|
|
4958
|
+
selectionSchema,
|
|
4785
4959
|
scoreResponse,
|
|
4960
|
+
rpScalarSchema,
|
|
4961
|
+
rpExpressionSchema,
|
|
4786
4962
|
resolveTemplate,
|
|
4787
4963
|
reportItemCapability,
|
|
4788
4964
|
referenceInteractionKinds,
|
|
4965
|
+
outcomeRuleSchema,
|
|
4966
|
+
outcomeProcessingSchema,
|
|
4967
|
+
outcomeDeclarationSchema,
|
|
4968
|
+
outcomeConditionBranchSchema,
|
|
4969
|
+
orderingSchema,
|
|
4789
4970
|
mulberry32,
|
|
4971
|
+
matchTableSchema,
|
|
4790
4972
|
matchCorrect,
|
|
4791
4973
|
mapResponsePoint,
|
|
4792
4974
|
mapResponse,
|
|
4975
|
+
makeAssessmentTestSchema,
|
|
4976
|
+
itemSessionControlSchema,
|
|
4977
|
+
interpolationTableSchema,
|
|
4793
4978
|
foldString,
|
|
4794
4979
|
executeTemplateProcessing,
|
|
4795
4980
|
executeResponseProcessing,
|
|
@@ -4798,7 +4983,11 @@ export {
|
|
|
4798
4983
|
createAttemptStore,
|
|
4799
4984
|
collectTemplateIssues,
|
|
4800
4985
|
collectRpIssues,
|
|
4986
|
+
cardinalitySchema,
|
|
4987
|
+
branchRuleSchema,
|
|
4988
|
+
assessmentTestViewSchema,
|
|
4801
4989
|
assessmentTestViewFromNormalized,
|
|
4802
4990
|
assessmentItemViewFromNormalized,
|
|
4991
|
+
assessmentItemRefViewSchema,
|
|
4803
4992
|
applyCorrectResponseOverrides
|
|
4804
4993
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -16,3 +16,5 @@ export { associateInteraction, choiceInteraction, drawingInteraction, endAttempt
|
|
|
16
16
|
export { AssociateReferenceSkin, ChoiceReferenceSkin, DrawingReferenceSkin, EndAttemptReferenceSkin, ExtendedTextReferenceSkin, GapMatchReferenceSkin, GraphicAssociateReferenceSkin, GraphicGapMatchReferenceSkin, GraphicOrderReferenceSkin, GraphicStage, HotspotReferenceSkin, HottextReferenceSkin, InlineChoiceReferenceSkin, MatchReferenceSkin, MediaReferenceSkin, OrderReferenceSkin, PositionObjectReferenceSkin, SelectPointReferenceSkin, SliderReferenceSkin, TextEntryReferenceSkin, UploadReferenceSkin, referenceSkin, textOf, } from "./reference-skin";
|
|
17
17
|
export type { AreaMapEntryView, AreaMappingView, Cardinality, CorrectResponseView, MapEntryView, MappingView, ResponseDeclarationView, ResponseValue, ScoreResult, } from "./types";
|
|
18
18
|
export { resolveCatalogSupports, resolvePnpActivation, type CatalogCardEntryView, type CatalogCardView, type CatalogContentView, type CatalogFileHrefView, type CatalogResolution, type CatalogView, type PnpActivation, type PnpAdditionalTestingTimeView, type PnpFeatureSetView, type PnpLanguageModeView, type PnpReplaceAccessModeView, type PnpView, type ResolvedCatalogSupport, } from "./pnp";
|
|
19
|
+
export { cardinalitySchema, interpolationTableSchema, matchTableSchema, outcomeDeclarationSchema, rpExpressionSchema, rpScalarSchema, type OutcomeDeclarationSchema, type RpExpressionSchema, type RpScalarSchema, } from "./rp/schema";
|
|
20
|
+
export { assessmentItemRefViewSchema, assessmentTestViewSchema, branchRuleSchema, itemSessionControlSchema, makeAssessmentTestSchema, orderingSchema, outcomeConditionBranchSchema, outcomeProcessingSchema, outcomeRuleSchema, selectionSchema, testFeedbackSchema, timeLimitsSchema, type AssessmentItemRefViewSchema, type AssessmentSectionNode, type AssessmentTestNode, type AssessmentTestSchemas, type AssessmentTestViewSchema, type BranchRuleSchema, type ItemSessionControlSchema, type OrderingSchema, type SelectionSchema, type TestPartNode, type TimeLimitsSchema, } from "./test/schema";
|
package/dist/index.js
CHANGED
|
@@ -6800,6 +6800,177 @@ var referenceSkin = {
|
|
|
6800
6800
|
textEntryInteraction: TextEntryReferenceSkin,
|
|
6801
6801
|
uploadInteraction: UploadReferenceSkin
|
|
6802
6802
|
};
|
|
6803
|
+
// src/rp/schema.ts
|
|
6804
|
+
import { z as z18 } from "zod";
|
|
6805
|
+
var rpScalarSchema = z18.union([z18.string(), z18.number(), z18.boolean()]);
|
|
6806
|
+
var numberOrRef = z18.union([z18.number(), z18.string()]);
|
|
6807
|
+
var categoryFilter = z18.union([z18.string(), z18.array(z18.string())]);
|
|
6808
|
+
var cardinalitySchema = z18.enum(["single", "multiple", "ordered", "record"]);
|
|
6809
|
+
var rpExpressionSchema = z18.lazy(() => z18.object({
|
|
6810
|
+
kind: z18.string(),
|
|
6811
|
+
identifier: z18.string().optional(),
|
|
6812
|
+
baseType: z18.string().optional(),
|
|
6813
|
+
value: rpScalarSchema.optional(),
|
|
6814
|
+
expressions: z18.array(rpExpressionSchema).optional(),
|
|
6815
|
+
min: numberOrRef.optional(),
|
|
6816
|
+
max: numberOrRef.optional(),
|
|
6817
|
+
step: numberOrRef.optional(),
|
|
6818
|
+
toleranceMode: z18.enum(["exact", "absolute", "relative"]).optional(),
|
|
6819
|
+
tolerance: z18.array(numberOrRef).optional(),
|
|
6820
|
+
includeLowerBound: z18.boolean().optional(),
|
|
6821
|
+
includeUpperBound: z18.boolean().optional(),
|
|
6822
|
+
n: numberOrRef.optional(),
|
|
6823
|
+
name: z18.string().optional(),
|
|
6824
|
+
roundingMode: z18.enum(["decimalPlaces", "significantFigures"]).optional(),
|
|
6825
|
+
figures: numberOrRef.optional(),
|
|
6826
|
+
numberRepeats: numberOrRef.optional(),
|
|
6827
|
+
pattern: z18.string().optional(),
|
|
6828
|
+
caseSensitive: z18.boolean().optional(),
|
|
6829
|
+
substring: z18.boolean().optional(),
|
|
6830
|
+
shape: z18.string().optional(),
|
|
6831
|
+
coords: z18.string().optional(),
|
|
6832
|
+
variableIdentifier: z18.string().optional(),
|
|
6833
|
+
outcomeIdentifier: z18.string().optional(),
|
|
6834
|
+
weightIdentifier: z18.string().optional(),
|
|
6835
|
+
sectionIdentifier: z18.string().optional(),
|
|
6836
|
+
includeCategory: categoryFilter.optional(),
|
|
6837
|
+
excludeCategory: categoryFilter.optional(),
|
|
6838
|
+
class: z18.string().optional(),
|
|
6839
|
+
definition: z18.string().optional(),
|
|
6840
|
+
fieldIdentifier: z18.string().optional()
|
|
6841
|
+
}));
|
|
6842
|
+
var matchTableSchema = z18.object({
|
|
6843
|
+
defaultValue: rpScalarSchema.optional(),
|
|
6844
|
+
matchTableEntries: z18.array(z18.object({ sourceValue: z18.number(), targetValue: rpScalarSchema }))
|
|
6845
|
+
});
|
|
6846
|
+
var interpolationTableSchema = z18.object({
|
|
6847
|
+
defaultValue: rpScalarSchema.optional(),
|
|
6848
|
+
interpolationTableEntries: z18.array(z18.object({
|
|
6849
|
+
sourceValue: z18.number(),
|
|
6850
|
+
targetValue: rpScalarSchema,
|
|
6851
|
+
includeBoundary: z18.boolean().optional()
|
|
6852
|
+
}))
|
|
6853
|
+
});
|
|
6854
|
+
var outcomeDeclarationSchema = z18.object({
|
|
6855
|
+
identifier: z18.string(),
|
|
6856
|
+
cardinality: cardinalitySchema,
|
|
6857
|
+
baseType: z18.string().optional(),
|
|
6858
|
+
defaultValue: z18.object({ values: z18.array(z18.object({ value: rpScalarSchema })) }).optional(),
|
|
6859
|
+
matchTable: matchTableSchema.optional(),
|
|
6860
|
+
interpolationTable: interpolationTableSchema.optional(),
|
|
6861
|
+
normalMaximum: z18.number().optional(),
|
|
6862
|
+
normalMinimum: z18.number().optional()
|
|
6863
|
+
});
|
|
6864
|
+
// src/test/schema.ts
|
|
6865
|
+
import { z as z19 } from "zod";
|
|
6866
|
+
var timeLimitsSchema = z19.object({
|
|
6867
|
+
minTime: z19.number().nonnegative().optional(),
|
|
6868
|
+
maxTime: z19.number().nonnegative().optional(),
|
|
6869
|
+
allowLateSubmission: z19.boolean().optional()
|
|
6870
|
+
});
|
|
6871
|
+
var itemSessionControlSchema = z19.object({
|
|
6872
|
+
maxAttempts: z19.number().int().nonnegative().optional(),
|
|
6873
|
+
showFeedback: z19.boolean().optional(),
|
|
6874
|
+
allowReview: z19.boolean().optional(),
|
|
6875
|
+
showSolution: z19.boolean().optional(),
|
|
6876
|
+
allowComment: z19.boolean().optional(),
|
|
6877
|
+
allowSkipping: z19.boolean().optional(),
|
|
6878
|
+
validateResponses: z19.boolean().optional()
|
|
6879
|
+
});
|
|
6880
|
+
var selectionSchema = z19.object({
|
|
6881
|
+
select: z19.number().int().nonnegative(),
|
|
6882
|
+
withReplacement: z19.boolean().optional()
|
|
6883
|
+
});
|
|
6884
|
+
var orderingSchema = z19.object({ shuffle: z19.boolean().optional() });
|
|
6885
|
+
var branchRuleSchema = z19.object({
|
|
6886
|
+
target: z19.string().trim().min(1),
|
|
6887
|
+
expression: rpExpressionSchema
|
|
6888
|
+
});
|
|
6889
|
+
var weightSchema = z19.object({ identifier: z19.string().trim().min(1), value: z19.number() });
|
|
6890
|
+
var templateDefaultSchema = z19.object({
|
|
6891
|
+
templateIdentifier: z19.string().trim().min(1),
|
|
6892
|
+
expression: rpExpressionSchema
|
|
6893
|
+
});
|
|
6894
|
+
var outcomeConditionBranchSchema = z19.lazy(() => z19.object({
|
|
6895
|
+
expression: rpExpressionSchema,
|
|
6896
|
+
rules: z19.array(outcomeRuleSchema)
|
|
6897
|
+
}));
|
|
6898
|
+
var outcomeRuleSchema = z19.lazy(() => z19.object({
|
|
6899
|
+
kind: z19.string(),
|
|
6900
|
+
identifier: z19.string().optional(),
|
|
6901
|
+
expression: rpExpressionSchema.optional(),
|
|
6902
|
+
rules: z19.array(outcomeRuleSchema).optional(),
|
|
6903
|
+
outcomeIf: outcomeConditionBranchSchema.optional(),
|
|
6904
|
+
outcomeElseIfs: z19.array(outcomeConditionBranchSchema).optional(),
|
|
6905
|
+
outcomeElse: z19.object({ rules: z19.array(outcomeRuleSchema) }).optional()
|
|
6906
|
+
}));
|
|
6907
|
+
var outcomeProcessingSchema = z19.object({ rules: z19.array(outcomeRuleSchema) });
|
|
6908
|
+
var testFeedbackSchema = z19.object({
|
|
6909
|
+
access: z19.enum(["atEnd", "during"]).optional(),
|
|
6910
|
+
outcomeIdentifier: z19.string().trim().min(1),
|
|
6911
|
+
identifier: z19.string().trim().min(1),
|
|
6912
|
+
showHide: z19.enum(["show", "hide"]).optional(),
|
|
6913
|
+
content: z19.array(z19.unknown()).optional()
|
|
6914
|
+
});
|
|
6915
|
+
var assessmentItemRefViewSchema = z19.object({
|
|
6916
|
+
kind: z19.literal("assessmentItemRef"),
|
|
6917
|
+
identifier: z19.string().trim().min(1),
|
|
6918
|
+
href: z19.string().optional(),
|
|
6919
|
+
categories: z19.array(z19.string()).optional(),
|
|
6920
|
+
fixed: z19.boolean().optional(),
|
|
6921
|
+
required: z19.boolean().optional(),
|
|
6922
|
+
preConditions: z19.array(rpExpressionSchema).optional(),
|
|
6923
|
+
branchRules: z19.array(branchRuleSchema).optional(),
|
|
6924
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
6925
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
6926
|
+
weights: z19.array(weightSchema).optional(),
|
|
6927
|
+
templateDefaults: z19.array(templateDefaultSchema).optional()
|
|
6928
|
+
});
|
|
6929
|
+
var assessmentSectionFlags = {
|
|
6930
|
+
title: z19.string().trim().min(1).optional(),
|
|
6931
|
+
visible: z19.boolean().optional(),
|
|
6932
|
+
fixed: z19.boolean().optional(),
|
|
6933
|
+
required: z19.boolean().optional(),
|
|
6934
|
+
keepTogether: z19.boolean().optional(),
|
|
6935
|
+
selection: selectionSchema.optional(),
|
|
6936
|
+
ordering: orderingSchema.optional(),
|
|
6937
|
+
preConditions: z19.array(rpExpressionSchema).optional(),
|
|
6938
|
+
branchRules: z19.array(branchRuleSchema).optional(),
|
|
6939
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
6940
|
+
timeLimits: timeLimitsSchema.optional()
|
|
6941
|
+
};
|
|
6942
|
+
var testPartLevel = {
|
|
6943
|
+
preConditions: z19.array(rpExpressionSchema).optional(),
|
|
6944
|
+
branchRules: z19.array(branchRuleSchema).optional(),
|
|
6945
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
6946
|
+
timeLimits: timeLimitsSchema.optional()
|
|
6947
|
+
};
|
|
6948
|
+
function makeAssessmentTestSchema(itemRefSchema) {
|
|
6949
|
+
const assessmentSectionSchema = z19.lazy(() => z19.object({
|
|
6950
|
+
kind: z19.literal("assessmentSection"),
|
|
6951
|
+
identifier: z19.string().trim().min(1),
|
|
6952
|
+
...assessmentSectionFlags,
|
|
6953
|
+
children: z19.array(z19.union([assessmentSectionSchema, itemRefSchema]))
|
|
6954
|
+
}));
|
|
6955
|
+
const testPartSchema = z19.object({
|
|
6956
|
+
identifier: z19.string().trim().min(1),
|
|
6957
|
+
navigationMode: z19.enum(["linear", "nonlinear"]),
|
|
6958
|
+
submissionMode: z19.enum(["individual", "simultaneous"]),
|
|
6959
|
+
...testPartLevel,
|
|
6960
|
+
assessmentSections: z19.array(assessmentSectionSchema)
|
|
6961
|
+
});
|
|
6962
|
+
const assessmentTestSchema = z19.object({
|
|
6963
|
+
identifier: z19.string().trim().min(1),
|
|
6964
|
+
title: z19.string().trim().min(1).optional(),
|
|
6965
|
+
outcomeDeclarations: z19.array(outcomeDeclarationSchema).optional(),
|
|
6966
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
6967
|
+
testParts: z19.array(testPartSchema),
|
|
6968
|
+
outcomeProcessing: outcomeProcessingSchema.optional(),
|
|
6969
|
+
testFeedbacks: z19.array(testFeedbackSchema).optional()
|
|
6970
|
+
});
|
|
6971
|
+
return { assessmentSectionSchema, testPartSchema, assessmentTestSchema };
|
|
6972
|
+
}
|
|
6973
|
+
var assessmentTestViewSchema = makeAssessmentTestSchema(assessmentItemRefViewSchema).assessmentTestSchema;
|
|
6803
6974
|
|
|
6804
6975
|
// src/index.ts
|
|
6805
6976
|
var qtiReactPackageName = "@conform-ed/qti-react";
|
|
@@ -6808,14 +6979,19 @@ export {
|
|
|
6808
6979
|
v0InteractionKinds,
|
|
6809
6980
|
v0ContentModel,
|
|
6810
6981
|
uploadInteraction,
|
|
6982
|
+
timeLimitsSchema,
|
|
6811
6983
|
textOf,
|
|
6812
6984
|
textEntryInteraction,
|
|
6985
|
+
testFeedbackSchema,
|
|
6813
6986
|
stimulusContentFromNormalized,
|
|
6814
6987
|
sliderInteraction,
|
|
6815
6988
|
serializePciMarkup,
|
|
6989
|
+
selectionSchema,
|
|
6816
6990
|
selectPointInteraction,
|
|
6817
6991
|
scoreResponse,
|
|
6818
6992
|
sanitizeAttributes,
|
|
6993
|
+
rpScalarSchema,
|
|
6994
|
+
rpExpressionSchema,
|
|
6819
6995
|
resolveTemplate,
|
|
6820
6996
|
resolvePnpActivation,
|
|
6821
6997
|
resolveCatalogSupports,
|
|
@@ -6830,16 +7006,25 @@ export {
|
|
|
6830
7006
|
pciResponseToValue,
|
|
6831
7007
|
parsePoint,
|
|
6832
7008
|
parseCoords,
|
|
7009
|
+
outcomeRuleSchema,
|
|
7010
|
+
outcomeProcessingSchema,
|
|
7011
|
+
outcomeDeclarationSchema,
|
|
7012
|
+
outcomeConditionBranchSchema,
|
|
7013
|
+
orderingSchema,
|
|
6833
7014
|
orderInteraction,
|
|
6834
7015
|
mulberry32,
|
|
6835
7016
|
mountPci,
|
|
6836
7017
|
mediaInteraction,
|
|
7018
|
+
matchTableSchema,
|
|
6837
7019
|
matchInteraction,
|
|
6838
7020
|
matchCorrect,
|
|
6839
7021
|
mapResponsePoint,
|
|
6840
7022
|
mapResponse,
|
|
7023
|
+
makeAssessmentTestSchema,
|
|
7024
|
+
itemSessionControlSchema,
|
|
6841
7025
|
isInteractionKind,
|
|
6842
7026
|
isAllowedFlowElement,
|
|
7027
|
+
interpolationTableSchema,
|
|
6843
7028
|
inlineChoiceInteraction,
|
|
6844
7029
|
hottextInteraction,
|
|
6845
7030
|
hotspotInteraction,
|
|
@@ -6867,9 +7052,13 @@ export {
|
|
|
6867
7052
|
collectResponseViolations,
|
|
6868
7053
|
collectInteractionConstraints,
|
|
6869
7054
|
choiceInteraction,
|
|
7055
|
+
cardinalitySchema,
|
|
7056
|
+
branchRuleSchema,
|
|
6870
7057
|
associateInteraction,
|
|
7058
|
+
assessmentTestViewSchema,
|
|
6871
7059
|
assessmentTestViewFromNormalized,
|
|
6872
7060
|
assessmentItemViewFromNormalized,
|
|
7061
|
+
assessmentItemRefViewSchema,
|
|
6873
7062
|
applyCorrectResponseOverrides,
|
|
6874
7063
|
UploadReferenceSkin,
|
|
6875
7064
|
TextEntryReferenceSkin,
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod mirrors of the Response-Processing view types (./types.ts). conform-ed owns the
|
|
3
|
+
* QTI view *types*; this is the validation surface that sits beside them — reusable for
|
|
4
|
+
* import/parse validation and kept honest against the types it mirrors. React-free.
|
|
5
|
+
*
|
|
6
|
+
* The expression union is deliberately permissive (`kind: z.string()`), exactly like
|
|
7
|
+
* `RpExpressionView`: kinds the interpreter does not implement still validate structurally
|
|
8
|
+
* and surface as `unsupported-rp` issues at evaluation time rather than being rejected here.
|
|
9
|
+
*
|
|
10
|
+
* The recursive schema is annotated against an explicit interface (`RpExpressionNode`); under
|
|
11
|
+
* `exactOptionalPropertyTypes` zod emits `T | undefined` optionals, so `z.infer` is a
|
|
12
|
+
* superset of the hand-written views (every view value parses — verified in the schema
|
|
13
|
+
* tests against real view samples). Callers that need the exact view shape map explicitly.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
import type { RpScalar } from "./types";
|
|
17
|
+
/** string | number | boolean — the QTI scalar value space (NULL is modelled by absence). */
|
|
18
|
+
export declare const rpScalarSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
19
|
+
/**
|
|
20
|
+
* The inferred shape of `rpExpressionSchema` — a faithful, recursive mirror of
|
|
21
|
+
* `RpExpressionView` with zod's `T | undefined` optionals. Declared explicitly (and the
|
|
22
|
+
* schema is annotated with it) because zod's getter-based recursion degrades the recursive
|
|
23
|
+
* `expressions` field to `Record<string, unknown>` under `z.infer`; the `z.lazy` + interface
|
|
24
|
+
* form keeps the type precise (the expression-builder authoring relies on it).
|
|
25
|
+
*/
|
|
26
|
+
export interface RpExpressionNode {
|
|
27
|
+
readonly kind: string;
|
|
28
|
+
readonly identifier?: string | undefined;
|
|
29
|
+
readonly baseType?: string | undefined;
|
|
30
|
+
readonly value?: RpScalar | undefined;
|
|
31
|
+
readonly expressions?: readonly RpExpressionNode[] | undefined;
|
|
32
|
+
readonly min?: number | string | undefined;
|
|
33
|
+
readonly max?: number | string | undefined;
|
|
34
|
+
readonly step?: number | string | undefined;
|
|
35
|
+
readonly toleranceMode?: "exact" | "absolute" | "relative" | undefined;
|
|
36
|
+
readonly tolerance?: readonly (number | string)[] | undefined;
|
|
37
|
+
readonly includeLowerBound?: boolean | undefined;
|
|
38
|
+
readonly includeUpperBound?: boolean | undefined;
|
|
39
|
+
readonly n?: number | string | undefined;
|
|
40
|
+
readonly name?: string | undefined;
|
|
41
|
+
readonly roundingMode?: "decimalPlaces" | "significantFigures" | undefined;
|
|
42
|
+
readonly figures?: number | string | undefined;
|
|
43
|
+
readonly numberRepeats?: number | string | undefined;
|
|
44
|
+
readonly pattern?: string | undefined;
|
|
45
|
+
readonly caseSensitive?: boolean | undefined;
|
|
46
|
+
readonly substring?: boolean | undefined;
|
|
47
|
+
readonly shape?: string | undefined;
|
|
48
|
+
readonly coords?: string | undefined;
|
|
49
|
+
readonly variableIdentifier?: string | undefined;
|
|
50
|
+
readonly outcomeIdentifier?: string | undefined;
|
|
51
|
+
readonly weightIdentifier?: string | undefined;
|
|
52
|
+
readonly sectionIdentifier?: string | undefined;
|
|
53
|
+
readonly includeCategory?: string | readonly string[] | undefined;
|
|
54
|
+
readonly excludeCategory?: string | readonly string[] | undefined;
|
|
55
|
+
readonly class?: string | undefined;
|
|
56
|
+
readonly definition?: string | undefined;
|
|
57
|
+
readonly fieldIdentifier?: string | undefined;
|
|
58
|
+
}
|
|
59
|
+
export declare const cardinalitySchema: z.ZodEnum<{
|
|
60
|
+
multiple: "multiple";
|
|
61
|
+
ordered: "ordered";
|
|
62
|
+
record: "record";
|
|
63
|
+
single: "single";
|
|
64
|
+
}>;
|
|
65
|
+
/** The recursive expression schema (`z.lazy` recursion; annotated for a precise `z.infer`). */
|
|
66
|
+
export declare const rpExpressionSchema: z.ZodType<RpExpressionNode>;
|
|
67
|
+
export declare const matchTableSchema: z.ZodObject<{
|
|
68
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
69
|
+
matchTableEntries: z.ZodArray<z.ZodObject<{
|
|
70
|
+
sourceValue: z.ZodNumber;
|
|
71
|
+
targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
72
|
+
}, z.core.$strip>>;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
export declare const interpolationTableSchema: z.ZodObject<{
|
|
75
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
76
|
+
interpolationTableEntries: z.ZodArray<z.ZodObject<{
|
|
77
|
+
sourceValue: z.ZodNumber;
|
|
78
|
+
targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
79
|
+
includeBoundary: z.ZodOptional<z.ZodBoolean>;
|
|
80
|
+
}, z.core.$strip>>;
|
|
81
|
+
}, z.core.$strip>;
|
|
82
|
+
export declare const outcomeDeclarationSchema: z.ZodObject<{
|
|
83
|
+
identifier: z.ZodString;
|
|
84
|
+
cardinality: z.ZodEnum<{
|
|
85
|
+
multiple: "multiple";
|
|
86
|
+
ordered: "ordered";
|
|
87
|
+
record: "record";
|
|
88
|
+
single: "single";
|
|
89
|
+
}>;
|
|
90
|
+
baseType: z.ZodOptional<z.ZodString>;
|
|
91
|
+
defaultValue: z.ZodOptional<z.ZodObject<{
|
|
92
|
+
values: z.ZodArray<z.ZodObject<{
|
|
93
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
94
|
+
}, z.core.$strip>>;
|
|
95
|
+
}, z.core.$strip>>;
|
|
96
|
+
matchTable: z.ZodOptional<z.ZodObject<{
|
|
97
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
98
|
+
matchTableEntries: z.ZodArray<z.ZodObject<{
|
|
99
|
+
sourceValue: z.ZodNumber;
|
|
100
|
+
targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
101
|
+
}, z.core.$strip>>;
|
|
102
|
+
}, z.core.$strip>>;
|
|
103
|
+
interpolationTable: z.ZodOptional<z.ZodObject<{
|
|
104
|
+
defaultValue: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
|
|
105
|
+
interpolationTableEntries: z.ZodArray<z.ZodObject<{
|
|
106
|
+
sourceValue: z.ZodNumber;
|
|
107
|
+
targetValue: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
|
|
108
|
+
includeBoundary: z.ZodOptional<z.ZodBoolean>;
|
|
109
|
+
}, z.core.$strip>>;
|
|
110
|
+
}, z.core.$strip>>;
|
|
111
|
+
normalMaximum: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
normalMinimum: z.ZodOptional<z.ZodNumber>;
|
|
113
|
+
}, z.core.$strip>;
|
|
114
|
+
export type RpScalarSchema = z.infer<typeof rpScalarSchema>;
|
|
115
|
+
export type RpExpressionSchema = z.infer<typeof rpExpressionSchema>;
|
|
116
|
+
export type OutcomeDeclarationSchema = z.infer<typeof outcomeDeclarationSchema>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod mirrors of the `assessmentTest` structural views (./types.ts) — the shared QTI
|
|
3
|
+
* atoms (`timeLimits`, `itemSessionControl`, `selection`, `ordering`, `branchRule`,
|
|
4
|
+
* test-level outcome processing/feedback) and the recursive structure
|
|
5
|
+
* (`assessmentSection` / `testPart` / `assessmentTest`).
|
|
6
|
+
*
|
|
7
|
+
* The structure is exposed as a **factory parameterized by the itemRef schema** —
|
|
8
|
+
* `makeAssessmentTestSchema(itemRefSchema)` — because the only thing that varies between
|
|
9
|
+
* delivery (an `href`) and authoring (an `itemVersionId`) is the item reference's identity.
|
|
10
|
+
* The ready `assessmentTestViewSchema` binds the delivery `href` itemRef; emergent binds
|
|
11
|
+
* its authoring itemRef. React-free; see ../rp/schema.ts for the RP-level mirrors.
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { type OutcomeDeclarationSchema, type RpExpressionSchema } from "../rp/schema";
|
|
15
|
+
/** `timeLimits` (seconds); the controller enforces these under its injected clock. */
|
|
16
|
+
export declare const timeLimitsSchema: z.ZodObject<{
|
|
17
|
+
minTime: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
maxTime: z.ZodOptional<z.ZodNumber>;
|
|
19
|
+
allowLateSubmission: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
/** `itemSessionControl`: per-level overrides cascading testPart → section → itemRef. */
|
|
22
|
+
export declare const itemSessionControlSchema: z.ZodObject<{
|
|
23
|
+
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
showFeedback: z.ZodOptional<z.ZodBoolean>;
|
|
25
|
+
allowReview: z.ZodOptional<z.ZodBoolean>;
|
|
26
|
+
showSolution: z.ZodOptional<z.ZodBoolean>;
|
|
27
|
+
allowComment: z.ZodOptional<z.ZodBoolean>;
|
|
28
|
+
allowSkipping: z.ZodOptional<z.ZodBoolean>;
|
|
29
|
+
validateResponses: z.ZodOptional<z.ZodBoolean>;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
/** `selection`: how many children to draw, optionally with replacement (§4.2.6). */
|
|
32
|
+
export declare const selectionSchema: z.ZodObject<{
|
|
33
|
+
select: z.ZodNumber;
|
|
34
|
+
withReplacement: z.ZodOptional<z.ZodBoolean>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
/** `ordering`: whether the surviving children are shuffled (§4.2.7). */
|
|
37
|
+
export declare const orderingSchema: z.ZodObject<{
|
|
38
|
+
shuffle: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
export type TimeLimitsSchema = z.infer<typeof timeLimitsSchema>;
|
|
41
|
+
export type ItemSessionControlSchema = z.infer<typeof itemSessionControlSchema>;
|
|
42
|
+
export type SelectionSchema = z.infer<typeof selectionSchema>;
|
|
43
|
+
export type OrderingSchema = z.infer<typeof orderingSchema>;
|
|
44
|
+
/** `branchRule`: a target identifier (or EXIT_*) gated by a boolean expression. */
|
|
45
|
+
export declare const branchRuleSchema: z.ZodObject<{
|
|
46
|
+
target: z.ZodString;
|
|
47
|
+
expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
|
|
48
|
+
}, z.core.$strip>;
|
|
49
|
+
export type BranchRuleSchema = z.infer<typeof branchRuleSchema>;
|
|
50
|
+
/** A gate plus its nested rules (`outcomeIf` / `outcomeElseIf`); see `OutcomeRuleNode`. */
|
|
51
|
+
export interface OutcomeConditionBranchNode {
|
|
52
|
+
readonly expression: RpExpressionSchema;
|
|
53
|
+
readonly rules: readonly OutcomeRuleNode[];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The recursive outcome-rule shape (a faithful mirror of `OutcomeRuleView` with zod's
|
|
57
|
+
* `T | undefined` optionals). Declared explicitly so `z.infer` stays precise through the
|
|
58
|
+
* recursion (the getter form degrades nested rules to `Record<string, unknown>`).
|
|
59
|
+
*/
|
|
60
|
+
export interface OutcomeRuleNode {
|
|
61
|
+
readonly kind: string;
|
|
62
|
+
readonly identifier?: string | undefined;
|
|
63
|
+
readonly expression?: RpExpressionSchema | undefined;
|
|
64
|
+
readonly rules?: readonly OutcomeRuleNode[] | undefined;
|
|
65
|
+
readonly outcomeIf?: OutcomeConditionBranchNode | undefined;
|
|
66
|
+
readonly outcomeElseIfs?: readonly OutcomeConditionBranchNode[] | undefined;
|
|
67
|
+
readonly outcomeElse?: {
|
|
68
|
+
readonly rules: readonly OutcomeRuleNode[];
|
|
69
|
+
} | undefined;
|
|
70
|
+
}
|
|
71
|
+
/** One `outcomeIf` / `outcomeElseIf` branch: a gate plus its nested rules. */
|
|
72
|
+
export declare const outcomeConditionBranchSchema: z.ZodType<OutcomeConditionBranchNode>;
|
|
73
|
+
/** A recursive outcome rule (mirrors `OutcomeRuleView`); `kind` stays permissive. */
|
|
74
|
+
export declare const outcomeRuleSchema: z.ZodType<OutcomeRuleNode>;
|
|
75
|
+
export declare const outcomeProcessingSchema: z.ZodObject<{
|
|
76
|
+
rules: z.ZodArray<z.ZodType<OutcomeRuleNode, unknown, z.core.$ZodTypeInternals<OutcomeRuleNode, unknown>>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
export type OutcomeProcessingSchema = z.infer<typeof outcomeProcessingSchema>;
|
|
79
|
+
/**
|
|
80
|
+
* `testFeedback` — the scalar attributes are mirrored; the `content` body is accepted
|
|
81
|
+
* opaquely (BodyNode is the item runtime's content model, not authored at the test level).
|
|
82
|
+
*/
|
|
83
|
+
export declare const testFeedbackSchema: z.ZodObject<{
|
|
84
|
+
access: z.ZodOptional<z.ZodEnum<{
|
|
85
|
+
atEnd: "atEnd";
|
|
86
|
+
during: "during";
|
|
87
|
+
}>>;
|
|
88
|
+
outcomeIdentifier: z.ZodString;
|
|
89
|
+
identifier: z.ZodString;
|
|
90
|
+
showHide: z.ZodOptional<z.ZodEnum<{
|
|
91
|
+
hide: "hide";
|
|
92
|
+
show: "show";
|
|
93
|
+
}>>;
|
|
94
|
+
content: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
95
|
+
}, z.core.$strip>;
|
|
96
|
+
export type TestFeedbackSchema = z.infer<typeof testFeedbackSchema>;
|
|
97
|
+
/** The delivery `assessmentItemRef` (`href` identity) — the ready view itemRef. */
|
|
98
|
+
export declare const assessmentItemRefViewSchema: z.ZodObject<{
|
|
99
|
+
kind: z.ZodLiteral<"assessmentItemRef">;
|
|
100
|
+
identifier: z.ZodString;
|
|
101
|
+
href: z.ZodOptional<z.ZodString>;
|
|
102
|
+
categories: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
103
|
+
fixed: z.ZodOptional<z.ZodBoolean>;
|
|
104
|
+
required: z.ZodOptional<z.ZodBoolean>;
|
|
105
|
+
preConditions: z.ZodOptional<z.ZodArray<z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>>>;
|
|
106
|
+
branchRules: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
107
|
+
target: z.ZodString;
|
|
108
|
+
expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
|
|
109
|
+
}, z.core.$strip>>>;
|
|
110
|
+
itemSessionControl: z.ZodOptional<z.ZodObject<{
|
|
111
|
+
maxAttempts: z.ZodOptional<z.ZodNumber>;
|
|
112
|
+
showFeedback: z.ZodOptional<z.ZodBoolean>;
|
|
113
|
+
allowReview: z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
showSolution: z.ZodOptional<z.ZodBoolean>;
|
|
115
|
+
allowComment: z.ZodOptional<z.ZodBoolean>;
|
|
116
|
+
allowSkipping: z.ZodOptional<z.ZodBoolean>;
|
|
117
|
+
validateResponses: z.ZodOptional<z.ZodBoolean>;
|
|
118
|
+
}, z.core.$strip>>;
|
|
119
|
+
timeLimits: z.ZodOptional<z.ZodObject<{
|
|
120
|
+
minTime: z.ZodOptional<z.ZodNumber>;
|
|
121
|
+
maxTime: z.ZodOptional<z.ZodNumber>;
|
|
122
|
+
allowLateSubmission: z.ZodOptional<z.ZodBoolean>;
|
|
123
|
+
}, z.core.$strip>>;
|
|
124
|
+
weights: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
125
|
+
identifier: z.ZodString;
|
|
126
|
+
value: z.ZodNumber;
|
|
127
|
+
}, z.core.$strip>>>;
|
|
128
|
+
templateDefaults: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
129
|
+
templateIdentifier: z.ZodString;
|
|
130
|
+
expression: z.ZodType<import("../rp/schema").RpExpressionNode, unknown, z.core.$ZodTypeInternals<import("../rp/schema").RpExpressionNode, unknown>>;
|
|
131
|
+
}, z.core.$strip>>>;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
/**
|
|
134
|
+
* The recursive section shape, parameterized by the injected itemRef's inferred type.
|
|
135
|
+
* Optionals carry `| undefined` and arrays are `readonly` so this matches zod's object
|
|
136
|
+
* output exactly — letting the `z.lazy` section schema below be annotated `z.ZodType<…>`
|
|
137
|
+
* (a getter cannot self-infer once a generic itemRef joins the `children` union).
|
|
138
|
+
*/
|
|
139
|
+
export interface AssessmentSectionNode<TItemRef> {
|
|
140
|
+
readonly kind: "assessmentSection";
|
|
141
|
+
readonly identifier: string;
|
|
142
|
+
readonly title?: string | undefined;
|
|
143
|
+
readonly visible?: boolean | undefined;
|
|
144
|
+
readonly fixed?: boolean | undefined;
|
|
145
|
+
readonly required?: boolean | undefined;
|
|
146
|
+
readonly keepTogether?: boolean | undefined;
|
|
147
|
+
readonly selection?: SelectionSchema | undefined;
|
|
148
|
+
readonly ordering?: OrderingSchema | undefined;
|
|
149
|
+
readonly preConditions?: readonly RpExpressionSchema[] | undefined;
|
|
150
|
+
readonly branchRules?: readonly BranchRuleSchema[] | undefined;
|
|
151
|
+
readonly itemSessionControl?: ItemSessionControlSchema | undefined;
|
|
152
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
153
|
+
readonly children: ReadonlyArray<AssessmentSectionNode<TItemRef> | TItemRef>;
|
|
154
|
+
}
|
|
155
|
+
/** A test part: navigation/submission modes plus the cascade levels and its sections. */
|
|
156
|
+
export interface TestPartNode<TItemRef> {
|
|
157
|
+
readonly identifier: string;
|
|
158
|
+
readonly navigationMode: "linear" | "nonlinear";
|
|
159
|
+
readonly submissionMode: "individual" | "simultaneous";
|
|
160
|
+
readonly preConditions?: readonly RpExpressionSchema[] | undefined;
|
|
161
|
+
readonly branchRules?: readonly BranchRuleSchema[] | undefined;
|
|
162
|
+
readonly itemSessionControl?: ItemSessionControlSchema | undefined;
|
|
163
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
164
|
+
readonly assessmentSections: readonly AssessmentSectionNode<TItemRef>[];
|
|
165
|
+
}
|
|
166
|
+
/** The whole `assessmentTest`: declarations/processing/feedback plus the ordered parts. */
|
|
167
|
+
export interface AssessmentTestNode<TItemRef> {
|
|
168
|
+
readonly identifier: string;
|
|
169
|
+
readonly title?: string | undefined;
|
|
170
|
+
readonly outcomeDeclarations?: readonly OutcomeDeclarationSchema[] | undefined;
|
|
171
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
172
|
+
readonly testParts: readonly TestPartNode<TItemRef>[];
|
|
173
|
+
readonly outcomeProcessing?: OutcomeProcessingSchema | undefined;
|
|
174
|
+
readonly testFeedbacks?: readonly TestFeedbackSchema[] | undefined;
|
|
175
|
+
}
|
|
176
|
+
/** The schema bundle the factory returns (each level parameterized by the itemRef type). */
|
|
177
|
+
export interface AssessmentTestSchemas<TItemRef> {
|
|
178
|
+
readonly assessmentSectionSchema: z.ZodType<AssessmentSectionNode<TItemRef>>;
|
|
179
|
+
readonly testPartSchema: z.ZodType<TestPartNode<TItemRef>>;
|
|
180
|
+
readonly assessmentTestSchema: z.ZodType<AssessmentTestNode<TItemRef>>;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Build the recursive `assessmentTest` schema around a caller-supplied itemRef schema.
|
|
184
|
+
* Sections nest sections-or-itemRefs through `children` (recursion via `z.lazy`); `z.infer`
|
|
185
|
+
* follows the recursion and carries the injected itemRef's inferred type. The explicit
|
|
186
|
+
* return type keeps the package's `isolatedDeclarations` build able to emit the bundle.
|
|
187
|
+
*/
|
|
188
|
+
export declare function makeAssessmentTestSchema<ItemRef extends z.ZodType>(itemRefSchema: ItemRef): AssessmentTestSchemas<z.infer<ItemRef>>;
|
|
189
|
+
/** The ready delivery-view schema: the recursive structure over the `href` itemRef. */
|
|
190
|
+
export declare const assessmentTestViewSchema: z.ZodType<AssessmentTestNode<AssessmentItemRefViewSchema>>;
|
|
191
|
+
export type AssessmentItemRefViewSchema = z.infer<typeof assessmentItemRefViewSchema>;
|
|
192
|
+
export type AssessmentTestViewSchema = z.infer<typeof assessmentTestViewSchema>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@conform-ed/qti-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"dist"
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"xspattern": "^3.1.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@conform-ed/contracts": "0.0.
|
|
34
|
-
"@conform-ed/qti-xml": "0.0.
|
|
33
|
+
"@conform-ed/contracts": "0.0.21",
|
|
34
|
+
"@conform-ed/qti-xml": "0.0.21",
|
|
35
35
|
"@types/react": "^19.2.17",
|
|
36
36
|
"@types/react-dom": "^19",
|
|
37
37
|
"happy-dom": "^20.10.3",
|
package/src/headless.ts
CHANGED
|
@@ -34,12 +34,20 @@ export {
|
|
|
34
34
|
} from "./rp";
|
|
35
35
|
export type {
|
|
36
36
|
CustomOperatorImplementation,
|
|
37
|
+
InterpolationTableView,
|
|
38
|
+
MatchTableView,
|
|
39
|
+
MaybeRpValue,
|
|
37
40
|
OutcomeDeclarationView,
|
|
38
41
|
OutcomeValue,
|
|
39
42
|
ResponseNormalization,
|
|
40
43
|
ResponseProcessingContext,
|
|
41
44
|
ResponseProcessingResult,
|
|
42
45
|
ResponseProcessingView,
|
|
46
|
+
RpConditionBranch,
|
|
47
|
+
RpExpressionView,
|
|
48
|
+
RpRuleView,
|
|
49
|
+
RpScalar,
|
|
50
|
+
RpValue,
|
|
43
51
|
TemplateDeclarationView,
|
|
44
52
|
} from "./rp";
|
|
45
53
|
export { createAttemptStore, type AttemptSnapshot, type AttemptStore, type AttemptStoreOptions } from "./store";
|
|
@@ -54,5 +62,45 @@ export type {
|
|
|
54
62
|
StimulusContentView,
|
|
55
63
|
XmlContentNode,
|
|
56
64
|
} from "./runtime";
|
|
57
|
-
export type { AssessmentTestView } from "./test";
|
|
65
|
+
export type { AssessmentItemRefView, AssessmentSectionView, AssessmentTestView, TestPartView } from "./test";
|
|
58
66
|
export type { Cardinality, ResponseDeclarationView, ResponseValue, ScoreResult } from "./types";
|
|
67
|
+
|
|
68
|
+
// QTI validation surface (zod mirrors of the views above) — atoms, the recursive
|
|
69
|
+
// rpExpression/outcome schemas, and the structure factory. React-free; reusable for
|
|
70
|
+
// import/parse validation and parameterized for authoring deltas (emergent's itemVersionId).
|
|
71
|
+
export {
|
|
72
|
+
cardinalitySchema,
|
|
73
|
+
interpolationTableSchema,
|
|
74
|
+
matchTableSchema,
|
|
75
|
+
outcomeDeclarationSchema,
|
|
76
|
+
rpExpressionSchema,
|
|
77
|
+
rpScalarSchema,
|
|
78
|
+
type OutcomeDeclarationSchema,
|
|
79
|
+
type RpExpressionSchema,
|
|
80
|
+
type RpScalarSchema,
|
|
81
|
+
} from "./rp/schema";
|
|
82
|
+
export {
|
|
83
|
+
assessmentItemRefViewSchema,
|
|
84
|
+
assessmentTestViewSchema,
|
|
85
|
+
branchRuleSchema,
|
|
86
|
+
itemSessionControlSchema,
|
|
87
|
+
makeAssessmentTestSchema,
|
|
88
|
+
orderingSchema,
|
|
89
|
+
outcomeConditionBranchSchema,
|
|
90
|
+
outcomeProcessingSchema,
|
|
91
|
+
outcomeRuleSchema,
|
|
92
|
+
selectionSchema,
|
|
93
|
+
testFeedbackSchema,
|
|
94
|
+
timeLimitsSchema,
|
|
95
|
+
type AssessmentItemRefViewSchema,
|
|
96
|
+
type AssessmentSectionNode,
|
|
97
|
+
type AssessmentTestNode,
|
|
98
|
+
type AssessmentTestSchemas,
|
|
99
|
+
type AssessmentTestViewSchema,
|
|
100
|
+
type BranchRuleSchema,
|
|
101
|
+
type ItemSessionControlSchema,
|
|
102
|
+
type OrderingSchema,
|
|
103
|
+
type SelectionSchema,
|
|
104
|
+
type TestPartNode,
|
|
105
|
+
type TimeLimitsSchema,
|
|
106
|
+
} from "./test/schema";
|
package/src/index.ts
CHANGED
|
@@ -229,3 +229,42 @@ export {
|
|
|
229
229
|
type PnpView,
|
|
230
230
|
type ResolvedCatalogSupport,
|
|
231
231
|
} from "./pnp";
|
|
232
|
+
|
|
233
|
+
// QTI validation surface (zod mirrors of the assessmentTest/RP views). Also exposed at
|
|
234
|
+
// `@conform-ed/qti-react/headless` for server-side import/parse validation without React.
|
|
235
|
+
export {
|
|
236
|
+
cardinalitySchema,
|
|
237
|
+
interpolationTableSchema,
|
|
238
|
+
matchTableSchema,
|
|
239
|
+
outcomeDeclarationSchema,
|
|
240
|
+
rpExpressionSchema,
|
|
241
|
+
rpScalarSchema,
|
|
242
|
+
type OutcomeDeclarationSchema,
|
|
243
|
+
type RpExpressionSchema,
|
|
244
|
+
type RpScalarSchema,
|
|
245
|
+
} from "./rp/schema";
|
|
246
|
+
export {
|
|
247
|
+
assessmentItemRefViewSchema,
|
|
248
|
+
assessmentTestViewSchema,
|
|
249
|
+
branchRuleSchema,
|
|
250
|
+
itemSessionControlSchema,
|
|
251
|
+
makeAssessmentTestSchema,
|
|
252
|
+
orderingSchema,
|
|
253
|
+
outcomeConditionBranchSchema,
|
|
254
|
+
outcomeProcessingSchema,
|
|
255
|
+
outcomeRuleSchema,
|
|
256
|
+
selectionSchema,
|
|
257
|
+
testFeedbackSchema,
|
|
258
|
+
timeLimitsSchema,
|
|
259
|
+
type AssessmentItemRefViewSchema,
|
|
260
|
+
type AssessmentSectionNode,
|
|
261
|
+
type AssessmentTestNode,
|
|
262
|
+
type AssessmentTestSchemas,
|
|
263
|
+
type AssessmentTestViewSchema,
|
|
264
|
+
type BranchRuleSchema,
|
|
265
|
+
type ItemSessionControlSchema,
|
|
266
|
+
type OrderingSchema,
|
|
267
|
+
type SelectionSchema,
|
|
268
|
+
type TestPartNode,
|
|
269
|
+
type TimeLimitsSchema,
|
|
270
|
+
} from "./test/schema";
|
package/src/rp/schema.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod mirrors of the Response-Processing view types (./types.ts). conform-ed owns the
|
|
3
|
+
* QTI view *types*; this is the validation surface that sits beside them — reusable for
|
|
4
|
+
* import/parse validation and kept honest against the types it mirrors. React-free.
|
|
5
|
+
*
|
|
6
|
+
* The expression union is deliberately permissive (`kind: z.string()`), exactly like
|
|
7
|
+
* `RpExpressionView`: kinds the interpreter does not implement still validate structurally
|
|
8
|
+
* and surface as `unsupported-rp` issues at evaluation time rather than being rejected here.
|
|
9
|
+
*
|
|
10
|
+
* The recursive schema is annotated against an explicit interface (`RpExpressionNode`); under
|
|
11
|
+
* `exactOptionalPropertyTypes` zod emits `T | undefined` optionals, so `z.infer` is a
|
|
12
|
+
* superset of the hand-written views (every view value parses — verified in the schema
|
|
13
|
+
* tests against real view samples). Callers that need the exact view shape map explicitly.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
import type { RpScalar } from "./types";
|
|
19
|
+
|
|
20
|
+
/** string | number | boolean — the QTI scalar value space (NULL is modelled by absence). */
|
|
21
|
+
export const rpScalarSchema = z.union([z.string(), z.number(), z.boolean()]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The inferred shape of `rpExpressionSchema` — a faithful, recursive mirror of
|
|
25
|
+
* `RpExpressionView` with zod's `T | undefined` optionals. Declared explicitly (and the
|
|
26
|
+
* schema is annotated with it) because zod's getter-based recursion degrades the recursive
|
|
27
|
+
* `expressions` field to `Record<string, unknown>` under `z.infer`; the `z.lazy` + interface
|
|
28
|
+
* form keeps the type precise (the expression-builder authoring relies on it).
|
|
29
|
+
*/
|
|
30
|
+
export interface RpExpressionNode {
|
|
31
|
+
readonly kind: string;
|
|
32
|
+
readonly identifier?: string | undefined;
|
|
33
|
+
readonly baseType?: string | undefined;
|
|
34
|
+
readonly value?: RpScalar | undefined;
|
|
35
|
+
readonly expressions?: readonly RpExpressionNode[] | undefined;
|
|
36
|
+
readonly min?: number | string | undefined;
|
|
37
|
+
readonly max?: number | string | undefined;
|
|
38
|
+
readonly step?: number | string | undefined;
|
|
39
|
+
readonly toleranceMode?: "exact" | "absolute" | "relative" | undefined;
|
|
40
|
+
readonly tolerance?: readonly (number | string)[] | undefined;
|
|
41
|
+
readonly includeLowerBound?: boolean | undefined;
|
|
42
|
+
readonly includeUpperBound?: boolean | undefined;
|
|
43
|
+
readonly n?: number | string | undefined;
|
|
44
|
+
readonly name?: string | undefined;
|
|
45
|
+
readonly roundingMode?: "decimalPlaces" | "significantFigures" | undefined;
|
|
46
|
+
readonly figures?: number | string | undefined;
|
|
47
|
+
readonly numberRepeats?: number | string | undefined;
|
|
48
|
+
readonly pattern?: string | undefined;
|
|
49
|
+
readonly caseSensitive?: boolean | undefined;
|
|
50
|
+
readonly substring?: boolean | undefined;
|
|
51
|
+
readonly shape?: string | undefined;
|
|
52
|
+
readonly coords?: string | undefined;
|
|
53
|
+
readonly variableIdentifier?: string | undefined;
|
|
54
|
+
readonly outcomeIdentifier?: string | undefined;
|
|
55
|
+
readonly weightIdentifier?: string | undefined;
|
|
56
|
+
readonly sectionIdentifier?: string | undefined;
|
|
57
|
+
readonly includeCategory?: string | readonly string[] | undefined;
|
|
58
|
+
readonly excludeCategory?: string | readonly string[] | undefined;
|
|
59
|
+
readonly class?: string | undefined;
|
|
60
|
+
readonly definition?: string | undefined;
|
|
61
|
+
readonly fieldIdentifier?: string | undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A numeric attribute that may also be a variable reference (§2.11.3.6 / §7.13): the
|
|
66
|
+
* random/aggregate bounds, `index`/`anyN` positions, rounding `figures`, `repeat` counts.
|
|
67
|
+
*/
|
|
68
|
+
const numberOrRef = z.union([z.number(), z.string()]);
|
|
69
|
+
|
|
70
|
+
/** `includeCategory` / `excludeCategory` accept a single category or a list. */
|
|
71
|
+
const categoryFilter = z.union([z.string(), z.array(z.string())]);
|
|
72
|
+
|
|
73
|
+
export const cardinalitySchema = z.enum(["single", "multiple", "ordered", "record"]);
|
|
74
|
+
|
|
75
|
+
/** The recursive expression schema (`z.lazy` recursion; annotated for a precise `z.infer`). */
|
|
76
|
+
export const rpExpressionSchema: z.ZodType<RpExpressionNode> = z.lazy(() =>
|
|
77
|
+
z.object({
|
|
78
|
+
kind: z.string(),
|
|
79
|
+
identifier: z.string().optional(),
|
|
80
|
+
baseType: z.string().optional(),
|
|
81
|
+
value: rpScalarSchema.optional(),
|
|
82
|
+
expressions: z.array(rpExpressionSchema).optional(),
|
|
83
|
+
min: numberOrRef.optional(),
|
|
84
|
+
max: numberOrRef.optional(),
|
|
85
|
+
step: numberOrRef.optional(),
|
|
86
|
+
toleranceMode: z.enum(["exact", "absolute", "relative"]).optional(),
|
|
87
|
+
tolerance: z.array(numberOrRef).optional(),
|
|
88
|
+
includeLowerBound: z.boolean().optional(),
|
|
89
|
+
includeUpperBound: z.boolean().optional(),
|
|
90
|
+
n: numberOrRef.optional(),
|
|
91
|
+
name: z.string().optional(),
|
|
92
|
+
roundingMode: z.enum(["decimalPlaces", "significantFigures"]).optional(),
|
|
93
|
+
figures: numberOrRef.optional(),
|
|
94
|
+
numberRepeats: numberOrRef.optional(),
|
|
95
|
+
pattern: z.string().optional(),
|
|
96
|
+
caseSensitive: z.boolean().optional(),
|
|
97
|
+
substring: z.boolean().optional(),
|
|
98
|
+
shape: z.string().optional(),
|
|
99
|
+
coords: z.string().optional(),
|
|
100
|
+
variableIdentifier: z.string().optional(),
|
|
101
|
+
outcomeIdentifier: z.string().optional(),
|
|
102
|
+
weightIdentifier: z.string().optional(),
|
|
103
|
+
sectionIdentifier: z.string().optional(),
|
|
104
|
+
includeCategory: categoryFilter.optional(),
|
|
105
|
+
excludeCategory: categoryFilter.optional(),
|
|
106
|
+
class: z.string().optional(),
|
|
107
|
+
definition: z.string().optional(),
|
|
108
|
+
fieldIdentifier: z.string().optional(),
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// ---------- Outcome declarations (lookup tables) ----------
|
|
113
|
+
|
|
114
|
+
export const matchTableSchema = z.object({
|
|
115
|
+
defaultValue: rpScalarSchema.optional(),
|
|
116
|
+
matchTableEntries: z.array(z.object({ sourceValue: z.number(), targetValue: rpScalarSchema })),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const interpolationTableSchema = z.object({
|
|
120
|
+
defaultValue: rpScalarSchema.optional(),
|
|
121
|
+
interpolationTableEntries: z.array(
|
|
122
|
+
z.object({
|
|
123
|
+
sourceValue: z.number(),
|
|
124
|
+
targetValue: rpScalarSchema,
|
|
125
|
+
includeBoundary: z.boolean().optional(),
|
|
126
|
+
}),
|
|
127
|
+
),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
export const outcomeDeclarationSchema = z.object({
|
|
131
|
+
identifier: z.string(),
|
|
132
|
+
cardinality: cardinalitySchema,
|
|
133
|
+
baseType: z.string().optional(),
|
|
134
|
+
defaultValue: z.object({ values: z.array(z.object({ value: rpScalarSchema })) }).optional(),
|
|
135
|
+
matchTable: matchTableSchema.optional(),
|
|
136
|
+
interpolationTable: interpolationTableSchema.optional(),
|
|
137
|
+
normalMaximum: z.number().optional(),
|
|
138
|
+
normalMinimum: z.number().optional(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export type RpScalarSchema = z.infer<typeof rpScalarSchema>;
|
|
142
|
+
export type RpExpressionSchema = z.infer<typeof rpExpressionSchema>;
|
|
143
|
+
export type OutcomeDeclarationSchema = z.infer<typeof outcomeDeclarationSchema>;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod mirrors of the `assessmentTest` structural views (./types.ts) — the shared QTI
|
|
3
|
+
* atoms (`timeLimits`, `itemSessionControl`, `selection`, `ordering`, `branchRule`,
|
|
4
|
+
* test-level outcome processing/feedback) and the recursive structure
|
|
5
|
+
* (`assessmentSection` / `testPart` / `assessmentTest`).
|
|
6
|
+
*
|
|
7
|
+
* The structure is exposed as a **factory parameterized by the itemRef schema** —
|
|
8
|
+
* `makeAssessmentTestSchema(itemRefSchema)` — because the only thing that varies between
|
|
9
|
+
* delivery (an `href`) and authoring (an `itemVersionId`) is the item reference's identity.
|
|
10
|
+
* The ready `assessmentTestViewSchema` binds the delivery `href` itemRef; emergent binds
|
|
11
|
+
* its authoring itemRef. React-free; see ../rp/schema.ts for the RP-level mirrors.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
outcomeDeclarationSchema,
|
|
18
|
+
type OutcomeDeclarationSchema,
|
|
19
|
+
type RpExpressionSchema,
|
|
20
|
+
rpExpressionSchema,
|
|
21
|
+
} from "../rp/schema";
|
|
22
|
+
|
|
23
|
+
// ---------- Shared atoms (cascade levels: testPart → section → itemRef) ----------
|
|
24
|
+
|
|
25
|
+
/** `timeLimits` (seconds); the controller enforces these under its injected clock. */
|
|
26
|
+
export const timeLimitsSchema = z.object({
|
|
27
|
+
minTime: z.number().nonnegative().optional(),
|
|
28
|
+
maxTime: z.number().nonnegative().optional(),
|
|
29
|
+
allowLateSubmission: z.boolean().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/** `itemSessionControl`: per-level overrides cascading testPart → section → itemRef. */
|
|
33
|
+
export const itemSessionControlSchema = z.object({
|
|
34
|
+
maxAttempts: z.number().int().nonnegative().optional(),
|
|
35
|
+
showFeedback: z.boolean().optional(),
|
|
36
|
+
allowReview: z.boolean().optional(),
|
|
37
|
+
showSolution: z.boolean().optional(),
|
|
38
|
+
allowComment: z.boolean().optional(),
|
|
39
|
+
allowSkipping: z.boolean().optional(),
|
|
40
|
+
validateResponses: z.boolean().optional(),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/** `selection`: how many children to draw, optionally with replacement (§4.2.6). */
|
|
44
|
+
export const selectionSchema = z.object({
|
|
45
|
+
select: z.number().int().nonnegative(),
|
|
46
|
+
withReplacement: z.boolean().optional(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/** `ordering`: whether the surviving children are shuffled (§4.2.7). */
|
|
50
|
+
export const orderingSchema = z.object({ shuffle: z.boolean().optional() });
|
|
51
|
+
|
|
52
|
+
export type TimeLimitsSchema = z.infer<typeof timeLimitsSchema>;
|
|
53
|
+
export type ItemSessionControlSchema = z.infer<typeof itemSessionControlSchema>;
|
|
54
|
+
export type SelectionSchema = z.infer<typeof selectionSchema>;
|
|
55
|
+
export type OrderingSchema = z.infer<typeof orderingSchema>;
|
|
56
|
+
|
|
57
|
+
/** `branchRule`: a target identifier (or EXIT_*) gated by a boolean expression. */
|
|
58
|
+
export const branchRuleSchema = z.object({
|
|
59
|
+
target: z.string().trim().min(1),
|
|
60
|
+
expression: rpExpressionSchema,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export type BranchRuleSchema = z.infer<typeof branchRuleSchema>;
|
|
64
|
+
|
|
65
|
+
const weightSchema = z.object({ identifier: z.string().trim().min(1), value: z.number() });
|
|
66
|
+
|
|
67
|
+
const templateDefaultSchema = z.object({
|
|
68
|
+
templateIdentifier: z.string().trim().min(1),
|
|
69
|
+
expression: rpExpressionSchema,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ---------- Test-level outcome processing + feedback ----------
|
|
73
|
+
|
|
74
|
+
/** A gate plus its nested rules (`outcomeIf` / `outcomeElseIf`); see `OutcomeRuleNode`. */
|
|
75
|
+
export interface OutcomeConditionBranchNode {
|
|
76
|
+
readonly expression: RpExpressionSchema;
|
|
77
|
+
readonly rules: readonly OutcomeRuleNode[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The recursive outcome-rule shape (a faithful mirror of `OutcomeRuleView` with zod's
|
|
82
|
+
* `T | undefined` optionals). Declared explicitly so `z.infer` stays precise through the
|
|
83
|
+
* recursion (the getter form degrades nested rules to `Record<string, unknown>`).
|
|
84
|
+
*/
|
|
85
|
+
export interface OutcomeRuleNode {
|
|
86
|
+
readonly kind: string;
|
|
87
|
+
readonly identifier?: string | undefined;
|
|
88
|
+
readonly expression?: RpExpressionSchema | undefined;
|
|
89
|
+
readonly rules?: readonly OutcomeRuleNode[] | undefined;
|
|
90
|
+
readonly outcomeIf?: OutcomeConditionBranchNode | undefined;
|
|
91
|
+
readonly outcomeElseIfs?: readonly OutcomeConditionBranchNode[] | undefined;
|
|
92
|
+
readonly outcomeElse?: { readonly rules: readonly OutcomeRuleNode[] } | undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** One `outcomeIf` / `outcomeElseIf` branch: a gate plus its nested rules. */
|
|
96
|
+
export const outcomeConditionBranchSchema: z.ZodType<OutcomeConditionBranchNode> = z.lazy(() =>
|
|
97
|
+
z.object({
|
|
98
|
+
expression: rpExpressionSchema,
|
|
99
|
+
rules: z.array(outcomeRuleSchema),
|
|
100
|
+
}),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/** A recursive outcome rule (mirrors `OutcomeRuleView`); `kind` stays permissive. */
|
|
104
|
+
export const outcomeRuleSchema: z.ZodType<OutcomeRuleNode> = z.lazy(() =>
|
|
105
|
+
z.object({
|
|
106
|
+
kind: z.string(),
|
|
107
|
+
identifier: z.string().optional(),
|
|
108
|
+
expression: rpExpressionSchema.optional(),
|
|
109
|
+
rules: z.array(outcomeRuleSchema).optional(),
|
|
110
|
+
outcomeIf: outcomeConditionBranchSchema.optional(),
|
|
111
|
+
outcomeElseIfs: z.array(outcomeConditionBranchSchema).optional(),
|
|
112
|
+
outcomeElse: z.object({ rules: z.array(outcomeRuleSchema) }).optional(),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export const outcomeProcessingSchema = z.object({ rules: z.array(outcomeRuleSchema) });
|
|
117
|
+
|
|
118
|
+
export type OutcomeProcessingSchema = z.infer<typeof outcomeProcessingSchema>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* `testFeedback` — the scalar attributes are mirrored; the `content` body is accepted
|
|
122
|
+
* opaquely (BodyNode is the item runtime's content model, not authored at the test level).
|
|
123
|
+
*/
|
|
124
|
+
export const testFeedbackSchema = z.object({
|
|
125
|
+
access: z.enum(["atEnd", "during"]).optional(),
|
|
126
|
+
outcomeIdentifier: z.string().trim().min(1),
|
|
127
|
+
identifier: z.string().trim().min(1),
|
|
128
|
+
showHide: z.enum(["show", "hide"]).optional(),
|
|
129
|
+
content: z.array(z.unknown()).optional(),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
export type TestFeedbackSchema = z.infer<typeof testFeedbackSchema>;
|
|
133
|
+
|
|
134
|
+
// ---------- The delivery itemRef (href identity) ----------
|
|
135
|
+
|
|
136
|
+
/** The delivery `assessmentItemRef` (`href` identity) — the ready view itemRef. */
|
|
137
|
+
export const assessmentItemRefViewSchema = z.object({
|
|
138
|
+
kind: z.literal("assessmentItemRef"),
|
|
139
|
+
identifier: z.string().trim().min(1),
|
|
140
|
+
href: z.string().optional(),
|
|
141
|
+
categories: z.array(z.string()).optional(),
|
|
142
|
+
fixed: z.boolean().optional(),
|
|
143
|
+
required: z.boolean().optional(),
|
|
144
|
+
preConditions: z.array(rpExpressionSchema).optional(),
|
|
145
|
+
branchRules: z.array(branchRuleSchema).optional(),
|
|
146
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
147
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
148
|
+
weights: z.array(weightSchema).optional(),
|
|
149
|
+
templateDefaults: z.array(templateDefaultSchema).optional(),
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ---------- The recursive structure factory ----------
|
|
153
|
+
|
|
154
|
+
const assessmentSectionFlags = {
|
|
155
|
+
title: z.string().trim().min(1).optional(),
|
|
156
|
+
visible: z.boolean().optional(),
|
|
157
|
+
fixed: z.boolean().optional(),
|
|
158
|
+
required: z.boolean().optional(),
|
|
159
|
+
keepTogether: z.boolean().optional(),
|
|
160
|
+
selection: selectionSchema.optional(),
|
|
161
|
+
ordering: orderingSchema.optional(),
|
|
162
|
+
preConditions: z.array(rpExpressionSchema).optional(),
|
|
163
|
+
branchRules: z.array(branchRuleSchema).optional(),
|
|
164
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
165
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
166
|
+
} as const;
|
|
167
|
+
|
|
168
|
+
const testPartLevel = {
|
|
169
|
+
preConditions: z.array(rpExpressionSchema).optional(),
|
|
170
|
+
branchRules: z.array(branchRuleSchema).optional(),
|
|
171
|
+
itemSessionControl: itemSessionControlSchema.optional(),
|
|
172
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
173
|
+
} as const;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* The recursive section shape, parameterized by the injected itemRef's inferred type.
|
|
177
|
+
* Optionals carry `| undefined` and arrays are `readonly` so this matches zod's object
|
|
178
|
+
* output exactly — letting the `z.lazy` section schema below be annotated `z.ZodType<…>`
|
|
179
|
+
* (a getter cannot self-infer once a generic itemRef joins the `children` union).
|
|
180
|
+
*/
|
|
181
|
+
export interface AssessmentSectionNode<TItemRef> {
|
|
182
|
+
readonly kind: "assessmentSection";
|
|
183
|
+
readonly identifier: string;
|
|
184
|
+
readonly title?: string | undefined;
|
|
185
|
+
readonly visible?: boolean | undefined;
|
|
186
|
+
readonly fixed?: boolean | undefined;
|
|
187
|
+
readonly required?: boolean | undefined;
|
|
188
|
+
readonly keepTogether?: boolean | undefined;
|
|
189
|
+
readonly selection?: SelectionSchema | undefined;
|
|
190
|
+
readonly ordering?: OrderingSchema | undefined;
|
|
191
|
+
readonly preConditions?: readonly RpExpressionSchema[] | undefined;
|
|
192
|
+
readonly branchRules?: readonly BranchRuleSchema[] | undefined;
|
|
193
|
+
readonly itemSessionControl?: ItemSessionControlSchema | undefined;
|
|
194
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
195
|
+
readonly children: ReadonlyArray<AssessmentSectionNode<TItemRef> | TItemRef>;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** A test part: navigation/submission modes plus the cascade levels and its sections. */
|
|
199
|
+
export interface TestPartNode<TItemRef> {
|
|
200
|
+
readonly identifier: string;
|
|
201
|
+
readonly navigationMode: "linear" | "nonlinear";
|
|
202
|
+
readonly submissionMode: "individual" | "simultaneous";
|
|
203
|
+
readonly preConditions?: readonly RpExpressionSchema[] | undefined;
|
|
204
|
+
readonly branchRules?: readonly BranchRuleSchema[] | undefined;
|
|
205
|
+
readonly itemSessionControl?: ItemSessionControlSchema | undefined;
|
|
206
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
207
|
+
readonly assessmentSections: readonly AssessmentSectionNode<TItemRef>[];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The whole `assessmentTest`: declarations/processing/feedback plus the ordered parts. */
|
|
211
|
+
export interface AssessmentTestNode<TItemRef> {
|
|
212
|
+
readonly identifier: string;
|
|
213
|
+
readonly title?: string | undefined;
|
|
214
|
+
readonly outcomeDeclarations?: readonly OutcomeDeclarationSchema[] | undefined;
|
|
215
|
+
readonly timeLimits?: TimeLimitsSchema | undefined;
|
|
216
|
+
readonly testParts: readonly TestPartNode<TItemRef>[];
|
|
217
|
+
readonly outcomeProcessing?: OutcomeProcessingSchema | undefined;
|
|
218
|
+
readonly testFeedbacks?: readonly TestFeedbackSchema[] | undefined;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** The schema bundle the factory returns (each level parameterized by the itemRef type). */
|
|
222
|
+
export interface AssessmentTestSchemas<TItemRef> {
|
|
223
|
+
readonly assessmentSectionSchema: z.ZodType<AssessmentSectionNode<TItemRef>>;
|
|
224
|
+
readonly testPartSchema: z.ZodType<TestPartNode<TItemRef>>;
|
|
225
|
+
readonly assessmentTestSchema: z.ZodType<AssessmentTestNode<TItemRef>>;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Build the recursive `assessmentTest` schema around a caller-supplied itemRef schema.
|
|
230
|
+
* Sections nest sections-or-itemRefs through `children` (recursion via `z.lazy`); `z.infer`
|
|
231
|
+
* follows the recursion and carries the injected itemRef's inferred type. The explicit
|
|
232
|
+
* return type keeps the package's `isolatedDeclarations` build able to emit the bundle.
|
|
233
|
+
*/
|
|
234
|
+
export function makeAssessmentTestSchema<ItemRef extends z.ZodType>(
|
|
235
|
+
itemRefSchema: ItemRef,
|
|
236
|
+
): AssessmentTestSchemas<z.infer<ItemRef>> {
|
|
237
|
+
const assessmentSectionSchema: z.ZodType<AssessmentSectionNode<z.infer<ItemRef>>> = z.lazy(() =>
|
|
238
|
+
z.object({
|
|
239
|
+
kind: z.literal("assessmentSection"),
|
|
240
|
+
identifier: z.string().trim().min(1),
|
|
241
|
+
...assessmentSectionFlags,
|
|
242
|
+
children: z.array(z.union([assessmentSectionSchema, itemRefSchema])),
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const testPartSchema: z.ZodType<TestPartNode<z.infer<ItemRef>>> = z.object({
|
|
247
|
+
identifier: z.string().trim().min(1),
|
|
248
|
+
navigationMode: z.enum(["linear", "nonlinear"]),
|
|
249
|
+
submissionMode: z.enum(["individual", "simultaneous"]),
|
|
250
|
+
...testPartLevel,
|
|
251
|
+
assessmentSections: z.array(assessmentSectionSchema),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const assessmentTestSchema: z.ZodType<AssessmentTestNode<z.infer<ItemRef>>> = z.object({
|
|
255
|
+
identifier: z.string().trim().min(1),
|
|
256
|
+
title: z.string().trim().min(1).optional(),
|
|
257
|
+
outcomeDeclarations: z.array(outcomeDeclarationSchema).optional(),
|
|
258
|
+
timeLimits: timeLimitsSchema.optional(),
|
|
259
|
+
testParts: z.array(testPartSchema),
|
|
260
|
+
outcomeProcessing: outcomeProcessingSchema.optional(),
|
|
261
|
+
testFeedbacks: z.array(testFeedbackSchema).optional(),
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
return { assessmentSectionSchema, testPartSchema, assessmentTestSchema };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** The ready delivery-view schema: the recursive structure over the `href` itemRef. */
|
|
268
|
+
export const assessmentTestViewSchema: z.ZodType<AssessmentTestNode<AssessmentItemRefViewSchema>> =
|
|
269
|
+
makeAssessmentTestSchema(assessmentItemRefViewSchema).assessmentTestSchema;
|
|
270
|
+
|
|
271
|
+
export type AssessmentItemRefViewSchema = z.infer<typeof assessmentItemRefViewSchema>;
|
|
272
|
+
export type AssessmentTestViewSchema = z.infer<typeof assessmentTestViewSchema>;
|