@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,531 @@
1
+ # Common Cartridge XSD bundle (including embedded QTI profiles) → Zod template notes
2
+
3
+ ## Published specification
4
+
5
+ - Overview: `https://www.imsglobal.org/cc/ccv1p3/imscc_Overview-v1p3.html`
6
+
7
+ This note accompanies the split source under `packages/contracts/src/common-cartridge/v1_3/` and the version barrel `packages/contracts/src/common-cartridge/v1_3/index.ts`.
8
+
9
+ The TypeScript templates are now split primarily by source XSD file. This document explains the design decisions, the tradeoffs behind them, and the places where the XSDs are not sufficient on their own because the real constraints live in embedded Schematron rules or in profile prose.
10
+
11
+ ---
12
+
13
+ ## 1. What was analyzed
14
+
15
+ The templates are based on these local XSD files under `tmp/cc`:
16
+
17
+ - `ccv1p3_imscp_v1p2_v1p0.xsd`
18
+ - `ccv1p3_imscsmd_v1p0.xsd`
19
+ - `ccv1p3_imsdt_v1p3.xsd`
20
+ - `ccv1p3_imswl_v1p3.xsd`
21
+ - `ccv1p3_lomccltilink_v1p0.xsd`
22
+ - `ccv1p3_lommanifest_v1p0.xsd`
23
+ - `ccv1p3_lomresource_v1p0.xsd`
24
+ - `ccv1p3_qtiasiv1p2p1_v1p0.xsd`
25
+ - `ccv1p3_imsccauth_v1p3.xsd`
26
+
27
+ This directory is best understood as a **Common Cartridge schema bundle**, not a separate QTI directory. One of the files (`ccv1p3_qtiasiv1p2p1_v1p0.xsd`) is the embedded Common Cartridge profile of QTI 1.2.1, but it belongs to this Common Cartridge family of schemas.
28
+
29
+ The Common Cartridge manifest XSD imports the authorization schema `ccv1p3_imsccauth_v1p3.xsd`, which is now modeled directly. That lets the templates represent both the optional manifest-level `authorizations` element and the imported resource-level `protected` boolean attribute.
30
+
31
+ Implementation layout:
32
+
33
+ - one TypeScript module per source XSD under `src/common-cartridge/v1_3/`
34
+ - a small number of internal support modules for shared helpers and LOM profile generation
35
+ - a version barrel at `src/common-cartridge/v1_3/index.ts`
36
+
37
+ ---
38
+
39
+ ## 2. The biggest design decision: raw XML fidelity vs useful TypeScript shapes
40
+
41
+ ### Problem
42
+
43
+ XSD is fundamentally an XML grammar. Zod validates JavaScript values. There is no single “correct” way to project XML/XSD into JS objects.
44
+
45
+ Some examples from these schemas:
46
+
47
+ - XML attributes like `xml:lang`, `xml:base`, `linkrefid`, `feedbacktype`
48
+ - simple-content elements that carry both text and attributes
49
+ - embedded QTI-profile nodes whose meaning depends on **ordered heterogeneous children**
50
+ - LOM nodes whose XSD uses repeated unordered `xs:choice`, with the real cardinality enforced in embedded Schematron
51
+
52
+ If the goal were exact round-tripping, the right model would look like a generic XML tree.
53
+ If the goal were practical validation in TypeScript, the right model is a normalized object model.
54
+
55
+ ### Decision
56
+
57
+ The templates deliberately target a **normalized parsed-XML representation**:
58
+
59
+ - element text is represented as `value`
60
+ - attributes are flattened to JS properties
61
+ - imported namespace prefixes are normalized away when context already disambiguates them
62
+ - embedded QTI-profile ordered children are preserved with `children: Node[]` unions where necessary
63
+ - LOM is normalized into plain object properties and arrays instead of preserving arbitrary sibling order
64
+
65
+ ### Rationale
66
+
67
+ This choice makes the schemas usable.
68
+
69
+ A perfectly XML-faithful model would technically be more complete, but it would also be so generic that the resulting Zod templates would say very little about the real domain structure. The current templates are intentionally opinionated in favor of developer usability.
70
+
71
+ ---
72
+
73
+ ## 3. Why the templates use “raw” and “profile” layers
74
+
75
+ A recurring pattern across these XSDs is that the literal XSD body is **not the whole story**.
76
+
77
+ Two important examples:
78
+
79
+ 1. **Common Cartridge manifest**
80
+ - the XSD defines the structural grammar
81
+ - the embedded Schematron defines many real business rules
82
+ - duplicate dependency/file detection
83
+ - forbidden dependency graphs between resource types
84
+ - `href`/`file` coupling
85
+ - item-to-question-bank restrictions
86
+
87
+ 2. **Embedded QTI ASI profile inside Common Cartridge**
88
+ - the XSD allows broad structural patterns
89
+ - the embedded Schematron narrows them into actual question profile rules
90
+ - T/F, multiple choice, multiple response, FIB, pattern match, essay
91
+ - metadata field vocabularies and multiplicities
92
+ - feedback linkage rules
93
+
94
+ ### Decision
95
+
96
+ The TypeScript file exports both:
97
+
98
+ - **raw structural schemas** that match the XSD-declared object shape
99
+ - **profile schemas** with `.superRefine()` rules to encode the most important Schematron-level constraints
100
+
101
+ ### Rationale
102
+
103
+ This makes the output reusable in two modes:
104
+
105
+ - validate “is this shaped like the XML grammar?”
106
+ - validate “does this satisfy the actual profile semantics the XSD expects?”
107
+
108
+ That split is especially important because the profile prose, the XSD, and the active embedded Schematron do not always agree perfectly.
109
+
110
+ ---
111
+
112
+ ## 4. File-by-file analysis and mapping decisions
113
+
114
+ ## 4.1 `ccv1p3_imscsmd_v1p0.xsd`
115
+
116
+ This is the simplest schema in the set.
117
+
118
+ ### Structural summary
119
+
120
+ Root element:
121
+
122
+ - `curriculumStandardsMetadataSet`
123
+
124
+ Important types:
125
+
126
+ - `CurriculumStandardsMetadataSet.Type`
127
+ - `CurriculumStandardsMetadata.Type`
128
+ - `SetOfGUIDs.Type`
129
+ - `LabelledGUID.Type`
130
+
131
+ ### Mapping decision
132
+
133
+ This XSD maps very directly to plain objects, so the template stays close to the XSD names.
134
+
135
+ ### Rationale
136
+
137
+ There are no difficult ordering or namespace tradeoffs here, so a straightforward direct Zod object model is the most transparent choice.
138
+
139
+ ---
140
+
141
+ ## 4.2 `ccv1p3_imsdt_v1p3.xsd`
142
+
143
+ Discussion Topic is also structurally straightforward.
144
+
145
+ ### Structural summary
146
+
147
+ Root element:
148
+
149
+ - `topic`
150
+
151
+ Important types:
152
+
153
+ - `Topic.Type`
154
+ - `Text.Type`
155
+ - `Attachments.Type`
156
+ - `Attachment.Type`
157
+
158
+ ### Mapping decision
159
+
160
+ - `text` becomes an object with `value` and `texttype`
161
+ - `attachment` becomes an object with `href`
162
+ - the `grpStrict.any` extension point becomes `extensions?: XmlExtensionNode[]`
163
+
164
+ ### Rationale
165
+
166
+ This keeps the schema readable while still preserving the important information from the XSD:
167
+
168
+ - text value
169
+ - MIME-style text type
170
+ - extension slot
171
+
172
+ ---
173
+
174
+ ## 4.3 `ccv1p3_imswl_v1p3.xsd`
175
+
176
+ Web Link is similar to Discussion Topic.
177
+
178
+ ### Structural summary
179
+
180
+ Root element:
181
+
182
+ - `webLink`
183
+
184
+ Important types:
185
+
186
+ - `WebLink.Type`
187
+ - `URL.Type`
188
+
189
+ ### Mapping decision
190
+
191
+ - `url` is modeled as a nested object with `href`, `target`, and `windowFeatures`
192
+ - extension points again become `extensions?: XmlExtensionNode[]`
193
+
194
+ ### Rationale
195
+
196
+ This is the cleanest normalized JS shape while still remaining very easy to map back to the source XSD.
197
+
198
+ ---
199
+
200
+ ## 4.4 `ccv1p3_lommanifest_v1p0.xsd` and `ccv1p3_lomresource_v1p0.xsd`
201
+
202
+ These two schemas are effectively the same structurally.
203
+
204
+ ### Structural summary
205
+
206
+ Both expose:
207
+
208
+ - root element `lom`
209
+ - the same large set of LOM complex types
210
+ - loose value typing for vocab-like `value` elements
211
+ - `grpLax.any` extension points and foreign attributes on many types
212
+
213
+ ### Important XSD characteristic
214
+
215
+ These schemas use repeated unordered `xs:choice` content models. The **real singleton limits** are enforced by the embedded Schematron rules.
216
+
217
+ Example:
218
+
219
+ - `general.title` is intended to be 0..1
220
+ - `rights.cost` is intended to be 0..1
221
+ - `relation.kind` is intended to be 0..1
222
+
223
+ but the raw XSD shape on its own is looser than that.
224
+
225
+ ### Mapping decision
226
+
227
+ The templates use:
228
+
229
+ - plain object properties for singleton-ish children
230
+ - arrays for repeatable children
231
+ - optional `extensions` / `foreignAttributes` on loose profiles
232
+ - a small Schematron-inspired refinement on the root LOM object to document the singleton expectation
233
+
234
+ ### Rationale
235
+
236
+ Trying to preserve the unordered repeated `xs:choice` literally in JS would produce an awkward structure that is much less usable than a normalized object model. This is a case where Zod is better used to validate the **intended object model** rather than to mirror the low-level serialization trick the XSD uses.
237
+
238
+ ---
239
+
240
+ ## 4.5 `ccv1p3_lomccltilink_v1p0.xsd`
241
+
242
+ This is the strictest of the LOM family.
243
+
244
+ ### Structural summary
245
+
246
+ It shares the same high-level LOM section graph as the other LOM profiles, but it differs in two important ways:
247
+
248
+ 1. it does **not** allow the loose extension model
249
+ 2. many `value` elements are restricted to explicit enumerations
250
+
251
+ ### Mapping decision
252
+
253
+ The TypeScript file reuses the same LOM section structure but swaps in strict value enums for the LTI profile.
254
+
255
+ ### Rationale
256
+
257
+ This is a good place to share structure and vary only the profile policy:
258
+
259
+ - loose manifest/resource profiles: `value: z.string()`
260
+ - strict LTI profile: `value: z.enum([...])`
261
+
262
+ That keeps the templates internally consistent and makes the profile difference explicit.
263
+
264
+ ---
265
+
266
+ ## 4.6 `ccv1p3_imscp_v1p2_v1p0.xsd`
267
+
268
+ This is the Common Cartridge manifest profile.
269
+
270
+ ### Structural summary
271
+
272
+ Root element:
273
+
274
+ - `manifest`
275
+
276
+ Key containers:
277
+
278
+ - `metadata`
279
+ - `organizations`
280
+ - `resources`
281
+ - optional imported `authorizations`
282
+
283
+ Key nested types:
284
+
285
+ - `Manifest.Type`
286
+ - `ManifestMetadata.Type`
287
+ - `Organization.Type`
288
+ - `ItemOrg.Type`
289
+ - `Item.Type`
290
+ - `Resource.Type`
291
+ - `ResourceMetadata.Type`
292
+ - `File.Type`
293
+ - `Dependency.Type`
294
+ - imported `Authorizations.Type`
295
+ - imported `Authorization.Type`
296
+
297
+ ### Important tradeoff
298
+
299
+ The XSD itself is not enough. The embedded Schematron contributes a large amount of real semantic validation.
300
+
301
+ Examples encoded into the profile schema:
302
+
303
+ - duplicate `file/@href` inside a resource is forbidden
304
+ - duplicate `dependency/@identifierref` inside a resource is forbidden
305
+ - circular dependency on self is forbidden
306
+ - certain resource types must not use `href`
307
+ - `resource/@href` requires at least one matching `file/@href`
308
+ - web links must not have any dependency elements
309
+ - question-bank resources must not be referenced by manifest items
310
+ - manifest items with `identifierref` must not have child items
311
+
312
+ ### Important modeling decision
313
+
314
+ The imported authorization schema is now modeled directly.
315
+
316
+ That adds explicit support for:
317
+
318
+ - manifest-level `authorizations`
319
+ - the nested `authorization` object with required `cartridgeId` and optional `webservice`
320
+ - required `access` values of `cartridge | resource`
321
+ - optional boolean `import` on the `authorizations` wrapper
322
+ - the imported resource-level `protected` boolean attribute
323
+ - the strict foreign-extension slot allowed by the auth XSD on `authorizations`
324
+
325
+ ### Rationale
326
+
327
+ Now that the dependency is present locally, a direct schema mapping is more accurate than a placeholder. The templates still stop at **metadata structure validation**; they do not attempt to implement any authorization runtime or content-protection workflow.
328
+
329
+ ---
330
+
331
+ ## 4.7 `ccv1p3_qtiasiv1p2p1_v1p0.xsd`
332
+
333
+ This is the embedded Common Cartridge profile of QTI 1.2.1, and it is the most complex schema in the set.
334
+
335
+ ### Structural summary
336
+
337
+ Root element:
338
+
339
+ - `questestinterop`
340
+
341
+ The root contains exactly one of:
342
+
343
+ - `assessment`
344
+ - `objectbank`
345
+
346
+ Key subtrees:
347
+
348
+ - item presentation
349
+ - response processing
350
+ - metadata fields
351
+ - feedback / hint / solution blocks
352
+ - recursive boolean condition nodes
353
+ - recursive flow / material / label nodes
354
+
355
+ ### Biggest tradeoff in the whole set
356
+
357
+ QTI uses a lot of **ordered heterogeneous content**.
358
+
359
+ Examples:
360
+
361
+ - `flow`
362
+ - `flow_mat`
363
+ - `flow_label`
364
+ - `material`
365
+ - `render_choice`
366
+ - `render_fib`
367
+ - `itemfeedback`
368
+
369
+ If those are flattened into “just arrays of materials” or “just arrays of responses”, too much meaning is lost.
370
+
371
+ ### Mapping decision
372
+
373
+ The templates model these QTI sections as small AST-like unions with explicit `kind` fields and ordered `children` arrays.
374
+
375
+ Examples:
376
+
377
+ - `material` → ordered array of `mattext | matref | matbreak`
378
+ - `flow` → ordered array of `flow | material | material_ref | response_lid | response_str`
379
+ - `itemfeedback` → ordered array of `flow_mat | material | solution | hint`
380
+
381
+ ### Rationale
382
+
383
+ This is the smallest normalized JS representation that still preserves the aspects of order that matter for QTI logic and rendering.
384
+
385
+ ---
386
+
387
+ ## 5. Why `kind` discriminators were added for QTI nodes
388
+
389
+ The XSD obviously does not contain a `kind` attribute on most nodes.
390
+
391
+ ### Decision
392
+
393
+ The templates add a synthetic `kind` literal field on many QTI node types.
394
+
395
+ Examples:
396
+
397
+ - `kind: "material"`
398
+ - `kind: "flow"`
399
+ - `kind: "response_label"`
400
+ - `kind: "hint"`
401
+ - `kind: "solution"`
402
+
403
+ ### Rationale
404
+
405
+ This is one of the places where a TypeScript/Zod model is different from the source XML model on purpose.
406
+
407
+ Without a discriminator, recursive Zod unions become much harder to understand and to use downstream. Adding `kind` is a pragmatic normalization step that makes the schemas substantially more usable while still remaining structurally faithful.
408
+
409
+ ---
410
+
411
+ ## 6. Important places where the templates intentionally follow the XSD + active Schematron over prose
412
+
413
+ There are several mismatches between comments/prose and the literal schema bodies.
414
+
415
+ ### Common Cartridge manifest examples
416
+
417
+ - the prose says `dependency/@identifierref` was changed to `xs:IDREF`, but the XSD body still declares `xs:string`
418
+ - the prose discusses external VDEX handling for resource types, but the XSD body still has a local inline enumeration
419
+ - the prose implies tighter constraints in some places than the active Schematron actually enforces
420
+
421
+ ### QTI examples
422
+
423
+ - the profile prose strongly suggests some metadata containers are mandatory
424
+ - the raw XSD leaves them optional
425
+ - the active Schematron generally validates them **if present**, rather than forcing them to exist everywhere
426
+
427
+ ### Decision
428
+
429
+ The templates favor:
430
+
431
+ 1. literal XSD structure
432
+ 2. active embedded Schematron
433
+ 3. prose comments only when they do not conflict with 1 or 2
434
+
435
+ ### Rationale
436
+
437
+ That order of trust is the safest way to avoid silently baking in rules that are described in comments but not actually present in the effective shipped schema binding.
438
+
439
+ ---
440
+
441
+ ## 7. What is still intentionally approximate
442
+
443
+ These templates are substantial, but they are still templates.
444
+
445
+ ### 7.1 LOM leaf-level foreign attributes
446
+
447
+ The loose LOM profiles technically allow foreign attributes on many leaf nodes. The template does not attempt to preserve that with perfect fidelity everywhere.
448
+
449
+ ### 7.2 Authorization workflow semantics
450
+
451
+ The authorization schema is now modeled structurally, but the templates still do **not** implement or validate any end-to-end authorization exchange or authenticated content access flow. They validate the schema shape only.
452
+
453
+ ### 7.3 Full XML lexical fidelity
454
+
455
+ The templates are not a byte-level XML round-trip model.
456
+
457
+ For example:
458
+
459
+ - namespace prefixes are not preserved as first-class data
460
+ - attribute order is not preserved
461
+ - the exact XSD mechanism for unordered repeated `choice` groups is normalized away in LOM
462
+
463
+ ### Rationale
464
+
465
+ All three omissions are deliberate. Modeling them exactly would make the templates much harder to use while adding comparatively little value for the likely TypeScript/Zod use case.
466
+
467
+ ---
468
+
469
+ ## 8. How to interpret the output structure
470
+
471
+ The main source layout is:
472
+
473
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_imscp_v1p2_v1p0.ts`
474
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_imscsmd_v1p0.ts`
475
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_imsdt_v1p3.ts`
476
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_imswl_v1p3.ts`
477
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_lommanifest_v1p0.ts`
478
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_lomresource_v1p0.ts`
479
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_lomccltilink_v1p0.ts`
480
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_imsccauth_v1p3.ts`
481
+ - `packages/contracts/src/common-cartridge/v1_3/ccv1p3_qtiasiv1p2p1_v1p0.ts`
482
+ - `packages/contracts/src/common-cartridge/v1_3/index.ts`
483
+
484
+ The version barrel `packages/contracts/src/common-cartridge/v1_3/index.ts` re-exports the directory surface for this specific Common Cartridge version.
485
+
486
+ The most important exports are:
487
+
488
+ ### Simpler direct mappings
489
+
490
+ - `CurriculumStandardsMetadataSetDocumentSchema`
491
+ - `DiscussionTopicDocumentSchema`
492
+ - `WebLinkDocumentSchema`
493
+
494
+ ### LOM profiles
495
+
496
+ - `LomManifestDocumentSchema`
497
+ - `LomResourceDocumentSchema`
498
+ - `LomCcLtiLinkDocumentSchema`
499
+
500
+ ### Common Cartridge manifest
501
+
502
+ - `CommonCartridgeAuthorizationsDocumentSchema`
503
+ - `CommonCartridgeManifestRawDocumentSchema`
504
+ - `CommonCartridgeManifestProfileDocumentSchema`
505
+
506
+ ### Embedded QTI ASI profile
507
+
508
+ - `QtiQuestestinteropRawDocumentSchema`
509
+ - `QtiQuestestinteropProfileDocumentSchema`
510
+
511
+ ### Convenience bundle
512
+
513
+ - `CommonCartridgeDerivedZodTemplates`
514
+
515
+ The raw/profile split is important:
516
+
517
+ - **raw** = XSD structure
518
+ - **profile** = XSD structure plus important Schematron-derived semantic checks
519
+
520
+ ---
521
+
522
+ ## 9. If this were taken further
523
+
524
+ The next logical refinements would be:
525
+
526
+ 1. add parser adapters for a specific XML parser output shape
527
+ 2. add parser-specific coercion or normalization for XML booleans/defaults such as `import` and `protected` if a concrete parser is chosen
528
+ 3. add more complete LOM Schematron refinements if strict profile-level validation becomes a requirement
529
+ 4. tighten the recursive schema typing so fewer helper-boundary casts are needed
530
+
531
+ The source is now split by schema file under `packages/contracts/src/common-cartridge/v1_3/`, with a version barrel preserved at `packages/contracts/src/common-cartridge/v1_3/index.ts`.