@conform-ed/contracts 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (187) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-format.log +6 -0
  3. package/.turbo/turbo-lint.log +4 -0
  4. package/.turbo/turbo-test.log +196 -0
  5. package/.turbo/turbo-typecheck.log +2 -0
  6. package/README.md +52 -0
  7. package/caliper-v1_2-zod-templates.md +123 -0
  8. package/case-v1_1-zod-templates.md +290 -0
  9. package/cat-v1_0-zod-templates.md +73 -0
  10. package/cc-v1_3-zod-templates.md +531 -0
  11. package/cc-v1_4-zod-templates.md +247 -0
  12. package/clr-v2_0-zod-templates.md +117 -0
  13. package/cmi5-v1_0-zod-templates.md +9 -0
  14. package/lti-zod-templates.md +11 -0
  15. package/oneroster-v1_2-zod-templates.md +76 -0
  16. package/open-badges-v3_0-zod-templates.md +66 -0
  17. package/package.json +42 -0
  18. package/qti-v2_1-zod-templates.md +45 -0
  19. package/qti-v2_2-zod-templates.md +32 -0
  20. package/qti-v3_0_1-zod-templates.md +421 -0
  21. package/src/adapter.ts +83 -0
  22. package/src/caliper/v1_2/caliper_v1p2_bootcamp_schema.ts +781 -0
  23. package/src/caliper/v1_2/index.ts +37 -0
  24. package/src/caliper/v1_2/shared.ts +122 -0
  25. package/src/caliper/v1_2/textual_requirements.ts +219 -0
  26. package/src/case/v1_1/case_v1p1_cfassociation_jsonschema1.ts +1 -0
  27. package/src/case/v1_1/case_v1p1_cfassociationgrouping_jsonschema1.ts +1 -0
  28. package/src/case/v1_1/case_v1p1_cfassociationset_jsonschema1.ts +1 -0
  29. package/src/case/v1_1/case_v1p1_cfconceptset_jsonschema1.ts +1 -0
  30. package/src/case/v1_1/case_v1p1_cfdocument_jsonschema1.ts +1 -0
  31. package/src/case/v1_1/case_v1p1_cfdocumentset_jsonschema1.ts +1 -0
  32. package/src/case/v1_1/case_v1p1_cfitem_jsonschema1.ts +1 -0
  33. package/src/case/v1_1/case_v1p1_cfitemtypeset_jsonschema1.ts +1 -0
  34. package/src/case/v1_1/case_v1p1_cflicense_jsonschema1.ts +1 -0
  35. package/src/case/v1_1/case_v1p1_cfpackage_jsonschema1.ts +1 -0
  36. package/src/case/v1_1/case_v1p1_cfrubric_jsonschema1.ts +1 -0
  37. package/src/case/v1_1/case_v1p1_cfsubjectset_jsonschema1.ts +1 -0
  38. package/src/case/v1_1/case_v1p1_imsx_statusinfo_jsonschema1.ts +1 -0
  39. package/src/case/v1_1/case_v1p1_openapi3_restbinding_schema.ts +113 -0
  40. package/src/case/v1_1/index.ts +95 -0
  41. package/src/case/v1_1/shared.ts +384 -0
  42. package/src/cat/v1_0/cat_v1p0_restbinding_operations_schema.ts +75 -0
  43. package/src/cat/v1_0/index.ts +124 -0
  44. package/src/cat/v1_0/shared.ts +345 -0
  45. package/src/clr/v2_0/clr_v2p0_achievementcredential_schema.ts +16 -0
  46. package/src/clr/v2_0/clr_v2p0_clrcredential_schema.ts +9 -0
  47. package/src/clr/v2_0/clr_v2p0_endorsementcredential_schema.ts +11 -0
  48. package/src/clr/v2_0/clr_v2p0_getclrcredentialsresponse_schema.ts +1 -0
  49. package/src/clr/v2_0/clr_v2p0_imsx_statusinfo_schema.ts +8 -0
  50. package/src/clr/v2_0/clr_v2p0_profile_schema.ts +9 -0
  51. package/src/clr/v2_0/index.ts +22 -0
  52. package/src/clr/v2_0/shared.ts +206 -0
  53. package/src/cmi5/index.ts +3 -0
  54. package/src/cmi5/v1_0/index.ts +134 -0
  55. package/src/common-cartridge/v1_3/ccv1p3_imsccauth_v1p3.ts +26 -0
  56. package/src/common-cartridge/v1_3/ccv1p3_imscp_v1p2_v1p0.ts +352 -0
  57. package/src/common-cartridge/v1_3/ccv1p3_imscsmd_v1p0.ts +35 -0
  58. package/src/common-cartridge/v1_3/ccv1p3_imsdt_v1p3.ts +33 -0
  59. package/src/common-cartridge/v1_3/ccv1p3_imswl_v1p3.ts +23 -0
  60. package/src/common-cartridge/v1_3/ccv1p3_lomccltilink_v1p0.ts +14 -0
  61. package/src/common-cartridge/v1_3/ccv1p3_lommanifest_v1p0.ts +14 -0
  62. package/src/common-cartridge/v1_3/ccv1p3_lomresource_v1p0.ts +14 -0
  63. package/src/common-cartridge/v1_3/ccv1p3_qtiasiv1p2p1_v1p0.ts +964 -0
  64. package/src/common-cartridge/v1_3/index.ts +41 -0
  65. package/src/common-cartridge/v1_3/lom-internal.ts +396 -0
  66. package/src/common-cartridge/v1_3/shared.ts +68 -0
  67. package/src/common-cartridge/v1_4/core/ccv1p4_imscp_v1p2_v1p0.ts +360 -0
  68. package/src/common-cartridge/v1_4/core/ccv1p4_lommanifest_v1p0.ts +14 -0
  69. package/src/common-cartridge/v1_4/core/ccv1p4_lomresource_v1p0.ts +14 -0
  70. package/src/common-cartridge/v1_4/extension/cc_extresource_assignmentv1p0_v1p0.ts +61 -0
  71. package/src/common-cartridge/v1_4/extension/ccv1p4_cpextensionv1p2_v1p0.ts +20 -0
  72. package/src/common-cartridge/v1_4/extension/ims_openvideov1p0_v1p0.ts +325 -0
  73. package/src/common-cartridge/v1_4/index.ts +104 -0
  74. package/src/common-cartridge/v1_4/k12/ccv1p4_imscp_v1p2_v1p0.ts +19 -0
  75. package/src/common-cartridge/v1_4/k12/ccv1p4_imscp_v1p2_v1p0_thin.ts +17 -0
  76. package/src/common-cartridge/v1_4/k12/ccv1p4_lommanifest_v1p0.ts +14 -0
  77. package/src/common-cartridge/v1_4/k12/ccv1p4_lomresource_v1p0.ts +14 -0
  78. package/src/common-cartridge/v1_4/lom-internal.ts +476 -0
  79. package/src/common-cartridge/v1_4/shared/ccv1p4_imsccauth_v1p4.ts +26 -0
  80. package/src/common-cartridge/v1_4/shared/ccv1p4_imscsmd_v1p1.ts +36 -0
  81. package/src/common-cartridge/v1_4/shared/ccv1p4_imsdt_v1p4.ts +33 -0
  82. package/src/common-cartridge/v1_4/shared/ccv1p4_imslticc_v1p4.ts +45 -0
  83. package/src/common-cartridge/v1_4/shared/ccv1p4_imswl_v1p4.ts +23 -0
  84. package/src/common-cartridge/v1_4/shared/ccv1p4_lomccltilink_v1p0.ts +14 -0
  85. package/src/common-cartridge/v1_4/shared/ccv1p4_qtiasiv1p2p1_v1p0.ts +964 -0
  86. package/src/common-cartridge/v1_4/shared/imsbasiclti_v1p0p1.ts +24 -0
  87. package/src/common-cartridge/v1_4/shared/imslticm_v1p0.ts +23 -0
  88. package/src/common-cartridge/v1_4/shared/imslticp_v1p0.ts +57 -0
  89. package/src/common-cartridge/v1_4/shared/lineitem_v1p0.ts +35 -0
  90. package/src/common-cartridge/v1_4/shared/resourcea11ymetadata-20210915.ts +110 -0
  91. package/src/common-cartridge/v1_4/shared.ts +68 -0
  92. package/src/common-cartridge/v1_4/thin/ccv1p4_imscp_v1p2_v1p0.ts +243 -0
  93. package/src/common-cartridge/v1_4/thin/ccv1p4_lommanifest_v1p0.ts +14 -0
  94. package/src/common-cartridge/v1_4/thin/ccv1p4_lomresource_v1p0.ts +14 -0
  95. package/src/common-cartridge/v1_4/vdex/imsmd_loose_v1p3p2.ts +13 -0
  96. package/src/common-cartridge/v1_4/vdex/imsvdex_v1p0.ts +124 -0
  97. package/src/config.ts +121 -0
  98. package/src/index.ts +32 -0
  99. package/src/lti/ags/v2_0/index.ts +97 -0
  100. package/src/lti/deep-linking/v2_0/index.ts +100 -0
  101. package/src/lti/index.ts +26 -0
  102. package/src/lti/nrps/v2_0/index.ts +69 -0
  103. package/src/lti/proctoring/v1_0/index.ts +131 -0
  104. package/src/lti/shared.ts +66 -0
  105. package/src/lti/v1_3/index.ts +84 -0
  106. package/src/oneroster/v1_2/index.ts +156 -0
  107. package/src/oneroster/v1_2/or_v1p2_csv_binding_schema.ts +427 -0
  108. package/src/oneroster/v1_2/or_v1p2_gradebook_restbinding_schema.ts +120 -0
  109. package/src/oneroster/v1_2/or_v1p2_gradebook_service_schema.ts +243 -0
  110. package/src/oneroster/v1_2/or_v1p2_resource_restbinding_schema.ts +24 -0
  111. package/src/oneroster/v1_2/or_v1p2_resource_service_schema.ts +47 -0
  112. package/src/oneroster/v1_2/or_v1p2_rostering_restbinding_schema.ts +84 -0
  113. package/src/oneroster/v1_2/or_v1p2_rostering_service_schema.ts +288 -0
  114. package/src/oneroster/v1_2/shared.ts +90 -0
  115. package/src/open-badges/v3_0/index.ts +20 -0
  116. package/src/open-badges/v3_0/ob_v3p0_achievementcredential_schema.ts +17 -0
  117. package/src/open-badges/v3_0/ob_v3p0_endorsementcredential_schema.ts +11 -0
  118. package/src/open-badges/v3_0/ob_v3p0_getopenbadgecredentialsresponse_schema.ts +1 -0
  119. package/src/open-badges/v3_0/ob_v3p0_imsx_statusinfo_schema.ts +8 -0
  120. package/src/open-badges/v3_0/ob_v3p0_profile_schema.ts +9 -0
  121. package/src/open-badges/v3_0/shared.ts +458 -0
  122. package/src/qti/v2-internal.ts +1683 -0
  123. package/src/qti/v2_1/apipv1p0_qtiextv2p1_v1p0.ts +3 -0
  124. package/src/qti/v2_1/imsqti_metadata_v2p1.ts +3 -0
  125. package/src/qti/v2_1/imsqti_result_v2p1.ts +3 -0
  126. package/src/qti/v2_1/imsqti_usagedata_v2p1.ts +3 -0
  127. package/src/qti/v2_1/imsqti_v2p1p2.ts +71 -0
  128. package/src/qti/v2_1/index.ts +53 -0
  129. package/src/qti/v2_1/qtiv2p1_imscpv1p2_v1p0.ts +3 -0
  130. package/src/qti/v2_1/schemas.ts +15 -0
  131. package/src/qti/v2_1/shared.ts +3 -0
  132. package/src/qti/v2_2/apipv1p0_qtiextv2p2_v1p0p1.ts +3 -0
  133. package/src/qti/v2_2/imsqti_metadata_v2p2.ts +3 -0
  134. package/src/qti/v2_2/imsqti_result_v2p2.ts +3 -0
  135. package/src/qti/v2_2/imsqti_usagedata_v2p2.ts +3 -0
  136. package/src/qti/v2_2/imsqti_v2p2p4.ts +76 -0
  137. package/src/qti/v2_2/imsqtiv2p2p4_html5_v1p0.ts +8 -0
  138. package/src/qti/v2_2/index.ts +59 -0
  139. package/src/qti/v2_2/qtiv2p2_csm_v2p2.ts +4 -0
  140. package/src/qti/v2_2/qtiv2p2_imscpv1p2_v1p0.ts +3 -0
  141. package/src/qti/v2_2/schemas.ts +19 -0
  142. package/src/qti/v2_2/shared.ts +3 -0
  143. package/src/qti/v3_0_1/assessment-internal.ts +1509 -0
  144. package/src/qti/v3_0_1/imsqti_asiv3p0p1_v1p0.ts +57 -0
  145. package/src/qti/v3_0_1/imsqti_itemv3p0p1_v1p0.ts +60 -0
  146. package/src/qti/v3_0_1/imsqti_metadatav3p0_v1p0.ts +73 -0
  147. package/src/qti/v3_0_1/imsqti_outcomev3p0p1_v1p0.ts +11 -0
  148. package/src/qti/v3_0_1/imsqti_responseprocessingv3p0p1_v1p0.ts +17 -0
  149. package/src/qti/v3_0_1/imsqti_resultv3p0_v1p0.ts +222 -0
  150. package/src/qti/v3_0_1/imsqti_sectionv3p0p1_v1p0.ts +15 -0
  151. package/src/qti/v3_0_1/imsqti_stimulusv3p0p1_v1p0.ts +15 -0
  152. package/src/qti/v3_0_1/imsqti_testv3p0p1_v1p0.ts +49 -0
  153. package/src/qti/v3_0_1/imsqti_usagedatav3p0_v1p0.ts +77 -0
  154. package/src/qti/v3_0_1/imsqtiv3p0_afa3p0pnp_v1p0.ts +269 -0
  155. package/src/qti/v3_0_1/index.ts +50 -0
  156. package/src/qti/v3_0_1/processing-internal.ts +667 -0
  157. package/src/qti/v3_0_1/shared.ts +146 -0
  158. package/src/qti/v3_0_1/variables-internal.ts +274 -0
  159. package/src/shared/imsx-status.ts +49 -0
  160. package/src/summary.ts +70 -0
  161. package/src/vc-data-model/v2_0/index.ts +27 -0
  162. package/src/vc-data-model/v2_0/shared.ts +206 -0
  163. package/src/xapi/index.ts +12 -0
  164. package/src/xapi/shared.ts +444 -0
  165. package/src/xapi/v1_0_3/index.ts +169 -0
  166. package/src/xapi/v2_0/index.ts +256 -0
  167. package/test/caliper-v1_2.test.ts +270 -0
  168. package/test/case-v1_1.test.ts +147 -0
  169. package/test/cat-v1_0.test.ts +338 -0
  170. package/test/cc-v1_4.test.ts +239 -0
  171. package/test/clr-v2_0.test.ts +225 -0
  172. package/test/cmi5-v1_0.test.ts +196 -0
  173. package/test/contracts.test.ts +125 -0
  174. package/test/fixtures/cmi5/extended-cmi5.xml +72 -0
  175. package/test/fixtures/cmi5/simple-cmi5.xml +26 -0
  176. package/test/lti.test.ts +146 -0
  177. package/test/oneroster-v1_2.test.ts +234 -0
  178. package/test/open-badges-v3_0.test.ts +144 -0
  179. package/test/qti-v2_1.test.ts +146 -0
  180. package/test/qti-v2_2.test.ts +154 -0
  181. package/test/qti-v3_0_1.test.ts +576 -0
  182. package/test/schema-examples.test.ts +101 -0
  183. package/test/vc-data-model-v2_0.test.ts +63 -0
  184. package/test/xapi.test.ts +384 -0
  185. package/tsconfig.json +7 -0
  186. package/vc-data-model-v2_0-zod-templates.md +43 -0
  187. package/xapi-zod-templates.md +95 -0
