@conform-ed/qti-react 0.0.19 → 0.0.20

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.
@@ -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.19",
3
+ "version": "0.0.20",
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.19",
34
- "@conform-ed/qti-xml": "0.0.19",
33
+ "@conform-ed/contracts": "0.0.20",
34
+ "@conform-ed/qti-xml": "0.0.20",
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";
@@ -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>;