@@ -0,0 +1,1683 @@
1
+ import { z } from "zod";
2
+
3
+ export type Qti2Config = {
4
+ includeAssessmentStimulus: boolean;
5
+ includeExternalSupplementalAccessibility: boolean;
6
+ includeScoringModes: boolean;
7
+ manifestSchemaValues: readonly string[];
8
+ manifestResourceTypes: readonly string[];
9
+ };
10
+
11
+ export const NonEmptyStringSchema = z.string().min(1);
12
+ export const UriReferenceSchema = NonEmptyStringSchema;
13
+ export const QtiIdentifierSchema = z
14
+ .string()
15
+ .min(1)
16
+ .regex(/^[A-Za-z_][A-Za-z0-9._-]*$/u);
17
+ export const QtiBaseTypeSchema = z.enum([
18
+ "boolean",
19
+ "directedPair",
20
+ "duration",
21
+ "file",
22
+ "float",
23
+ "identifier",
24
+ "integer",
25
+ "pair",
26
+ "point",
27
+ "string",
28
+ "uri",
29
+ ]);
30
+ export const QtiCardinalitySchema = z.enum(["single", "multiple", "ordered", "record"]);
31
+ export const QtiViewSchema = z.enum(["author", "candidate", "proctor", "scorer", "testConstructor", "tutor"]);
32
+ export const QtiShowHideSchema = z.enum(["show", "hide"]);
33
+ export const QtiOrientationSchema = z.enum(["horizontal", "vertical"]);
34
+ export const QtiNavigationModeSchema = z.enum(["linear", "nonlinear"]);
35
+ export const QtiSubmissionModeSchema = z.enum(["individual", "simultaneous"]);
36
+ export const QtiSessionStatusSchema = z.enum([
37
+ "final",
38
+ "initial",
39
+ "pendingExternalScoring",
40
+ "pendingResponseProcessing",
41
+ "pendingSubmission",
42
+ ]);
43
+ export const QtiFeedbackTypeSchema = z.enum(["adaptive", "nonadaptive", "none"]);
44
+ export const QtiScoringModeSchema = z.enum(["human", "externalmachine", "responseprocessing"]);
45
+ export const XmlForeignAttributesSchema = z.record(z.string(), z.unknown());
46
+
47
+ export const XmlExtensionNodeSchema = z
48
+ .object({
49
+ namespace: NonEmptyStringSchema,
50
+ name: NonEmptyStringSchema,
51
+ value: z.unknown().optional(),
52
+ attributes: XmlForeignAttributesSchema.optional(),
53
+ children: z.array(z.unknown()).optional(),
54
+ })
55
+ .strict();
56
+
57
+ export const XmlExtensionNodeListSchema = z.array(XmlExtensionNodeSchema);
58
+
59
+ export function strictObject<T extends z.ZodRawShape>(shape: T) {
60
+ return z.object(shape).strict();
61
+ }
62
+
63
+ export function looseObject<T extends z.ZodRawShape>(shape: T) {
64
+ return strictObject({
65
+ ...shape,
66
+ extensions: XmlExtensionNodeListSchema.optional(),
67
+ foreignAttributes: XmlForeignAttributesSchema.optional(),
68
+ });
69
+ }
70
+
71
+ export function addIssue(context: z.RefinementCtx, path: Array<string | number>, message: string) {
72
+ context.addIssue({
73
+ code: z.ZodIssueCode.custom,
74
+ path,
75
+ message,
76
+ });
77
+ }
78
+
79
+ export function collectDuplicates(values: string[]): string[] {
80
+ const seen = new Set<string>();
81
+ const duplicates = new Set<string>();
82
+
83
+ for (const value of values) {
84
+ if (seen.has(value)) {
85
+ duplicates.add(value);
86
+ continue;
87
+ }
88
+
89
+ seen.add(value);
90
+ }
91
+
92
+ return [...duplicates];
93
+ }
94
+
95
+ export function asArray<T>(value: readonly T[] | T[] | undefined | null): T[] {
96
+ return Array.isArray(value) ? [...value] : [];
97
+ }
98
+
99
+ function uniqueIdentifiersRefinement(
100
+ values: Array<{ identifier: string }>,
101
+ path: Array<string | number>,
102
+ label: string,
103
+ context: z.RefinementCtx,
104
+ ) {
105
+ for (const duplicate of collectDuplicates(values.map((value) => value.identifier))) {
106
+ addIssue(context, path, `Duplicate ${label} identifier "${duplicate}"`);
107
+ }
108
+ }
109
+
110
+ function validateDeclaredValues(
111
+ cardinality: z.infer<typeof QtiCardinalitySchema>,
112
+ baseType: z.infer<typeof QtiBaseTypeSchema> | undefined,
113
+ values: Array<{ fieldIdentifier?: string }>,
114
+ path: Array<string | number>,
115
+ context: z.RefinementCtx,
116
+ ) {
117
+ if (cardinality === "record") {
118
+ if (baseType) {
119
+ addIssue(context, path, "Record cardinality variables must not declare baseType");
120
+ }
121
+
122
+ values.forEach((value, index) => {
123
+ if (!value.fieldIdentifier) {
124
+ addIssue(context, [...path, index], "Record cardinality values require fieldIdentifier");
125
+ }
126
+ });
127
+
128
+ return;
129
+ }
130
+
131
+ if (!baseType) {
132
+ addIssue(context, path, "Non-record variables must declare baseType");
133
+ }
134
+ }
135
+
136
+ export function createQti2Schemas(config: Qti2Config) {
137
+ const ManifestSchemaValueSchema = z.string().refine((value) => config.manifestSchemaValues.includes(value), {
138
+ message: `Expected one of: ${config.manifestSchemaValues.join(", ")}`,
139
+ });
140
+
141
+ const ManifestResourceTypeSchema = z.string().refine((value) => config.manifestResourceTypes.includes(value), {
142
+ message: `Expected one of: ${config.manifestResourceTypes.join(", ")}`,
143
+ });
144
+
145
+ const QtiTextNodeSchema = strictObject({
146
+ kind: z.literal("text"),
147
+ value: z.string(),
148
+ });
149
+
150
+ const QtiContentNodeSchema: z.ZodTypeAny = z.lazy(() =>
151
+ z.union([
152
+ QtiTextNodeSchema,
153
+ looseObject({
154
+ kind: NonEmptyStringSchema.refine((value) => value !== "text", {
155
+ message: 'Element node kind must not be "text"',
156
+ }),
157
+ value: z.string().optional(),
158
+ attributes: XmlForeignAttributesSchema.optional(),
159
+ children: z.array(QtiContentNodeSchema).optional(),
160
+ }),
161
+ ]),
162
+ );
163
+
164
+ const QtiStylesheetSchema = strictObject({
165
+ href: UriReferenceSchema,
166
+ type: NonEmptyStringSchema.optional(),
167
+ media: NonEmptyStringSchema.optional(),
168
+ title: NonEmptyStringSchema.optional(),
169
+ });
170
+
171
+ const QtiValueSchema = strictObject({
172
+ value: z.string(),
173
+ fieldIdentifier: QtiIdentifierSchema.optional(),
174
+ baseType: QtiBaseTypeSchema.optional(),
175
+ });
176
+
177
+ const QtiDefaultValueSchema = strictObject({
178
+ values: z.array(QtiValueSchema).min(1),
179
+ });
180
+
181
+ const QtiCorrectResponseSchema = strictObject({
182
+ values: z.array(QtiValueSchema).min(1),
183
+ interpretation: z.string().optional(),
184
+ });
185
+
186
+ const QtiMapEntrySchema = strictObject({
187
+ mapKey: z.string(),
188
+ mappedValue: z.number(),
189
+ caseSensitive: z.boolean().optional(),
190
+ });
191
+
192
+ const QtiMappingSchema = strictObject({
193
+ lowerBound: z.number().optional(),
194
+ upperBound: z.number().optional(),
195
+ defaultValue: z.number().optional(),
196
+ entries: z.array(QtiMapEntrySchema).min(1),
197
+ });
198
+
199
+ const QtiAreaMapEntrySchema = strictObject({
200
+ shape: NonEmptyStringSchema.optional(),
201
+ coords: NonEmptyStringSchema.optional(),
202
+ mappedValue: z.number(),
203
+ caseSensitive: z.boolean().optional(),
204
+ });
205
+
206
+ const QtiAreaMappingSchema = strictObject({
207
+ lowerBound: z.number().optional(),
208
+ upperBound: z.number().optional(),
209
+ defaultValue: z.number().optional(),
210
+ entries: z.array(QtiAreaMapEntrySchema).min(1),
211
+ });
212
+
213
+ const QtiMatchTableEntrySchema = strictObject({
214
+ sourceValue: z.string(),
215
+ targetValue: z.string(),
216
+ });
217
+
218
+ const QtiMatchTableSchema = strictObject({
219
+ defaultValue: z.string().optional(),
220
+ entries: z.array(QtiMatchTableEntrySchema).min(1),
221
+ });
222
+
223
+ const QtiInterpolationTableEntrySchema = strictObject({
224
+ sourceValue: z.number(),
225
+ targetValue: z.string(),
226
+ includeBoundary: z.boolean().optional(),
227
+ });
228
+
229
+ const QtiInterpolationTableSchema = strictObject({
230
+ defaultValue: z.string().optional(),
231
+ entries: z.array(QtiInterpolationTableEntrySchema).min(1),
232
+ });
233
+
234
+ const QtiResponseDeclarationRawSchema = strictObject({
235
+ identifier: QtiIdentifierSchema,
236
+ cardinality: QtiCardinalitySchema,
237
+ baseType: QtiBaseTypeSchema.optional(),
238
+ defaultValue: QtiDefaultValueSchema.optional(),
239
+ correctResponse: QtiCorrectResponseSchema.optional(),
240
+ mapping: QtiMappingSchema.optional(),
241
+ areaMapping: QtiAreaMappingSchema.optional(),
242
+ });
243
+
244
+ const QtiResponseDeclarationSchema = QtiResponseDeclarationRawSchema.superRefine((value, context) => {
245
+ validateDeclaredValues(
246
+ value.cardinality,
247
+ value.baseType,
248
+ value.defaultValue?.values ?? value.correctResponse?.values ?? [],
249
+ [],
250
+ context,
251
+ );
252
+ });
253
+
254
+ const QtiOutcomeDeclarationRawSchema = strictObject({
255
+ identifier: QtiIdentifierSchema,
256
+ cardinality: QtiCardinalitySchema,
257
+ baseType: QtiBaseTypeSchema.optional(),
258
+ defaultValue: QtiDefaultValueSchema.optional(),
259
+ matchTable: QtiMatchTableSchema.optional(),
260
+ interpolationTable: QtiInterpolationTableSchema.optional(),
261
+ view: QtiViewSchema.optional(),
262
+ interpretation: z.string().optional(),
263
+ longInterpretation: UriReferenceSchema.optional(),
264
+ normalMaximum: z.number().optional(),
265
+ normalMinimum: z.number().optional(),
266
+ masteryValue: z.number().optional(),
267
+ externalScored: z.enum(["human", "externalMachine"]).optional(),
268
+ });
269
+
270
+ const QtiOutcomeDeclarationSchema = QtiOutcomeDeclarationRawSchema.superRefine((value, context) => {
271
+ validateDeclaredValues(value.cardinality, value.baseType, value.defaultValue?.values ?? [], [], context);
272
+ });
273
+
274
+ const QtiTemplateDeclarationRawSchema = strictObject({
275
+ identifier: QtiIdentifierSchema,
276
+ cardinality: QtiCardinalitySchema,
277
+ baseType: QtiBaseTypeSchema.optional(),
278
+ defaultValue: QtiDefaultValueSchema.optional(),
279
+ paramVariable: z.boolean().optional(),
280
+ mathVariable: z.boolean().optional(),
281
+ });
282
+
283
+ const QtiTemplateDeclarationSchema = QtiTemplateDeclarationRawSchema.superRefine((value, context) => {
284
+ validateDeclaredValues(value.cardinality, value.baseType, value.defaultValue?.values ?? [], [], context);
285
+ });
286
+
287
+ const QtiPromptSchema = strictObject({
288
+ kind: z.literal("prompt"),
289
+ children: z.array(QtiContentNodeSchema).min(1),
290
+ });
291
+
292
+ const QtiInlineChoiceSchema = strictObject({
293
+ kind: z.literal("inlineChoice"),
294
+ identifier: QtiIdentifierSchema,
295
+ fixed: z.boolean().optional(),
296
+ children: z.array(QtiContentNodeSchema).optional(),
297
+ });
298
+
299
+ const QtiSimpleChoiceSchema = strictObject({
300
+ kind: z.literal("simpleChoice"),
301
+ identifier: QtiIdentifierSchema,
302
+ fixed: z.boolean().optional(),
303
+ showHide: QtiShowHideSchema.optional(),
304
+ children: z.array(QtiContentNodeSchema).optional(),
305
+ });
306
+
307
+ const QtiSimpleAssociableChoiceSchema = strictObject({
308
+ kind: z.literal("simpleAssociableChoice"),
309
+ identifier: QtiIdentifierSchema,
310
+ matchMax: z.number().int().optional(),
311
+ matchGroup: z.array(NonEmptyStringSchema).optional(),
312
+ children: z.array(QtiContentNodeSchema).optional(),
313
+ });
314
+
315
+ const QtiHotspotChoiceSchema = strictObject({
316
+ kind: z.literal("hotspotChoice"),
317
+ identifier: QtiIdentifierSchema,
318
+ shape: NonEmptyStringSchema.optional(),
319
+ coords: NonEmptyStringSchema.optional(),
320
+ fixed: z.boolean().optional(),
321
+ children: z.array(QtiContentNodeSchema).optional(),
322
+ });
323
+
324
+ const QtiAssociableHotspotSchema = strictObject({
325
+ kind: z.literal("associableHotspot"),
326
+ identifier: QtiIdentifierSchema,
327
+ shape: NonEmptyStringSchema.optional(),
328
+ coords: NonEmptyStringSchema.optional(),
329
+ matchMax: z.number().int().optional(),
330
+ });
331
+
332
+ const QtiGapTextSchema = strictObject({
333
+ kind: z.literal("gapText"),
334
+ identifier: QtiIdentifierSchema,
335
+ matchMax: z.number().int().optional(),
336
+ children: z.array(QtiContentNodeSchema).optional(),
337
+ });
338
+
339
+ const QtiGapImgSchema = strictObject({
340
+ kind: z.literal("gapImg"),
341
+ identifier: QtiIdentifierSchema,
342
+ matchMax: z.number().int().optional(),
343
+ src: UriReferenceSchema.optional(),
344
+ alt: z.string().optional(),
345
+ });
346
+
347
+ const QtiGapSchema = strictObject({
348
+ kind: z.literal("gap"),
349
+ identifier: QtiIdentifierSchema,
350
+ matchMax: z.number().int().optional(),
351
+ });
352
+
353
+ const QtiSimpleMatchSetSchema = strictObject({
354
+ kind: z.literal("simpleMatchSet"),
355
+ choices: z.array(z.union([QtiSimpleAssociableChoiceSchema, QtiSimpleChoiceSchema])).min(1),
356
+ });
357
+
358
+ const QtiInteractionBaseShape = {
359
+ responseIdentifier: QtiIdentifierSchema.optional(),
360
+ title: z.string().optional(),
361
+ label: z.string().optional(),
362
+ maxChoices: z.number().int().optional(),
363
+ minChoices: z.number().int().optional(),
364
+ maxStrings: z.number().int().optional(),
365
+ minStrings: z.number().int().optional(),
366
+ maxAssociations: z.number().int().optional(),
367
+ minAssociations: z.number().int().optional(),
368
+ maxPlays: z.number().int().optional(),
369
+ minAssociationsPerChoice: z.number().int().optional(),
370
+ shuffle: z.boolean().optional(),
371
+ fixed: z.boolean().optional(),
372
+ orientation: QtiOrientationSchema.optional(),
373
+ expectedLength: z.number().int().optional(),
374
+ patternMask: z.string().optional(),
375
+ placeholderText: z.string().optional(),
376
+ step: z.number().optional(),
377
+ lowerBound: z.number().optional(),
378
+ upperBound: z.number().optional(),
379
+ attrs: XmlForeignAttributesSchema.optional(),
380
+ prompt: QtiPromptSchema.optional(),
381
+ } satisfies z.ZodRawShape;
382
+
383
+ const QtiChoiceInteractionSchema = strictObject({
384
+ kind: z.literal("choiceInteraction"),
385
+ ...QtiInteractionBaseShape,
386
+ simpleChoices: z.array(QtiSimpleChoiceSchema).min(1),
387
+ }).superRefine((value, context) => {
388
+ uniqueIdentifiersRefinement(value.simpleChoices, ["simpleChoices"], "simple choice", context);
389
+ });
390
+
391
+ const QtiOrderInteractionSchema = strictObject({
392
+ kind: z.literal("orderInteraction"),
393
+ ...QtiInteractionBaseShape,
394
+ simpleChoices: z.array(QtiSimpleChoiceSchema).min(1),
395
+ }).superRefine((value, context) => {
396
+ uniqueIdentifiersRefinement(value.simpleChoices, ["simpleChoices"], "simple choice", context);
397
+ });
398
+
399
+ const QtiAssociateInteractionSchema = strictObject({
400
+ kind: z.literal("associateInteraction"),
401
+ ...QtiInteractionBaseShape,
402
+ simpleAssociableChoices: z.array(QtiSimpleAssociableChoiceSchema).min(2),
403
+ }).superRefine((value, context) => {
404
+ uniqueIdentifiersRefinement(
405
+ value.simpleAssociableChoices,
406
+ ["simpleAssociableChoices"],
407
+ "associable choice",
408
+ context,
409
+ );
410
+ });
411
+
412
+ const QtiMatchInteractionSchema = strictObject({
413
+ kind: z.literal("matchInteraction"),
414
+ ...QtiInteractionBaseShape,
415
+ simpleMatchSets: z.array(QtiSimpleMatchSetSchema).min(2),
416
+ });
417
+
418
+ const QtiInlineChoiceInteractionSchema = strictObject({
419
+ kind: z.literal("inlineChoiceInteraction"),
420
+ ...QtiInteractionBaseShape,
421
+ responseIdentifier: QtiIdentifierSchema,
422
+ inlineChoices: z.array(QtiInlineChoiceSchema).min(1),
423
+ }).superRefine((value, context) => {
424
+ uniqueIdentifiersRefinement(value.inlineChoices, ["inlineChoices"], "inline choice", context);
425
+ });
426
+
427
+ const QtiTextEntryInteractionSchema = strictObject({
428
+ kind: z.literal("textEntryInteraction"),
429
+ ...QtiInteractionBaseShape,
430
+ responseIdentifier: QtiIdentifierSchema,
431
+ });
432
+
433
+ const QtiExtendedTextInteractionSchema = strictObject({
434
+ kind: z.literal("extendedTextInteraction"),
435
+ ...QtiInteractionBaseShape,
436
+ responseIdentifier: QtiIdentifierSchema,
437
+ expectedLines: z.number().int().optional(),
438
+ });
439
+
440
+ const QtiUploadInteractionSchema = strictObject({
441
+ kind: z.literal("uploadInteraction"),
442
+ ...QtiInteractionBaseShape,
443
+ responseIdentifier: QtiIdentifierSchema,
444
+ type: z.string().optional(),
445
+ });
446
+
447
+ const QtiHottextInteractionSchema = strictObject({
448
+ kind: z.literal("hottextInteraction"),
449
+ ...QtiInteractionBaseShape,
450
+ responseIdentifier: QtiIdentifierSchema,
451
+ hottexts: z
452
+ .array(
453
+ strictObject({
454
+ kind: z.literal("hottext"),
455
+ identifier: QtiIdentifierSchema,
456
+ fixed: z.boolean().optional(),
457
+ children: z.array(QtiContentNodeSchema).optional(),
458
+ }),
459
+ )
460
+ .min(1),
461
+ }).superRefine((value, context) => {
462
+ uniqueIdentifiersRefinement(value.hottexts, ["hottexts"], "hottext", context);
463
+ });
464
+
465
+ const QtiGapMatchInteractionSchema = strictObject({
466
+ kind: z.literal("gapMatchInteraction"),
467
+ ...QtiInteractionBaseShape,
468
+ responseIdentifier: QtiIdentifierSchema,
469
+ gapTexts: z.array(QtiGapTextSchema).optional(),
470
+ gapImgs: z.array(QtiGapImgSchema).optional(),
471
+ gaps: z.array(QtiGapSchema).optional(),
472
+ });
473
+
474
+ const QtiGraphicGapMatchInteractionSchema = strictObject({
475
+ kind: z.literal("graphicGapMatchInteraction"),
476
+ ...QtiInteractionBaseShape,
477
+ responseIdentifier: QtiIdentifierSchema,
478
+ gapTexts: z.array(QtiGapTextSchema).optional(),
479
+ gapImgs: z.array(QtiGapImgSchema).optional(),
480
+ hotspots: z.array(QtiGapSchema).optional(),
481
+ object: QtiContentNodeSchema.optional(),
482
+ });
483
+
484
+ const QtiHotspotInteractionSchema = strictObject({
485
+ kind: z.literal("hotspotInteraction"),
486
+ ...QtiInteractionBaseShape,
487
+ responseIdentifier: QtiIdentifierSchema,
488
+ hotspotChoices: z.array(QtiHotspotChoiceSchema).min(1),
489
+ }).superRefine((value, context) => {
490
+ uniqueIdentifiersRefinement(value.hotspotChoices, ["hotspotChoices"], "hotspot choice", context);
491
+ });
492
+
493
+ const QtiGraphicOrderInteractionSchema = strictObject({
494
+ kind: z.literal("graphicOrderInteraction"),
495
+ ...QtiInteractionBaseShape,
496
+ responseIdentifier: QtiIdentifierSchema,
497
+ hotspotChoices: z.array(QtiHotspotChoiceSchema).min(1),
498
+ }).superRefine((value, context) => {
499
+ uniqueIdentifiersRefinement(value.hotspotChoices, ["hotspotChoices"], "hotspot choice", context);
500
+ });
501
+
502
+ const QtiGraphicAssociateInteractionSchema = strictObject({
503
+ kind: z.literal("graphicAssociateInteraction"),
504
+ ...QtiInteractionBaseShape,
505
+ responseIdentifier: QtiIdentifierSchema,
506
+ associableHotspots: z.array(QtiAssociableHotspotSchema).min(2),
507
+ }).superRefine((value, context) => {
508
+ uniqueIdentifiersRefinement(value.associableHotspots, ["associableHotspots"], "associable hotspot", context);
509
+ });
510
+
511
+ const QtiSelectPointInteractionSchema = strictObject({
512
+ kind: z.literal("selectPointInteraction"),
513
+ ...QtiInteractionBaseShape,
514
+ responseIdentifier: QtiIdentifierSchema,
515
+ });
516
+
517
+ const QtiPositionObjectInteractionSchema = strictObject({
518
+ kind: z.literal("positionObjectInteraction"),
519
+ ...QtiInteractionBaseShape,
520
+ responseIdentifier: QtiIdentifierSchema,
521
+ object: QtiContentNodeSchema.optional(),
522
+ });
523
+
524
+ const QtiSliderInteractionSchema = strictObject({
525
+ kind: z.literal("sliderInteraction"),
526
+ ...QtiInteractionBaseShape,
527
+ responseIdentifier: QtiIdentifierSchema,
528
+ stepLabel: z.boolean().optional(),
529
+ });
530
+
531
+ const QtiMediaInteractionSchema = strictObject({
532
+ kind: z.literal("mediaInteraction"),
533
+ ...QtiInteractionBaseShape,
534
+ responseIdentifier: QtiIdentifierSchema.optional(),
535
+ autostart: z.boolean().optional(),
536
+ loop: z.boolean().optional(),
537
+ object: QtiContentNodeSchema.optional(),
538
+ });
539
+
540
+ const QtiCustomInteractionSchema = looseObject({
541
+ kind: z.literal("customInteraction"),
542
+ responseIdentifier: QtiIdentifierSchema.optional(),
543
+ prompt: QtiPromptSchema.optional(),
544
+ children: z.array(QtiContentNodeSchema).optional(),
545
+ });
546
+
547
+ const QtiDrawingInteractionSchema = looseObject({
548
+ kind: z.literal("drawingInteraction"),
549
+ responseIdentifier: QtiIdentifierSchema.optional(),
550
+ prompt: QtiPromptSchema.optional(),
551
+ children: z.array(QtiContentNodeSchema).optional(),
552
+ });
553
+
554
+ const QtiEndAttemptInteractionSchema = strictObject({
555
+ kind: z.literal("endAttemptInteraction"),
556
+ title: z.string().optional(),
557
+ children: z.array(QtiContentNodeSchema).optional(),
558
+ });
559
+
560
+ const QtiInteractionSchema = z.union([
561
+ QtiChoiceInteractionSchema,
562
+ QtiOrderInteractionSchema,
563
+ QtiAssociateInteractionSchema,
564
+ QtiMatchInteractionSchema,
565
+ QtiInlineChoiceInteractionSchema,
566
+ QtiTextEntryInteractionSchema,
567
+ QtiExtendedTextInteractionSchema,
568
+ QtiUploadInteractionSchema,
569
+ QtiHottextInteractionSchema,
570
+ QtiGapMatchInteractionSchema,
571
+ QtiGraphicGapMatchInteractionSchema,
572
+ QtiHotspotInteractionSchema,
573
+ QtiGraphicOrderInteractionSchema,
574
+ QtiGraphicAssociateInteractionSchema,
575
+ QtiSelectPointInteractionSchema,
576
+ QtiPositionObjectInteractionSchema,
577
+ QtiSliderInteractionSchema,
578
+ QtiMediaInteractionSchema,
579
+ QtiCustomInteractionSchema,
580
+ QtiDrawingInteractionSchema,
581
+ QtiEndAttemptInteractionSchema,
582
+ ]);
583
+
584
+ const QtiRubricBlockSchema = strictObject({
585
+ kind: z.literal("rubricBlock"),
586
+ view: z.array(QtiViewSchema).optional(),
587
+ use: z.string().optional(),
588
+ children: z.array(QtiContentNodeSchema).min(1),
589
+ });
590
+
591
+ const QtiStimulusBodySchema = strictObject({
592
+ children: z.array(QtiContentNodeSchema).min(1),
593
+ });
594
+
595
+ const QtiItemBodySchema = strictObject({
596
+ children: z.array(z.union([QtiRubricBlockSchema, QtiInteractionSchema, QtiContentNodeSchema])).min(1),
597
+ });
598
+
599
+ const QtiApipFileHrefSchema = strictObject({
600
+ href: UriReferenceSchema,
601
+ });
602
+
603
+ const QtiApipContentLinkInfoSchema = looseObject({
604
+ identifier: QtiIdentifierSchema.optional(),
605
+ qtiLinkIdentifier: QtiIdentifierSchema.optional(),
606
+ qtiHtmlTagIdentifier: QtiIdentifierSchema.optional(),
607
+ fileHrefs: z.array(QtiApipFileHrefSchema).optional(),
608
+ objectLabel: z.string().optional(),
609
+ });
610
+
611
+ const QtiApipRelatedElementInfoSchema = looseObject({
612
+ identifierRef: QtiIdentifierSchema.optional(),
613
+ qtiClass: NonEmptyStringSchema.optional(),
614
+ title: z.string().optional(),
615
+ });
616
+
617
+ const QtiApipAccessElementSchema = looseObject({
618
+ identifier: QtiIdentifierSchema,
619
+ contentLinkInfos: z.array(QtiApipContentLinkInfoSchema).min(1),
620
+ relatedElementInfo: QtiApipRelatedElementInfoSchema,
621
+ });
622
+
623
+ const QtiApipAccessibilityInfoSchema = strictObject({
624
+ accessElements: z.array(QtiApipAccessElementSchema).min(1),
625
+ });
626
+
627
+ const QtiApipCompanionMaterialsInfoSchema = looseObject({
628
+ fileHrefs: z.array(QtiApipFileHrefSchema).optional(),
629
+ description: z.string().optional(),
630
+ });
631
+
632
+ const QtiApipInclusionOrderSchema = looseObject({
633
+ identifiers: z.array(QtiIdentifierSchema).optional(),
634
+ });
635
+
636
+ const QtiApipExternalSupplementalAccessibilitySchema = looseObject({
637
+ fileHrefs: z.array(QtiApipFileHrefSchema).optional(),
638
+ description: z.string().optional(),
639
+ });
640
+
641
+ const QtiApipAccessibilitySchema = looseObject({
642
+ companionMaterialsInfo: QtiApipCompanionMaterialsInfoSchema.optional(),
643
+ ...(config.includeExternalSupplementalAccessibility
644
+ ? { externalSupplementalAccessibility: QtiApipExternalSupplementalAccessibilitySchema.optional() }
645
+ : {}),
646
+ inclusionOrder: QtiApipInclusionOrderSchema.optional(),
647
+ accessibilityInfo: QtiApipAccessibilityInfoSchema.optional(),
648
+ });
649
+
650
+ const QtiApipAccessibilityDocumentSchema = strictObject({
651
+ apipAccessibility: QtiApipAccessibilitySchema,
652
+ });
653
+
654
+ const QtiModalFeedbackSchema = strictObject({
655
+ kind: z.literal("modalFeedback"),
656
+ identifier: QtiIdentifierSchema.optional(),
657
+ outcomeIdentifier: QtiIdentifierSchema.optional(),
658
+ showHide: QtiShowHideSchema.optional(),
659
+ title: z.string().optional(),
660
+ stylesheets: z.array(QtiStylesheetSchema).optional(),
661
+ children: z.array(QtiContentNodeSchema).min(1),
662
+ apipAccessibility: QtiApipAccessibilitySchema.optional(),
663
+ });
664
+
665
+ const QtiTestFeedbackSchema = strictObject({
666
+ kind: z.literal("testFeedback"),
667
+ identifier: QtiIdentifierSchema.optional(),
668
+ outcomeIdentifier: QtiIdentifierSchema.optional(),
669
+ access: z.string().optional(),
670
+ showHide: QtiShowHideSchema.optional(),
671
+ title: z.string().optional(),
672
+ children: z.array(QtiContentNodeSchema).min(1),
673
+ });
674
+
675
+ const QtiIncludeSchema = strictObject({
676
+ kind: z.literal("include"),
677
+ href: UriReferenceSchema,
678
+ xpointer: z.string().optional(),
679
+ });
680
+
681
+ const QtiVariableSchema = strictObject({
682
+ kind: z.literal("variable"),
683
+ identifier: QtiIdentifierSchema,
684
+ weightIdentifier: QtiIdentifierSchema.optional(),
685
+ });
686
+
687
+ const QtiBaseValueExpressionSchema = strictObject({
688
+ kind: z.literal("baseValue"),
689
+ baseType: QtiBaseTypeSchema,
690
+ value: z.string(),
691
+ });
692
+
693
+ const QtiNullExpressionSchema = strictObject({
694
+ kind: z.literal("null"),
695
+ });
696
+
697
+ const QtiCorrectExpressionSchema = strictObject({
698
+ kind: z.literal("correct"),
699
+ identifier: QtiIdentifierSchema.optional(),
700
+ });
701
+
702
+ const QtiDefaultExpressionSchema = strictObject({
703
+ kind: z.literal("default"),
704
+ identifier: QtiIdentifierSchema.optional(),
705
+ });
706
+
707
+ const QtiFieldValueExpressionSchema = strictObject({
708
+ kind: z.literal("fieldValue"),
709
+ identifier: QtiIdentifierSchema.optional(),
710
+ fieldIdentifier: QtiIdentifierSchema,
711
+ });
712
+
713
+ const QtiRandomIntegerExpressionSchema = strictObject({
714
+ kind: z.literal("randomInteger"),
715
+ min: z.number().int(),
716
+ max: z.number().int(),
717
+ step: z.number().int().optional(),
718
+ });
719
+
720
+ const QtiRandomFloatExpressionSchema = strictObject({
721
+ kind: z.literal("randomFloat"),
722
+ min: z.number(),
723
+ max: z.number(),
724
+ });
725
+
726
+ const QtiTestVariablesExpressionSchema = strictObject({
727
+ kind: z.literal("testVariables"),
728
+ variableIdentifier: QtiIdentifierSchema,
729
+ weightIdentifier: QtiIdentifierSchema.optional(),
730
+ baseType: QtiBaseTypeSchema.optional(),
731
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
732
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
733
+ });
734
+
735
+ const QtiOutcomeAggregateExpressionSchema = z.union([
736
+ strictObject({
737
+ kind: z.literal("outcomeMinimum"),
738
+ outcomeIdentifier: QtiIdentifierSchema,
739
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
740
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
741
+ }),
742
+ strictObject({
743
+ kind: z.literal("outcomeMaximum"),
744
+ outcomeIdentifier: QtiIdentifierSchema,
745
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
746
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
747
+ }),
748
+ ]);
749
+
750
+ const QtiCountExpressionSchema = z.union([
751
+ strictObject({
752
+ kind: z.literal("numberCorrect"),
753
+ sectionIdentifier: QtiIdentifierSchema.optional(),
754
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
755
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
756
+ }),
757
+ strictObject({
758
+ kind: z.literal("numberIncorrect"),
759
+ sectionIdentifier: QtiIdentifierSchema.optional(),
760
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
761
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
762
+ }),
763
+ strictObject({
764
+ kind: z.literal("numberPresented"),
765
+ sectionIdentifier: QtiIdentifierSchema.optional(),
766
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
767
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
768
+ }),
769
+ strictObject({
770
+ kind: z.literal("numberResponded"),
771
+ sectionIdentifier: QtiIdentifierSchema.optional(),
772
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
773
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
774
+ }),
775
+ strictObject({
776
+ kind: z.literal("numberSelected"),
777
+ sectionIdentifier: QtiIdentifierSchema.optional(),
778
+ includeCategory: z.array(NonEmptyStringSchema).optional(),
779
+ excludeCategory: z.array(NonEmptyStringSchema).optional(),
780
+ }),
781
+ ]);
782
+
783
+ const unaryKinds = [
784
+ "not",
785
+ "random",
786
+ "containerSize",
787
+ "isNull",
788
+ "integerToFloat",
789
+ "round",
790
+ "truncate",
791
+ "mapResponse",
792
+ "mapResponsePoint",
793
+ ] as const;
794
+ const binaryKinds = [
795
+ "gt",
796
+ "lt",
797
+ "gte",
798
+ "lte",
799
+ "durationLT",
800
+ "durationGTE",
801
+ "subtract",
802
+ "divide",
803
+ "match",
804
+ "equal",
805
+ "contains",
806
+ "delete",
807
+ "power",
808
+ "member",
809
+ "integerDivide",
810
+ "integerModulus",
811
+ "inside",
812
+ "patternMatch",
813
+ "stringMatch",
814
+ ] as const;
815
+ const manyKinds = ["and", "or", "sum", "product", "multiple", "ordered", "gcd", "lcm", "max", "min"] as const;
816
+
817
+ const QtiExpressionSchema: z.ZodTypeAny = z.lazy(() =>
818
+ z
819
+ .union([
820
+ QtiBaseValueExpressionSchema,
821
+ QtiNullExpressionSchema,
822
+ QtiVariableSchema,
823
+ QtiCorrectExpressionSchema,
824
+ QtiDefaultExpressionSchema,
825
+ QtiFieldValueExpressionSchema,
826
+ QtiRandomIntegerExpressionSchema,
827
+ QtiRandomFloatExpressionSchema,
828
+ QtiTestVariablesExpressionSchema,
829
+ QtiOutcomeAggregateExpressionSchema,
830
+ QtiCountExpressionSchema,
831
+ ...unaryKinds.map((kind) =>
832
+ strictObject({
833
+ kind: z.literal(kind),
834
+ children: z.array(QtiExpressionSchema).length(1),
835
+ }),
836
+ ),
837
+ ...binaryKinds.map((kind) =>
838
+ strictObject({
839
+ kind: z.literal(kind),
840
+ children: z.array(QtiExpressionSchema).length(2),
841
+ }),
842
+ ),
843
+ ...manyKinds.map((kind) =>
844
+ strictObject({
845
+ kind: z.literal(kind),
846
+ children: z.array(QtiExpressionSchema).min(1),
847
+ }),
848
+ ),
849
+ strictObject({
850
+ kind: z.literal("substring"),
851
+ caseSensitive: z.boolean().optional(),
852
+ children: z.array(QtiExpressionSchema).length(2),
853
+ }),
854
+ strictObject({
855
+ kind: z.literal("equalRounded"),
856
+ roundingMode: z.enum(["decimalPlaces", "significantFigures"]).optional(),
857
+ figures: z.number().int(),
858
+ children: z.array(QtiExpressionSchema).length(2),
859
+ }),
860
+ strictObject({
861
+ kind: z.literal("index"),
862
+ n: z.number().int(),
863
+ children: z.array(QtiExpressionSchema).length(1),
864
+ }),
865
+ strictObject({
866
+ kind: z.literal("repeat"),
867
+ numberRepeats: z.number().int(),
868
+ children: z.array(QtiExpressionSchema).length(1),
869
+ }),
870
+ strictObject({
871
+ kind: z.literal("roundTo"),
872
+ roundingMode: z.enum(["decimalPlaces", "significantFigures"]),
873
+ figures: z.number().int(),
874
+ children: z.array(QtiExpressionSchema).length(1),
875
+ }),
876
+ strictObject({
877
+ kind: z.literal("anyN"),
878
+ min: z.number().int().optional(),
879
+ max: z.number().int().optional(),
880
+ children: z.array(QtiExpressionSchema).min(1),
881
+ }),
882
+ strictObject({
883
+ kind: z.literal("statsOperator"),
884
+ name: NonEmptyStringSchema,
885
+ children: z.array(QtiExpressionSchema).min(1),
886
+ }),
887
+ strictObject({
888
+ kind: z.literal("mathOperator"),
889
+ name: NonEmptyStringSchema,
890
+ children: z.array(QtiExpressionSchema).min(1),
891
+ }),
892
+ strictObject({
893
+ kind: z.literal("customOperator"),
894
+ class: NonEmptyStringSchema.optional(),
895
+ definition: UriReferenceSchema.optional(),
896
+ children: z.array(QtiExpressionSchema).optional(),
897
+ }),
898
+ ])
899
+ .superRefine((value, context) => {
900
+ if (value.kind === "randomInteger" && value.min > value.max) {
901
+ addIssue(context, ["min"], "randomInteger min must be less than or equal to max");
902
+ }
903
+
904
+ if (value.kind === "randomFloat" && value.min > value.max) {
905
+ addIssue(context, ["min"], "randomFloat min must be less than or equal to max");
906
+ }
907
+
908
+ if (value.kind === "anyN") {
909
+ if (value.min !== undefined && value.min < 0) {
910
+ addIssue(context, ["min"], "anyN min must be non-negative");
911
+ }
912
+
913
+ if (value.max !== undefined && value.max < 0) {
914
+ addIssue(context, ["max"], "anyN max must be non-negative");
915
+ }
916
+
917
+ if (value.min !== undefined && value.max !== undefined && value.min > value.max) {
918
+ addIssue(context, ["min"], "anyN min must be less than or equal to max");
919
+ }
920
+ }
921
+
922
+ if (value.kind === "index" && value.n < 1) {
923
+ addIssue(context, ["n"], "index n must be greater than 0");
924
+ }
925
+
926
+ if (value.kind === "repeat" && value.numberRepeats < 1) {
927
+ addIssue(context, ["numberRepeats"], "repeat numberRepeats must be greater than 0");
928
+ }
929
+
930
+ if (value.kind === "roundTo" && value.figures < 1) {
931
+ addIssue(context, ["figures"], "roundTo figures must be greater than 0");
932
+ }
933
+
934
+ if (value.kind === "equalRounded" && value.figures < 1) {
935
+ addIssue(context, ["figures"], "equalRounded figures must be greater than 0");
936
+ }
937
+ }),
938
+ );
939
+
940
+ const QtiSetOutcomeValueSchema = strictObject({
941
+ kind: z.literal("setOutcomeValue"),
942
+ identifier: QtiIdentifierSchema,
943
+ expression: QtiExpressionSchema,
944
+ });
945
+
946
+ const QtiSetTemplateValueSchema = strictObject({
947
+ kind: z.literal("setTemplateValue"),
948
+ identifier: QtiIdentifierSchema,
949
+ expression: QtiExpressionSchema,
950
+ });
951
+
952
+ const QtiSetDefaultValueRuleSchema = strictObject({
953
+ kind: z.literal("setDefaultValue"),
954
+ identifier: QtiIdentifierSchema,
955
+ values: z.array(QtiValueSchema).min(1),
956
+ });
957
+
958
+ const QtiSetCorrectResponseRuleSchema = strictObject({
959
+ kind: z.literal("setCorrectResponse"),
960
+ identifier: QtiIdentifierSchema,
961
+ values: z.array(QtiValueSchema).min(1),
962
+ });
963
+
964
+ const QtiLookupOutcomeValueRuleSchema = looseObject({
965
+ kind: z.literal("lookupOutcomeValue"),
966
+ identifier: QtiIdentifierSchema.optional(),
967
+ outcomeIdentifier: QtiIdentifierSchema.optional(),
968
+ expression: QtiExpressionSchema.optional(),
969
+ });
970
+
971
+ const QtiTemplateConstraintSchema = strictObject({
972
+ kind: z.literal("templateConstraint"),
973
+ expression: QtiExpressionSchema,
974
+ });
975
+
976
+ const QtiExitResponseRuleSchema = strictObject({
977
+ kind: z.literal("exitResponse"),
978
+ });
979
+
980
+ const QtiExitTemplateRuleSchema = strictObject({
981
+ kind: z.literal("exitTemplate"),
982
+ });
983
+
984
+ const QtiExitTestRuleSchema = strictObject({
985
+ kind: z.literal("exitTest"),
986
+ });
987
+
988
+ const QtiProcessingFragmentSchema = z.union([
989
+ strictObject({
990
+ kind: z.literal("responseProcessingFragment"),
991
+ href: UriReferenceSchema.optional(),
992
+ }),
993
+ strictObject({
994
+ kind: z.literal("outcomeProcessingFragment"),
995
+ href: UriReferenceSchema.optional(),
996
+ }),
997
+ ]);
998
+
999
+ const QtiResponseProcessingRuleSchema: z.ZodTypeAny = z.lazy(() =>
1000
+ z.union([
1001
+ QtiSetOutcomeValueSchema,
1002
+ QtiLookupOutcomeValueRuleSchema,
1003
+ QtiExitResponseRuleSchema,
1004
+ strictObject({
1005
+ kind: z.literal("responseCondition"),
1006
+ ifBranch: strictObject({
1007
+ expression: QtiExpressionSchema,
1008
+ rules: z.array(QtiResponseProcessingRuleSchema).min(1),
1009
+ }),
1010
+ elseIfBranches: z
1011
+ .array(
1012
+ strictObject({
1013
+ expression: QtiExpressionSchema,
1014
+ rules: z.array(QtiResponseProcessingRuleSchema).min(1),
1015
+ }),
1016
+ )
1017
+ .optional(),
1018
+ elseRules: z.array(QtiResponseProcessingRuleSchema).optional(),
1019
+ }),
1020
+ strictObject({
1021
+ kind: z.literal("responseElse"),
1022
+ rules: z.array(QtiResponseProcessingRuleSchema).min(1),
1023
+ }),
1024
+ QtiProcessingFragmentSchema,
1025
+ QtiIncludeSchema,
1026
+ ]),
1027
+ );
1028
+
1029
+ const QtiOutcomeProcessingRuleSchema: z.ZodTypeAny = z.lazy(() =>
1030
+ z.union([
1031
+ QtiSetOutcomeValueSchema,
1032
+ QtiLookupOutcomeValueRuleSchema,
1033
+ QtiExitTestRuleSchema,
1034
+ strictObject({
1035
+ kind: z.literal("outcomeCondition"),
1036
+ ifBranch: strictObject({
1037
+ expression: QtiExpressionSchema,
1038
+ rules: z.array(QtiOutcomeProcessingRuleSchema).min(1),
1039
+ }),
1040
+ elseIfBranches: z
1041
+ .array(
1042
+ strictObject({
1043
+ expression: QtiExpressionSchema,
1044
+ rules: z.array(QtiOutcomeProcessingRuleSchema).min(1),
1045
+ }),
1046
+ )
1047
+ .optional(),
1048
+ elseRules: z.array(QtiOutcomeProcessingRuleSchema).optional(),
1049
+ }),
1050
+ strictObject({
1051
+ kind: z.literal("outcomeElse"),
1052
+ rules: z.array(QtiOutcomeProcessingRuleSchema).min(1),
1053
+ }),
1054
+ QtiProcessingFragmentSchema,
1055
+ QtiIncludeSchema,
1056
+ ]),
1057
+ );
1058
+
1059
+ const QtiTemplateProcessingRuleSchema: z.ZodTypeAny = z.lazy(() =>
1060
+ z.union([
1061
+ QtiSetTemplateValueSchema,
1062
+ QtiSetDefaultValueRuleSchema,
1063
+ QtiSetCorrectResponseRuleSchema,
1064
+ QtiTemplateConstraintSchema,
1065
+ QtiExitTemplateRuleSchema,
1066
+ strictObject({
1067
+ kind: z.literal("templateCondition"),
1068
+ ifBranch: strictObject({
1069
+ expression: QtiExpressionSchema,
1070
+ rules: z.array(QtiTemplateProcessingRuleSchema).min(1),
1071
+ }),
1072
+ elseIfBranches: z
1073
+ .array(
1074
+ strictObject({
1075
+ expression: QtiExpressionSchema,
1076
+ rules: z.array(QtiTemplateProcessingRuleSchema).min(1),
1077
+ }),
1078
+ )
1079
+ .optional(),
1080
+ elseRules: z.array(QtiTemplateProcessingRuleSchema).optional(),
1081
+ }),
1082
+ strictObject({
1083
+ kind: z.literal("templateElse"),
1084
+ rules: z.array(QtiTemplateProcessingRuleSchema).min(1),
1085
+ }),
1086
+ QtiIncludeSchema,
1087
+ ]),
1088
+ );
1089
+
1090
+ const QtiResponseProcessingSchema = strictObject({
1091
+ rules: z.array(QtiResponseProcessingRuleSchema).default([]),
1092
+ });
1093
+
1094
+ const QtiOutcomeProcessingSchema = strictObject({
1095
+ rules: z.array(QtiOutcomeProcessingRuleSchema).default([]),
1096
+ });
1097
+
1098
+ const QtiTemplateProcessingSchema = strictObject({
1099
+ rules: z.array(QtiTemplateProcessingRuleSchema).min(1),
1100
+ });
1101
+
1102
+ const QtiPreConditionSchema = strictObject({
1103
+ expression: QtiExpressionSchema,
1104
+ });
1105
+
1106
+ const QtiBranchRuleSchema = strictObject({
1107
+ target: QtiIdentifierSchema.optional(),
1108
+ expression: QtiExpressionSchema,
1109
+ });
1110
+
1111
+ const QtiSelectionSchema = looseObject({
1112
+ select: z.number().int().optional(),
1113
+ withReplacement: z.boolean().optional(),
1114
+ }).superRefine((value, context) => {
1115
+ if (value.select !== undefined && value.select < 1) {
1116
+ addIssue(context, ["select"], "selection select must be greater than 0");
1117
+ }
1118
+ });
1119
+
1120
+ const QtiOrderingSchema = looseObject({
1121
+ shuffle: z.boolean().optional(),
1122
+ });
1123
+
1124
+ const QtiTimeLimitsSchema = strictObject({
1125
+ minTime: z.string().optional(),
1126
+ maxTime: z.string().optional(),
1127
+ allowLateSubmission: z.boolean().optional(),
1128
+ });
1129
+
1130
+ const QtiItemSessionControlSchema = strictObject({
1131
+ maxAttempts: z.number().int().optional(),
1132
+ showFeedback: z.boolean().optional(),
1133
+ allowReview: z.boolean().optional(),
1134
+ allowComment: z.boolean().optional(),
1135
+ validateResponses: z.boolean().optional(),
1136
+ showSolution: z.boolean().optional(),
1137
+ allowSkipping: z.boolean().optional(),
1138
+ }).superRefine((value, context) => {
1139
+ if (value.maxAttempts !== undefined && value.maxAttempts < 1) {
1140
+ addIssue(context, ["maxAttempts"], "itemSessionControl maxAttempts must be greater than 0");
1141
+ }
1142
+ });
1143
+
1144
+ const QtiTemplateDefaultSchema = strictObject({
1145
+ identifier: QtiIdentifierSchema,
1146
+ values: z.array(QtiValueSchema).min(1),
1147
+ });
1148
+
1149
+ const QtiWeightSchema = strictObject({
1150
+ identifier: QtiIdentifierSchema,
1151
+ value: z.number(),
1152
+ });
1153
+
1154
+ const QtiVariableMappingSchema = looseObject({
1155
+ identifier: QtiIdentifierSchema.optional(),
1156
+ sourceIdentifier: QtiIdentifierSchema.optional(),
1157
+ targetIdentifier: QtiIdentifierSchema.optional(),
1158
+ values: z.array(QtiValueSchema).optional(),
1159
+ });
1160
+
1161
+ const QtiAssessmentSectionRefSchema = strictObject({
1162
+ identifier: QtiIdentifierSchema,
1163
+ href: UriReferenceSchema,
1164
+ required: z.boolean().optional(),
1165
+ fixed: z.boolean().optional(),
1166
+ visible: z.boolean().optional(),
1167
+ category: z.array(NonEmptyStringSchema).optional(),
1168
+ keepTogether: z.boolean().optional(),
1169
+ });
1170
+
1171
+ const QtiAssessmentItemRefSchema = strictObject({
1172
+ identifier: QtiIdentifierSchema,
1173
+ href: UriReferenceSchema,
1174
+ required: z.boolean().optional(),
1175
+ fixed: z.boolean().optional(),
1176
+ visible: z.boolean().optional(),
1177
+ category: z.array(NonEmptyStringSchema).optional(),
1178
+ preConditions: z.array(QtiPreConditionSchema).optional(),
1179
+ branchRules: z.array(QtiBranchRuleSchema).optional(),
1180
+ itemSessionControl: QtiItemSessionControlSchema.optional(),
1181
+ timeLimits: QtiTimeLimitsSchema.optional(),
1182
+ variableMappings: z.array(QtiVariableMappingSchema).optional(),
1183
+ weights: z.array(QtiWeightSchema).optional(),
1184
+ templateDefaults: z.array(QtiTemplateDefaultSchema).optional(),
1185
+ });
1186
+
1187
+ const QtiAssessmentStimulusRefSchema = strictObject({
1188
+ identifier: QtiIdentifierSchema.optional(),
1189
+ href: UriReferenceSchema,
1190
+ required: z.boolean().optional(),
1191
+ fixed: z.boolean().optional(),
1192
+ category: z.array(NonEmptyStringSchema).optional(),
1193
+ });
1194
+
1195
+ const QtiAssessmentSectionRawSchema: z.ZodTypeAny = z.lazy(() =>
1196
+ strictObject({
1197
+ identifier: QtiIdentifierSchema,
1198
+ title: z.string().optional(),
1199
+ visible: z.boolean().optional(),
1200
+ required: z.boolean().optional(),
1201
+ fixed: z.boolean().optional(),
1202
+ keepTogether: z.boolean().optional(),
1203
+ preConditions: z.array(QtiPreConditionSchema).optional(),
1204
+ branchRules: z.array(QtiBranchRuleSchema).optional(),
1205
+ itemSessionControl: QtiItemSessionControlSchema.optional(),
1206
+ timeLimits: QtiTimeLimitsSchema.optional(),
1207
+ selection: QtiSelectionSchema.optional(),
1208
+ ordering: QtiOrderingSchema.optional(),
1209
+ rubricBlocks: z.array(QtiRubricBlockSchema).optional(),
1210
+ children: z
1211
+ .array(
1212
+ z.union([
1213
+ QtiIncludeSchema,
1214
+ QtiAssessmentItemRefSchema,
1215
+ QtiAssessmentSectionRawSchema,
1216
+ QtiAssessmentSectionRefSchema,
1217
+ ]),
1218
+ )
1219
+ .default([]),
1220
+ }),
1221
+ );
1222
+
1223
+ const QtiAssessmentSectionSchema: z.ZodTypeAny = QtiAssessmentSectionRawSchema.superRefine((input, context) => {
1224
+ const value = input as { children?: unknown[] };
1225
+ const identifierValues = asArray(value.children).flatMap((child) => {
1226
+ if (!child || typeof child !== "object" || !("identifier" in child)) {
1227
+ return [];
1228
+ }
1229
+
1230
+ const identifier = child.identifier;
1231
+ return typeof identifier === "string" && identifier.length > 0 ? [identifier] : [];
1232
+ });
1233
+
1234
+ for (const duplicate of collectDuplicates(identifierValues)) {
1235
+ addIssue(context, ["children"], `Duplicate assessment section child identifier "${duplicate}"`);
1236
+ }
1237
+ });
1238
+
1239
+ const QtiTestPartSchema = strictObject({
1240
+ identifier: QtiIdentifierSchema,
1241
+ navigationMode: QtiNavigationModeSchema,
1242
+ submissionMode: QtiSubmissionModeSchema,
1243
+ preConditions: z.array(QtiPreConditionSchema).optional(),
1244
+ branchRules: z.array(QtiBranchRuleSchema).optional(),
1245
+ itemSessionControl: QtiItemSessionControlSchema.optional(),
1246
+ timeLimits: QtiTimeLimitsSchema.optional(),
1247
+ children: z.array(z.union([QtiAssessmentSectionSchema, QtiAssessmentSectionRefSchema])).min(1),
1248
+ testFeedbacks: z.array(QtiTestFeedbackSchema).optional(),
1249
+ });
1250
+
1251
+ const QtiAssessmentItemRawSchema = strictObject({
1252
+ identifier: QtiIdentifierSchema,
1253
+ title: z.string().optional(),
1254
+ label: z.string().optional(),
1255
+ adaptive: z.boolean().optional(),
1256
+ timeDependent: z.boolean().optional(),
1257
+ toolName: z.string().optional(),
1258
+ toolVersion: z.string().optional(),
1259
+ responseDeclarations: z.array(QtiResponseDeclarationSchema).default([]),
1260
+ outcomeDeclarations: z.array(QtiOutcomeDeclarationSchema).default([]),
1261
+ templateDeclarations: z.array(QtiTemplateDeclarationSchema).default([]),
1262
+ templateProcessing: QtiTemplateProcessingSchema.optional(),
1263
+ ...(config.includeAssessmentStimulus
1264
+ ? { assessmentStimulusRefs: z.array(QtiAssessmentStimulusRefSchema).optional() }
1265
+ : {}),
1266
+ stylesheets: z.array(QtiStylesheetSchema).optional(),
1267
+ itemBody: QtiItemBodySchema.optional(),
1268
+ responseProcessing: QtiResponseProcessingSchema.optional(),
1269
+ modalFeedbacks: z.array(QtiModalFeedbackSchema).optional(),
1270
+ apipAccessibility: QtiApipAccessibilitySchema.optional(),
1271
+ });
1272
+
1273
+ const QtiAssessmentItemSchema = QtiAssessmentItemRawSchema.superRefine((value, context) => {
1274
+ uniqueIdentifiersRefinement(value.responseDeclarations, ["responseDeclarations"], "response declaration", context);
1275
+ uniqueIdentifiersRefinement(value.outcomeDeclarations, ["outcomeDeclarations"], "outcome declaration", context);
1276
+ uniqueIdentifiersRefinement(value.templateDeclarations, ["templateDeclarations"], "template declaration", context);
1277
+ });
1278
+
1279
+ const QtiAssessmentStimulusSchema: z.ZodTypeAny = config.includeAssessmentStimulus
1280
+ ? strictObject({
1281
+ identifier: QtiIdentifierSchema,
1282
+ title: z.string().optional(),
1283
+ stylesheets: z.array(QtiStylesheetSchema).optional(),
1284
+ stimulusBody: QtiStimulusBodySchema,
1285
+ apipAccessibility: QtiApipAccessibilitySchema.optional(),
1286
+ })
1287
+ : z.never();
1288
+
1289
+ const QtiAssessmentTestRawSchema = strictObject({
1290
+ identifier: QtiIdentifierSchema,
1291
+ title: z.string().optional(),
1292
+ toolName: z.string().optional(),
1293
+ toolVersion: z.string().optional(),
1294
+ outcomeDeclarations: z.array(QtiOutcomeDeclarationSchema).default([]),
1295
+ timeLimits: QtiTimeLimitsSchema.optional(),
1296
+ stylesheets: z.array(QtiStylesheetSchema).optional(),
1297
+ testParts: z.array(QtiTestPartSchema).min(1),
1298
+ outcomeProcessing: QtiOutcomeProcessingSchema.optional(),
1299
+ testFeedbacks: z.array(QtiTestFeedbackSchema).optional(),
1300
+ });
1301
+
1302
+ const QtiAssessmentTestSchema = QtiAssessmentTestRawSchema.superRefine((value, context) => {
1303
+ uniqueIdentifiersRefinement(value.outcomeDeclarations, ["outcomeDeclarations"], "outcome declaration", context);
1304
+ uniqueIdentifiersRefinement(value.testParts, ["testParts"], "test part", context);
1305
+ });
1306
+
1307
+ const QtiMetadataSchema = strictObject({
1308
+ itemTemplate: z.boolean().optional(),
1309
+ timeDependent: z.boolean().optional(),
1310
+ composite: z.boolean().optional(),
1311
+ interactionType: z.array(NonEmptyStringSchema).optional(),
1312
+ feedbackType: QtiFeedbackTypeSchema.optional(),
1313
+ solutionAvailable: z.boolean().optional(),
1314
+ ...(config.includeScoringModes ? { scoringMode: z.array(QtiScoringModeSchema).optional() } : {}),
1315
+ toolName: z.string().max(256).optional(),
1316
+ toolVersion: z.string().max(256).optional(),
1317
+ toolVendor: z.string().max(256).optional(),
1318
+ });
1319
+
1320
+ const QtiMetadataDocumentSchema = strictObject({
1321
+ qtiMetadata: QtiMetadataSchema,
1322
+ });
1323
+
1324
+ const QtiCurriculumStandardsMetadataSetSchema = strictObject({
1325
+ resourceLabel: z.string().optional(),
1326
+ resourcePartId: z.string().optional(),
1327
+ curriculumStandardsMetadata: z
1328
+ .array(
1329
+ strictObject({
1330
+ providerId: z.string().optional(),
1331
+ setOfGuids: z
1332
+ .array(
1333
+ strictObject({
1334
+ region: z.string().optional(),
1335
+ version: z.string().optional(),
1336
+ labelledGuids: z
1337
+ .array(
1338
+ strictObject({
1339
+ label: z.string().optional(),
1340
+ guid: z.string(),
1341
+ }),
1342
+ )
1343
+ .min(1),
1344
+ }),
1345
+ )
1346
+ .min(1),
1347
+ }),
1348
+ )
1349
+ .min(1),
1350
+ });
1351
+
1352
+ const QtiCurriculumStandardsMetadataSetDocumentSchema = strictObject({
1353
+ curriculumStandardsMetadataSet: QtiCurriculumStandardsMetadataSetSchema,
1354
+ });
1355
+
1356
+ const QtiSessionIdentifierSchema = strictObject({
1357
+ sourceId: UriReferenceSchema,
1358
+ identifier: NonEmptyStringSchema,
1359
+ });
1360
+
1361
+ const QtiAssessmentResultContextSchema = strictObject({
1362
+ sourcedId: QtiIdentifierSchema.optional(),
1363
+ sessionIdentifiers: z.array(QtiSessionIdentifierSchema).optional(),
1364
+ });
1365
+
1366
+ const QtiCandidateResponseSchema = strictObject({
1367
+ values: z.array(QtiValueSchema).default([]),
1368
+ });
1369
+
1370
+ const QtiResultResponseVariableRawSchema = strictObject({
1371
+ identifier: QtiIdentifierSchema,
1372
+ cardinality: QtiCardinalitySchema,
1373
+ baseType: QtiBaseTypeSchema.optional(),
1374
+ correctResponse: QtiCorrectResponseSchema.optional(),
1375
+ candidateResponse: QtiCandidateResponseSchema,
1376
+ choiceSequence: z.array(NonEmptyStringSchema).optional(),
1377
+ });
1378
+
1379
+ const QtiResultResponseVariableSchema = QtiResultResponseVariableRawSchema.superRefine((value, context) => {
1380
+ validateDeclaredValues(
1381
+ value.cardinality,
1382
+ value.baseType,
1383
+ value.candidateResponse.values,
1384
+ ["candidateResponse", "values"],
1385
+ context,
1386
+ );
1387
+ });
1388
+
1389
+ const QtiResultTemplateVariableRawSchema = strictObject({
1390
+ identifier: QtiIdentifierSchema,
1391
+ cardinality: QtiCardinalitySchema,
1392
+ baseType: QtiBaseTypeSchema.optional(),
1393
+ values: z.array(QtiValueSchema).default([]),
1394
+ });
1395
+
1396
+ const QtiResultTemplateVariableSchema = QtiResultTemplateVariableRawSchema.superRefine((value, context) => {
1397
+ validateDeclaredValues(value.cardinality, value.baseType, value.values, ["values"], context);
1398
+ });
1399
+
1400
+ const QtiResultOutcomeVariableRawSchema = strictObject({
1401
+ identifier: QtiIdentifierSchema,
1402
+ cardinality: QtiCardinalitySchema,
1403
+ baseType: QtiBaseTypeSchema.optional(),
1404
+ values: z.array(QtiValueSchema).default([]),
1405
+ view: QtiViewSchema.optional(),
1406
+ interpretation: z.string().optional(),
1407
+ longInterpretation: UriReferenceSchema.optional(),
1408
+ normalMaximum: z.number().optional(),
1409
+ normalMinimum: z.number().optional(),
1410
+ masteryValue: z.number().optional(),
1411
+ });
1412
+
1413
+ const QtiResultOutcomeVariableSchema = QtiResultOutcomeVariableRawSchema.superRefine((value, context) => {
1414
+ validateDeclaredValues(value.cardinality, value.baseType, value.values, ["values"], context);
1415
+ });
1416
+
1417
+ const QtiTestResultSchema = strictObject({
1418
+ identifier: NonEmptyStringSchema,
1419
+ datestamp: z.string(),
1420
+ responseVariables: z.array(QtiResultResponseVariableSchema).optional(),
1421
+ templateVariables: z.array(QtiResultTemplateVariableSchema).optional(),
1422
+ outcomeVariables: z.array(QtiResultOutcomeVariableSchema).optional(),
1423
+ });
1424
+
1425
+ const QtiItemResultSchema = strictObject({
1426
+ identifier: NonEmptyStringSchema,
1427
+ sequenceIndex: z.number().int().optional(),
1428
+ datestamp: z.string(),
1429
+ sessionStatus: QtiSessionStatusSchema,
1430
+ responseVariables: z.array(QtiResultResponseVariableSchema).optional(),
1431
+ templateVariables: z.array(QtiResultTemplateVariableSchema).optional(),
1432
+ outcomeVariables: z.array(QtiResultOutcomeVariableSchema).optional(),
1433
+ candidateComment: z.string().optional(),
1434
+ });
1435
+
1436
+ const QtiAssessmentResultSchema = strictObject({
1437
+ context: QtiAssessmentResultContextSchema,
1438
+ testResult: QtiTestResultSchema.optional(),
1439
+ itemResults: z.array(QtiItemResultSchema).optional(),
1440
+ });
1441
+
1442
+ const QtiAssessmentResultDocumentSchema = strictObject({
1443
+ assessmentResult: QtiAssessmentResultSchema,
1444
+ });
1445
+
1446
+ const QtiUsageTargetObjectSchema = strictObject({
1447
+ identifier: NonEmptyStringSchema,
1448
+ partIdentifier: QtiIdentifierSchema.optional(),
1449
+ });
1450
+
1451
+ const QtiUsageMappingSchema = strictObject({
1452
+ lowerBound: z.number().optional(),
1453
+ upperBound: z.number().optional(),
1454
+ defaultValue: z.number().optional(),
1455
+ entries: z.array(QtiMapEntrySchema).min(1),
1456
+ });
1457
+
1458
+ const QtiOrdinaryStatisticSchema = strictObject({
1459
+ kind: z.literal("ordinaryStatistic"),
1460
+ name: QtiIdentifierSchema,
1461
+ glossary: UriReferenceSchema.optional(),
1462
+ context: UriReferenceSchema,
1463
+ caseCount: z.number().int().optional(),
1464
+ stdError: z.number().optional(),
1465
+ stdDeviation: z.number().optional(),
1466
+ lastUpdated: z.string().optional(),
1467
+ targetObjects: z.array(QtiUsageTargetObjectSchema).min(1),
1468
+ value: QtiValueSchema,
1469
+ });
1470
+
1471
+ const QtiCategorizedStatisticSchema = strictObject({
1472
+ kind: z.literal("categorizedStatistic"),
1473
+ name: QtiIdentifierSchema,
1474
+ glossary: UriReferenceSchema.optional(),
1475
+ context: UriReferenceSchema,
1476
+ caseCount: z.number().int().optional(),
1477
+ stdError: z.number().optional(),
1478
+ stdDeviation: z.number().optional(),
1479
+ lastUpdated: z.string().optional(),
1480
+ targetObjects: z.array(QtiUsageTargetObjectSchema).min(1),
1481
+ mapping: QtiUsageMappingSchema,
1482
+ });
1483
+
1484
+ const QtiUsageDataSchema = strictObject({
1485
+ glossary: UriReferenceSchema.optional(),
1486
+ statistics: z.array(z.union([QtiOrdinaryStatisticSchema, QtiCategorizedStatisticSchema])).default([]),
1487
+ });
1488
+
1489
+ const QtiUsageDataDocumentSchema = strictObject({
1490
+ usageData: QtiUsageDataSchema,
1491
+ });
1492
+
1493
+ const QtiManifestMetadataSchema = strictObject({
1494
+ schema: ManifestSchemaValueSchema,
1495
+ schemaVersion: z.literal("1.0.0"),
1496
+ qtiMetadata: QtiMetadataSchema.optional(),
1497
+ curriculumStandardsMetadataSet: config.includeAssessmentStimulus
1498
+ ? QtiCurriculumStandardsMetadataSetSchema.optional()
1499
+ : z.undefined().optional(),
1500
+ lom: z.unknown().optional(),
1501
+ });
1502
+
1503
+ const QtiManifestResourceMetadataSchema = strictObject({
1504
+ qtiMetadata: QtiMetadataSchema.optional(),
1505
+ curriculumStandardsMetadataSet: config.includeAssessmentStimulus
1506
+ ? QtiCurriculumStandardsMetadataSetSchema.optional()
1507
+ : z.undefined().optional(),
1508
+ lom: z.unknown().optional(),
1509
+ });
1510
+
1511
+ const QtiManifestFileSchema = strictObject({
1512
+ href: UriReferenceSchema,
1513
+ metadata: QtiManifestResourceMetadataSchema.optional(),
1514
+ });
1515
+
1516
+ const QtiManifestDependencySchema = strictObject({
1517
+ identifierRef: NonEmptyStringSchema,
1518
+ });
1519
+
1520
+ const QtiManifestResourceSchema = strictObject({
1521
+ identifier: NonEmptyStringSchema,
1522
+ type: ManifestResourceTypeSchema,
1523
+ href: UriReferenceSchema.optional(),
1524
+ metadata: QtiManifestResourceMetadataSchema.optional(),
1525
+ files: z.array(QtiManifestFileSchema).optional(),
1526
+ dependencies: z.array(QtiManifestDependencySchema).optional(),
1527
+ });
1528
+
1529
+ const QtiManifestSchema = strictObject({
1530
+ identifier: NonEmptyStringSchema,
1531
+ metadata: QtiManifestMetadataSchema,
1532
+ organizations: strictObject({}),
1533
+ resources: z.array(QtiManifestResourceSchema).default([]),
1534
+ }).superRefine((value, context) => {
1535
+ uniqueIdentifiersRefinement(value.resources, ["resources"], "manifest resource", context);
1536
+ });
1537
+
1538
+ const QtiManifestDocumentSchema = strictObject({
1539
+ manifest: QtiManifestSchema,
1540
+ });
1541
+
1542
+ const QtiAssessmentItemDocumentSchema = strictObject({
1543
+ assessmentItem: QtiAssessmentItemSchema,
1544
+ });
1545
+
1546
+ const QtiAssessmentSectionDocumentSchema = strictObject({
1547
+ assessmentSection: QtiAssessmentSectionSchema,
1548
+ });
1549
+
1550
+ const QtiAssessmentStimulusDocumentSchema: z.ZodTypeAny = config.includeAssessmentStimulus
1551
+ ? strictObject({
1552
+ assessmentStimulus: QtiAssessmentStimulusSchema,
1553
+ })
1554
+ : z.never();
1555
+
1556
+ const QtiAssessmentTestDocumentSchema = strictObject({
1557
+ assessmentTest: QtiAssessmentTestSchema,
1558
+ });
1559
+
1560
+ const QtiResponseDeclarationDocumentSchema = strictObject({
1561
+ responseDeclaration: QtiResponseDeclarationSchema,
1562
+ });
1563
+
1564
+ const QtiOutcomeDeclarationDocumentSchema = strictObject({
1565
+ outcomeDeclaration: QtiOutcomeDeclarationSchema,
1566
+ });
1567
+
1568
+ const QtiTemplateDeclarationDocumentSchema = strictObject({
1569
+ templateDeclaration: QtiTemplateDeclarationSchema,
1570
+ });
1571
+
1572
+ const QtiResponseProcessingDocumentSchema = strictObject({
1573
+ responseProcessing: QtiResponseProcessingSchema,
1574
+ });
1575
+
1576
+ const QtiOutcomeProcessingDocumentSchema = strictObject({
1577
+ outcomeProcessing: QtiOutcomeProcessingSchema,
1578
+ });
1579
+
1580
+ const QtiTemplateProcessingDocumentSchema = strictObject({
1581
+ templateProcessing: QtiTemplateProcessingSchema,
1582
+ });
1583
+
1584
+ const QtiItemBodyDocumentSchema = strictObject({
1585
+ itemBody: QtiItemBodySchema,
1586
+ });
1587
+
1588
+ const QtiModalFeedbackDocumentSchema = strictObject({
1589
+ modalFeedback: QtiModalFeedbackSchema,
1590
+ });
1591
+
1592
+ return {
1593
+ XmlExtensionNodeSchema,
1594
+ XmlExtensionNodeListSchema,
1595
+ QtiContentNodeSchema,
1596
+ QtiStylesheetSchema,
1597
+ QtiValueSchema,
1598
+ QtiMetadataSchema,
1599
+ QtiMetadataDocumentSchema,
1600
+ QtiCurriculumStandardsMetadataSetSchema,
1601
+ QtiCurriculumStandardsMetadataSetDocumentSchema,
1602
+ QtiApipAccessibilitySchema,
1603
+ QtiApipAccessibilityDocumentSchema,
1604
+ QtiResponseDeclarationRawSchema,
1605
+ QtiResponseDeclarationSchema,
1606
+ QtiOutcomeDeclarationRawSchema,
1607
+ QtiOutcomeDeclarationSchema,
1608
+ QtiTemplateDeclarationRawSchema,
1609
+ QtiTemplateDeclarationSchema,
1610
+ QtiExpressionSchema,
1611
+ QtiResponseProcessingSchema,
1612
+ QtiOutcomeProcessingSchema,
1613
+ QtiTemplateProcessingSchema,
1614
+ QtiBranchRuleSchema,
1615
+ QtiSelectionSchema,
1616
+ QtiOrderingSchema,
1617
+ QtiTimeLimitsSchema,
1618
+ QtiItemSessionControlSchema,
1619
+ QtiAssessmentItemRefSchema,
1620
+ QtiAssessmentSectionRefSchema,
1621
+ QtiAssessmentSectionRawSchema,
1622
+ QtiAssessmentSectionSchema,
1623
+ QtiAssessmentStimulusRefSchema,
1624
+ QtiTestPartSchema,
1625
+ QtiAssessmentItemRawSchema,
1626
+ QtiAssessmentItemSchema,
1627
+ QtiAssessmentStimulusSchema,
1628
+ QtiAssessmentTestRawSchema,
1629
+ QtiAssessmentTestSchema,
1630
+ QtiItemBodySchema,
1631
+ QtiStimulusBodySchema,
1632
+ QtiRubricBlockSchema,
1633
+ QtiModalFeedbackSchema,
1634
+ QtiTestFeedbackSchema,
1635
+ QtiChoiceInteractionSchema,
1636
+ QtiOrderInteractionSchema,
1637
+ QtiAssociateInteractionSchema,
1638
+ QtiMatchInteractionSchema,
1639
+ QtiInlineChoiceInteractionSchema,
1640
+ QtiTextEntryInteractionSchema,
1641
+ QtiExtendedTextInteractionSchema,
1642
+ QtiUploadInteractionSchema,
1643
+ QtiHottextInteractionSchema,
1644
+ QtiGapMatchInteractionSchema,
1645
+ QtiHotspotInteractionSchema,
1646
+ QtiMediaInteractionSchema,
1647
+ QtiManifestSchema,
1648
+ QtiManifestDocumentSchema,
1649
+ QtiAssessmentResultSchema,
1650
+ QtiAssessmentResultDocumentSchema,
1651
+ QtiUsageDataSchema,
1652
+ QtiUsageDataDocumentSchema,
1653
+ QtiAssessmentItemDocumentSchema,
1654
+ QtiAssessmentSectionDocumentSchema,
1655
+ QtiAssessmentStimulusDocumentSchema,
1656
+ QtiAssessmentTestDocumentSchema,
1657
+ QtiResponseDeclarationDocumentSchema,
1658
+ QtiOutcomeDeclarationDocumentSchema,
1659
+ QtiTemplateDeclarationDocumentSchema,
1660
+ QtiResponseProcessingDocumentSchema,
1661
+ QtiOutcomeProcessingDocumentSchema,
1662
+ QtiTemplateProcessingDocumentSchema,
1663
+ QtiItemBodyDocumentSchema,
1664
+ QtiModalFeedbackDocumentSchema,
1665
+ } as const;
1666
+ }
1667
+ // Inferred types from exported Zod validators.
1668
+ export type NonEmptyString = z.infer<typeof NonEmptyStringSchema>;
1669
+ export type UriReference = z.infer<typeof UriReferenceSchema>;
1670
+ export type QtiIdentifier = z.infer<typeof QtiIdentifierSchema>;
1671
+ export type QtiBaseType = z.infer<typeof QtiBaseTypeSchema>;
1672
+ export type QtiCardinality = z.infer<typeof QtiCardinalitySchema>;
1673
+ export type QtiView = z.infer<typeof QtiViewSchema>;
1674
+ export type QtiShowHide = z.infer<typeof QtiShowHideSchema>;
1675
+ export type QtiOrientation = z.infer<typeof QtiOrientationSchema>;
1676
+ export type QtiNavigationMode = z.infer<typeof QtiNavigationModeSchema>;
1677
+ export type QtiSubmissionMode = z.infer<typeof QtiSubmissionModeSchema>;
1678
+ export type QtiSessionStatus = z.infer<typeof QtiSessionStatusSchema>;
1679
+ export type QtiFeedbackType = z.infer<typeof QtiFeedbackTypeSchema>;
1680
+ export type QtiScoringMode = z.infer<typeof QtiScoringModeSchema>;
1681
+ export type XmlForeignAttributes = z.infer<typeof XmlForeignAttributesSchema>;
1682
+ export type XmlExtensionNode = z.infer<typeof XmlExtensionNodeSchema>;
1683
+ export type XmlExtensionNodeList = z.infer<typeof XmlExtensionNodeListSchema>;