@conform-ed/contracts 0.0.6 → 0.0.9

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.
@@ -2,4 +2,4 @@ $ oxfmt --config ../../.oxfmtrc.jsonc --check .
2
2
  Checking formatting...
3
3
 
4
4
  All matched files use the correct format.
5
- Finished in 416ms on 180 files using 8 threads.
5
+ Finished in 451ms on 188 files using 8 threads.
@@ -1,3 +1,3 @@
1
1
  $ oxlint --config ../../.oxlintrc.jsonc .
2
2
  Found 0 warnings and 0 errors.
3
- Finished in 823ms on 162 files with 102 rules using 8 threads.
3
+ Finished in 807ms on 169 files with 102 rules using 8 threads.
@@ -2,194 +2,222 @@ $ bun test
2
2
  bun test v1.3.14 (0d9b296a)
3
3
 
4
4
  test/xapi.test.ts:
5
- (pass) xAPI 1.0.3 statement schema accepts the core data model [9.30ms]
6
- (pass) xAPI 2.0 statement schema accepts the same core data model [1.16ms]
7
- (pass) xAPI 2.0 statement schema accepts IEEE 2.0 contextAgent/contextGroup objects [4.24ms]
5
+ (pass) xAPI 1.0.3 statement schema accepts the core data model [12.75ms]
6
+ (pass) xAPI 2.0 statement schema accepts the same core data model [3.77ms]
7
+ (pass) xAPI 2.0 statement schema accepts IEEE 2.0 contextAgent/contextGroup objects [4.51ms]
8
8
  (pass) xAPI statement submission schema accepts batches [0.25ms]
9
- (pass) xAPI document, listing, and about schemas validate LRS metadata [1.68ms]
10
- (pass) xAPI validation rejects statements missing required fields [0.32ms]
11
- (pass) xAPI statement result, person object, and resource queries validate missing spec objects [1.94ms]
9
+ (pass) xAPI document, listing, and about schemas validate LRS metadata [1.66ms]
10
+ (pass) xAPI validation rejects statements missing required fields [0.30ms]
11
+ (pass) xAPI statement result, person object, and resource queries validate missing spec objects [1.99ms]
12
+ (pass) xAPI Agent schema enforces exactly one Inverse Functional Identifier [0.42ms]
12
13
  (pass) xAPI HTTP method enum validates LRS operations [0.09ms]
13
14
  (pass) xAPI error response schema validates LRS error payloads [0.36ms]
14
- (pass) xAPI error codes validate standard HTTP status responses [0.10ms]
15
- (pass) xAPI concurrency schema validates ETags and conditional headers [0.30ms]
16
- (pass) xAPI multipart attachment part schema validates attachment data structure [0.14ms]
17
- (pass) xAPI multipart request schema validates statement with attachments [0.36ms]
18
- (pass) xAPI resource schema models LRS endpoint definitions [0.42ms]
19
- (pass) xAPI request and response headers are properly enumerated [0.16ms]
15
+ (pass) xAPI error codes validate standard HTTP status responses [0.09ms]
16
+ (pass) xAPI concurrency schema validates ETags and conditional headers [0.27ms]
17
+ (pass) xAPI multipart attachment part schema validates attachment data structure [0.13ms]
18
+ (pass) xAPI multipart request schema validates statement with attachments [0.39ms]
19
+ (pass) xAPI resource schema models LRS endpoint definitions [0.43ms]
20
+ (pass) xAPI request and response headers are properly enumerated [0.12ms]
20
21
 
21
22
  test/cmi5-v1_0.test.ts:
22
- (pass) cmi5 course structure schema parses the simple XML example [7.12ms]
23
- (pass) cmi5 keyword extension schema parses the extended XML example [3.74ms]
24
- (pass) cmi5 recursive course structure schema accepts nested blocks and AUs [1.20ms]
23
+ (pass) cmi5 course structure schema parses the simple XML example [5.83ms]
24
+ (pass) cmi5 keyword extension schema parses the extended XML example [2.69ms]
25
+ (pass) cmi5 recursive course structure schema accepts nested blocks and AUs [1.08ms]
25
26
 
26
27
  test/oneroster-v1_2.test.ts:
27
- (pass) UserSchema parses a minimal OneRoster user [2.34ms]
28
- (pass) LineItemSchema parses a minimal gradebook line item [1.14ms]
29
- (pass) ResourceSchema parses a minimal resource entry [0.62ms]
30
- (pass) ImsxStatusInfoSchema parses a OneRoster status envelope [0.63ms]
31
- (pass) EnrollmentRoleSchema accepts extension values and rejects unknown built-ins [0.18ms]
28
+ (pass) UserSchema parses a minimal OneRoster user [2.89ms]
29
+ (pass) LineItemSchema parses a minimal gradebook line item [1.17ms]
30
+ (pass) ResourceSchema parses a minimal resource entry [0.60ms]
31
+ (pass) ImsxStatusInfoSchema parses a OneRoster status envelope [0.61ms]
32
+ (pass) EnrollmentRoleSchema accepts extension values and rejects unknown built-ins [0.20ms]
32
33
  (pass) ClassSchema requires at least one term reference [1.03ms]
33
- (pass) OneRoster12DerivedZodTemplates exposes core published entry points [0.05ms]
34
+ (pass) OneRoster12DerivedZodTemplates exposes core published entry points [0.04ms]
34
35
  (pass) OneRoster REST binding operation catalogs expose all endpoint schemas [0.05ms]
35
36
  (pass) Gradebook operation bindings expose request/response payload schemas [0.03ms]
36
- (pass) ManifestCsvRowSchema accepts either starred or unstarred optional file keys [1.71ms]
37
- (pass) UsersCsvRowSchema validates required OneRoster 1.2 user CSV columns [1.04ms]
38
- (pass) OneRosterCsvBindingPackageSchema validates package-level CSV document sets [1.53ms]
37
+ (pass) ManifestCsvRowSchema accepts either starred or unstarred optional file keys [1.61ms]
38
+ (pass) UsersCsvRowSchema validates required OneRoster 1.2 user CSV columns [1.01ms]
39
+ (pass) OneRosterCsvBindingPackageSchema validates package-level CSV document sets [1.49ms]
39
40
 
40
41
  test/qti-v2_1.test.ts:
41
- (pass) QtiV2_1 metadata document parses a minimal metadata payload [0.75ms]
42
- (pass) QtiV2_1 assessment item document parses a minimal choice item [10.73ms]
43
- (pass) QtiV2_1 result document rejects record responses without field identifiers [1.76ms]
44
- (pass) QtiV2_1 manifest document parses a minimal package [1.38ms]
45
- (pass) QtiV2_1 APIP accessibility document parses a minimal payload [1.40ms]
42
+ (pass) QtiV2_1 metadata document parses a minimal metadata payload [0.74ms]
43
+ (pass) QtiV2_1 assessment item document parses a minimal choice item [10.61ms]
44
+ (pass) QtiV2_1 result document rejects record responses without field identifiers [1.63ms]
45
+ (pass) QtiV2_1 manifest document parses a minimal package [1.31ms]
46
+ (pass) QtiV2_1 APIP accessibility document parses a minimal payload [1.43ms]
46
47
 
47
48
  test/qti-v3_0_1.test.ts:
48
- (pass) QtiMetadataDocumentSchema parses a minimal metadata document [2.23ms]
49
- (pass) QtiAccessForAllPnpDocumentSchema parses a minimal preferences document [2.73ms]
50
- (pass) QtiAssessmentResultDocumentSchema parses a minimal result report [2.67ms]
51
- (pass) QtiAssessmentResultDocumentSchema rejects record response values without field identifiers [0.25ms]
52
- (pass) QtiAssessmentResultDocumentSchema rejects record outcomes with baseType [0.56ms]
53
- (pass) QtiUsageDataDocumentSchema parses ordinary and categorized statistics [2.63ms]
54
- (pass) QtiUsageDataDocumentSchema rejects invalid objectType and mapping bounds [0.34ms]
55
- (pass) QtiResponseProcessingDocumentSchema parses typed processing operators [10.93ms]
56
- (pass) QtiOutcomeProcessingDocumentSchema parses typed aggregate operators [6.98ms]
57
- (pass) QtiResponseProcessingDocumentSchema rejects invalid operator parameters [2.27ms]
58
- (pass) QtiOutcomeProcessingDocumentSchema rejects invalid operator arity and ranges [1.57ms]
59
- (pass) QtiAssessmentSectionDocumentSchema parses a minimal standalone section [2.36ms]
60
- (pass) QtiAssessmentStimulusDocumentSchema parses a minimal stimulus [1.68ms]
61
- (pass) QtiOutcomeDeclarationDocumentSchema parses a minimal outcome declaration [0.91ms]
49
+ (pass) QtiMetadataDocumentSchema parses a minimal metadata document [2.13ms]
50
+ (pass) QtiAccessForAllPnpDocumentSchema parses a minimal preferences document [2.57ms]
51
+ (pass) QtiAssessmentResultDocumentSchema parses a minimal result report [2.50ms]
52
+ (pass) QtiAssessmentResultDocumentSchema rejects record response values without field identifiers [0.22ms]
53
+ (pass) QtiAssessmentResultDocumentSchema rejects record outcomes with baseType [0.57ms]
54
+ (pass) QtiUsageDataDocumentSchema parses ordinary and categorized statistics [2.57ms]
55
+ (pass) QtiUsageDataDocumentSchema rejects invalid objectType and mapping bounds [0.36ms]
56
+ (pass) QtiResponseProcessingDocumentSchema parses typed processing operators [10.90ms]
57
+ (pass) QtiOutcomeProcessingDocumentSchema parses typed aggregate operators [6.60ms]
58
+ (pass) QtiResponseProcessingDocumentSchema rejects invalid operator parameters [2.18ms]
59
+ (pass) QtiOutcomeProcessingDocumentSchema rejects invalid operator arity and ranges [1.56ms]
60
+ (pass) QtiAssessmentSectionDocumentSchema parses a minimal standalone section [2.51ms]
61
+ (pass) QtiAssessmentStimulusDocumentSchema parses a minimal stimulus [1.67ms]
62
+ (pass) QtiOutcomeDeclarationDocumentSchema parses a minimal outcome declaration [0.93ms]
62
63
  (pass) QtiAsiProfileDocumentSchema accepts resource-specific QTI documents [1.09ms]
63
- (pass) QtiAssessmentItemDocumentSchema parses a minimal choice item [19.03ms]
64
- (pass) QtiAssessmentItemDocumentSchema rejects incompatible response declarations [0.66ms]
65
- (pass) QtiAssessmentTestDocumentSchema parses a minimal test document [1.91ms]
64
+ (pass) QtiAssessmentItemDocumentSchema parses a minimal choice item [19.23ms]
65
+ (pass) QtiAssessmentItemDocumentSchema rejects incompatible response declarations [0.63ms]
66
+ (pass) QtiAssessmentTestDocumentSchema parses a minimal test document [1.83ms]
66
67
  (pass) Qti301DerivedZodTemplates exposes expected document templates [0.03ms]
67
- (pass) QTI barrel exports prefixed XML extension node helpers [0.32ms]
68
+ (pass) QTI barrel exports prefixed XML extension node helpers [0.34ms]
68
69
 
69
70
  test/contracts.test.ts:
70
- (pass) RunnerConfigSchema parses minimal config [1.48ms]
71
+ (pass) RunnerConfigSchema parses minimal config [3.23ms]
71
72
  (pass) RunnerConfigSchema requires adapter for cmi5 suite [0.14ms]
72
- (pass) RunnerConfigSchema enforces bearer adapter token source [0.67ms]
73
- (pass) AdapterCapabilitySchema validates profile payload [0.46ms]
74
- (pass) AdapterProfileSchema validates cmi5 interoperability contract [1.29ms]
75
- (pass) AdapterProfileSchema requires cmi5 packageUpload [0.09ms]
76
- (pass) AdapterErrorSchema validates uniform error envelope [0.52ms]
73
+ (pass) RunnerConfigSchema enforces bearer adapter token source [0.71ms]
74
+ (pass) AdapterCapabilitySchema validates profile payload [0.41ms]
75
+ (pass) AdapterProfileSchema validates cmi5 interoperability contract [1.50ms]
76
+ (pass) AdapterProfileSchema requires cmi5 packageUpload [0.10ms]
77
+ (pass) AdapterErrorSchema validates uniform error envelope [0.50ms]
77
78
 
78
79
  test/qti-v2_2.test.ts:
79
- (pass) QtiV2_2 metadata document parses a minimal metadata payload with scoring modes [0.76ms]
80
- (pass) QtiV2_2 assessment stimulus and assessment item documents parse together [3.15ms]
81
- (pass) QtiV2_2 processing rejects invalid numeric operator parameters [5.85ms]
82
- (pass) QtiV2_2 curriculum standards metadata and manifest documents parse [1.31ms]
83
- (pass) QtiV2_2 APIP accessibility document parses external supplemental accessibility [1.08ms]
80
+ (pass) QtiV2_2 metadata document parses a minimal metadata payload with scoring modes [0.81ms]
81
+ (pass) QtiV2_2 assessment stimulus and assessment item documents parse together [3.17ms]
82
+ (pass) QtiV2_2 processing rejects invalid numeric operator parameters [5.53ms]
83
+ (pass) QtiV2_2 curriculum standards metadata and manifest documents parse [1.33ms]
84
+ (pass) QtiV2_2 APIP accessibility document parses external supplemental accessibility [1.02ms]
84
85
 
85
86
  test/cat-v1_0.test.ts:
86
- (pass) CAT v1.0 Zod Schemas > Shared validators > UuidSchema validates RFC4122 compliant UUIDs [1.64ms]
87
- (pass) CAT v1.0 Zod Schemas > Shared validators > DateTimeSchema validates ISO8601 datetimes [0.28ms]
88
- (pass) CAT v1.0 Zod Schemas > Shared validators > ExtensionEnum supports standard values and ext:* vendor extensions [0.20ms]
87
+ (pass) CAT v1.0 Zod Schemas > Shared validators > UuidSchema validates RFC4122 compliant UUIDs [1.63ms]
88
+ (pass) CAT v1.0 Zod Schemas > Shared validators > DateTimeSchema validates ISO8601 datetimes [0.37ms]
89
+ (pass) CAT v1.0 Zod Schemas > Shared validators > ExtensionEnum supports standard values and ext:* vendor extensions [0.22ms]
89
90
  (pass) CAT v1.0 Zod Schemas > Shared validators > OutcomeVariableType supports outcome types and vendor extensions [0.05ms]
90
- (pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable validates outcome variable structure [0.45ms]
91
- (pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable rejects additional properties [0.11ms]
92
- (pass) CAT v1.0 Zod Schemas > Core data types > ResponseVariable validates candidate responses [0.15ms]
91
+ (pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable validates outcome variable structure [0.44ms]
92
+ (pass) CAT v1.0 Zod Schemas > Core data types > OutcomeVariable rejects additional properties [0.10ms]
93
+ (pass) CAT v1.0 Zod Schemas > Core data types > ResponseVariable validates candidate responses [0.14ms]
93
94
  (pass) CAT v1.0 Zod Schemas > Core data types > ItemRef validates item references [0.21ms]
94
- (pass) CAT v1.0 Zod Schemas > Core data types > ItemPool validates item pool with multiple items [0.17ms]
95
- (pass) CAT v1.0 Zod Schemas > Core data types > SectionData validates complete section configuration [0.53ms]
95
+ (pass) CAT v1.0 Zod Schemas > Core data types > ItemPool validates item pool with multiple items [0.16ms]
96
+ (pass) CAT v1.0 Zod Schemas > Core data types > SectionData validates complete section configuration [0.61ms]
96
97
  (pass) CAT v1.0 Zod Schemas > Core data types > ItemStage validates items to present to candidate [0.47ms]
97
- (pass) CAT v1.0 Zod Schemas > Core data types > AssessmentResult validates candidate responses and outcomes [0.63ms]
98
- (pass) CAT v1.0 Zod Schemas > Core data types > CatEngineResultReport validates CAT engine response [0.56ms]
99
- (pass) CAT v1.0 Zod Schemas > Core data types > SessionInfo validates session state [0.56ms]
100
- (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionRequest validates section creation [0.19ms]
101
- (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionResponse validates section creation response [0.20ms]
98
+ (pass) CAT v1.0 Zod Schemas > Core data types > AssessmentResult validates candidate responses and outcomes [0.70ms]
99
+ (pass) CAT v1.0 Zod Schemas > Core data types > CatEngineResultReport validates CAT engine response [0.54ms]
100
+ (pass) CAT v1.0 Zod Schemas > Core data types > SessionInfo validates session state [0.49ms]
101
+ (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionRequest validates section creation [0.17ms]
102
+ (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSectionResponse validates section creation response [0.21ms]
102
103
  (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionRequest validates session creation [0.22ms]
103
- (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionResponse validates session creation response [0.20ms]
104
+ (pass) CAT v1.0 Zod Schemas > REST operation payloads > CreateSessionResponse validates session creation response [0.18ms]
104
105
  (pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsRequest validates result submission [0.21ms]
105
- (pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsResponse validates CAT engine result report [0.04ms]
106
+ (pass) CAT v1.0 Zod Schemas > REST operation payloads > SubmitResultsResponse validates CAT engine result report [0.05ms]
106
107
  (pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionRequest validates session termination [0.19ms]
107
108
  (pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSessionResponse validates session termination response [0.22ms]
108
- (pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionRequest validates section closure [0.13ms]
109
+ (pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionRequest validates section closure [0.14ms]
109
110
  (pass) CAT v1.0 Zod Schemas > REST operation payloads > EndSectionResponse validates section closure response [0.08ms]
110
- (pass) CAT v1.0 Zod Schemas > REST operation bindings > REST operation catalog exposes all 6 CAT operations [0.04ms]
111
- (pass) CAT v1.0 Zod Schemas > REST operation bindings > CreateSection operation has correct structure [0.03ms]
111
+ (pass) CAT v1.0 Zod Schemas > REST operation bindings > REST operation catalog exposes all 6 CAT operations [0.03ms]
112
+ (pass) CAT v1.0 Zod Schemas > REST operation bindings > CreateSection operation has correct structure [0.24ms]
112
113
  (pass) CAT v1.0 Zod Schemas > REST operation bindings > SubmitResults operation has correct path template [0.02ms]
113
- (pass) CAT v1.0 Zod Schemas > Derived templates > Cat10DerivedZodTemplates exposes specification metadata [0.24ms]
114
+ (pass) CAT v1.0 Zod Schemas > Derived templates > Cat10DerivedZodTemplates exposes specification metadata [0.23ms]
114
115
  (pass) CAT v1.0 Zod Schemas > Derived templates > Specification links point to official CAT v1.0 resources [0.05ms]
115
- (pass) CAT v1.0 Zod Schemas > Error handling > ErrorResponse validates error payloads [0.33ms]
116
+ (pass) CAT v1.0 Zod Schemas > Error handling > ErrorResponse validates error payloads [0.30ms]
116
117
 
117
118
  test/schema-examples.test.ts:
118
- (pass) example configs parse with RunnerConfigSchema [0.46ms]
119
+ (pass) example configs parse with RunnerConfigSchema [0.44ms]
119
120
  (pass) summary sample shape validates [1.53ms]
120
- (pass) requirement trace sample shape validates [0.53ms]
121
+ (pass) requirement trace sample shape validates [0.51ms]
121
122
  (pass) run metadata sample shape validates [1.02ms]
122
123
  (pass) adapter profile sample shape validates [0.15ms]
123
124
 
124
125
  test/clr-v2_0.test.ts:
125
126
  (pass) ClrCredentialSchema parses a realistic CLR 2.0 credential [6.57ms]
126
127
  (pass) AchievementCredentialSchema parses a minimal achievement credential [1.80ms]
127
- (pass) ProfileSchema parses a minimal issuer profile [0.07ms]
128
- (pass) GetClrCredentialsResponseSchema parses credential and JWS collections [0.40ms]
129
- (pass) ImsxStatusInfoSchema enforces known status vocabularies [0.48ms]
130
- (pass) ClrCredentialSchema rejects credentials without the CLR context set [0.37ms]
128
+ (pass) ProfileSchema parses a minimal issuer profile [0.09ms]
129
+ (pass) GetClrCredentialsResponseSchema parses credential and JWS collections [0.39ms]
130
+ (pass) ImsxStatusInfoSchema enforces known status vocabularies [0.45ms]
131
+ (pass) ClrCredentialSchema rejects credentials without the CLR context set [0.40ms]
131
132
  (pass) Clr20DerivedZodTemplates exposes the published entry points [0.04ms]
132
133
 
133
134
  test/cc-v1_4.test.ts:
134
- (pass) CommonCartridgeV1_4 parses a minimal core manifest [3.66ms]
135
- (pass) CommonCartridgeV1_4 rejects question-bank references from organization items [0.89ms]
136
- (pass) CommonCartridgeV1_4 parses a minimal thin manifest with embedded webLink XML [2.20ms]
137
- (pass) CommonCartridgeV1_4 thin profile rejects mismatched embedded resource XML [1.27ms]
138
- (pass) CommonCartridgeV1_4 K-12 LOM resource profile enforces required educational metadata [2.11ms]
139
- (pass) CommonCartridgeV1_4 parses assignment, line item, accessibility, and open video documents [3.48ms]
135
+ (pass) CommonCartridgeV1_4 parses a minimal core manifest [3.50ms]
136
+ (pass) CommonCartridgeV1_4 rejects question-bank references from organization items [0.88ms]
137
+ (pass) CommonCartridgeV1_4 parses a minimal thin manifest with embedded webLink XML [2.23ms]
138
+ (pass) CommonCartridgeV1_4 thin profile rejects mismatched embedded resource XML [1.29ms]
139
+ (pass) CommonCartridgeV1_4 K-12 LOM resource profile enforces required educational metadata [2.13ms]
140
+ (pass) CommonCartridgeV1_4 parses assignment, line item, accessibility, and open video documents [3.43ms]
140
141
  (pass) CommonCartridgeV1_4 exposes expected derived templates [0.04ms]
141
142
 
142
143
  test/open-badges-v3_0.test.ts:
143
- (pass) AchievementCredentialSchema parses a realistic Open Badge credential [0.42ms]
144
- (pass) EndorsementCredentialSchema parses a valid endorsement credential [1.35ms]
145
- (pass) GetOpenBadgeCredentialsResponseSchema parses credential and JWS collections [0.27ms]
146
- (pass) AchievementCredentialSchema rejects missing OB context [0.34ms]
144
+ (pass) AchievementCredentialSchema parses a realistic Open Badge credential [0.43ms]
145
+ (pass) EndorsementCredentialSchema parses a valid endorsement credential [1.32ms]
146
+ (pass) GetOpenBadgeCredentialsResponseSchema parses credential and JWS collections [0.26ms]
147
+ (pass) AchievementCredentialSchema rejects missing OB context [0.29ms]
147
148
  (pass) OpenBadges30DerivedZodTemplates exposes expected entry points [0.04ms]
148
149
 
149
150
  test/vc-data-model-v2_0.test.ts:
150
- (pass) VerifiableCredentialSchema parses a minimal VC Data Model 2.0 credential [1.02ms]
151
- (pass) VerifiablePresentationSchema parses a presentation carrying VC and JWS credentials [0.53ms]
152
- (pass) VerifiableCredentialSchema rejects wrong core context [0.18ms]
153
- (pass) VcDataModel20DerivedZodTemplates exposes expected entry points [0.04ms]
151
+ (pass) VerifiableCredentialSchema parses a minimal VC Data Model 2.0 credential [1.01ms]
152
+ (pass) VerifiablePresentationSchema parses a presentation carrying VC and JWS credentials [0.50ms]
153
+ (pass) VerifiableCredentialSchema rejects wrong core context [0.16ms]
154
+ (pass) VcDataModel20DerivedZodTemplates exposes expected entry points [0.03ms]
155
+
156
+ test/h5p-v1.test.ts:
157
+ (pass) H5P PackageManifest accepts a minimal valid h5p.json [1.72ms]
158
+ (pass) H5P PackageManifest accepts all optional fields [0.55ms]
159
+ (pass) H5P PackageManifest rejects missing required title [0.15ms]
160
+ (pass) H5P PackageManifest rejects invalid machine name [0.13ms]
161
+ (pass) H5P PackageManifest rejects invalid embed type [0.14ms]
162
+ (pass) H5P PackageManifest rejects yearTo < yearFrom [0.10ms]
163
+ (pass) H5P PackageManifest rejects duplicate embed types [0.11ms]
164
+ (pass) H5P LibraryManifest accepts a minimal valid library.json [1.06ms]
165
+ (pass) H5P LibraryManifest accepts a runnable library with full fields [0.64ms]
166
+ (pass) H5P LibraryManifest rejects invalid machineName [0.11ms]
167
+ (pass) H5P VersionRef rejects patch version (not part of the schema) [0.06ms]
168
+ (pass) H5P LibraryFolderName validates correct folder name [0.06ms]
169
+ (pass) H5P LibraryFolderName rejects folder names with patch version [0.04ms]
170
+ (pass) H5P Semantics accepts a simple text field [0.87ms]
171
+ (pass) H5P Semantics accepts a boolean field [2.12ms]
172
+ (pass) H5P Semantics accepts a select field with options [2.96ms]
173
+ (pass) H5P Semantics accepts a group with nested fields [1.61ms]
174
+ (pass) H5P Semantics accepts a list field containing a group [1.08ms]
175
+ (pass) H5P Semantics accepts deeply nested groups (3 levels) [0.19ms]
176
+ (pass) H5P Semantics accepts library field with options [0.12ms]
177
+ (pass) H5P Semantics accepts an image field [0.08ms]
178
+ (pass) H5P Semantics rejects unknown field type [1.05ms]
179
+ (pass) H5P Semantics rejects field without name [0.43ms]
180
+ (pass) H5P MediaFile accepts a valid image file reference [0.71ms]
181
+ (pass) H5P LibraryEmbed accepts a valid embedded library reference [0.32ms]
154
182
 
155
183
  test/lti.test.ts:
156
- (pass) LTI core launch schema accepts a normalized resource link launch [2.01ms]
157
- (pass) LTI deep linking schema accepts request settings and response items [2.01ms]
158
- (pass) LTI AGS schema accepts endpoint, line item, score, and result shapes [1.59ms]
159
- (pass) LTI NRPS schema accepts names/roles service and membership container shapes [1.28ms]
184
+ (pass) LTI core launch schema accepts a normalized resource link launch [1.98ms]
185
+ (pass) LTI deep linking schema accepts request settings and response items [1.99ms]
186
+ (pass) LTI AGS schema accepts endpoint, line item, score, and result shapes [1.56ms]
187
+ (pass) LTI NRPS schema accepts names/roles service and membership container shapes [1.30ms]
160
188
  (pass) LTI proctoring schema accepts start and end assessment messages [1.56ms]
161
189
 
162
190
  test/case-v1_1.test.ts:
163
- (pass) CASE v1.1 Schemas > Core entity schemas > should validate a valid CFAssociation [1.22ms]
191
+ (pass) CASE v1.1 Schemas > Core entity schemas > should validate a valid CFAssociation [1.97ms]
164
192
  (pass) CASE v1.1 Schemas > Core entity schemas > should reject CFAssociation with invalid UUID [0.16ms]
165
- (pass) CASE v1.1 Schemas > REST binding operations > should expose getCFAssociation operation [0.04ms]
193
+ (pass) CASE v1.1 Schemas > REST binding operations > should expose getCFAssociation operation [0.03ms]
166
194
  (pass) CASE v1.1 Schemas > REST binding operations > should expose listCFAssociations operation [0.02ms]
167
195
  (pass) CASE v1.1 Schemas > REST binding operations > should expose getCFPackage operation [0.02ms]
168
- (pass) CASE v1.1 Schemas > REST binding operations > should expose listCFItems operation [0.01ms]
169
- (pass) CASE v1.1 Schemas > REST binding operations > should expose getCFRubric operation [0.02ms]
196
+ (pass) CASE v1.1 Schemas > REST binding operations > should expose listCFItems operation [0.02ms]
197
+ (pass) CASE v1.1 Schemas > REST binding operations > should expose getCFRubric operation [0.01ms]
170
198
  (pass) CASE v1.1 Schemas > Shared validators > should validate RFC4122 UUID [0.02ms]
171
199
  (pass) CASE v1.1 Schemas > Shared validators > should reject invalid UUID format [0.05ms]
172
200
  (pass) CASE v1.1 Schemas > Shared validators > should validate ISO8601 datetime [0.02ms]
173
201
  (pass) CASE v1.1 Schemas > Shared validators > should validate link URI [0.27ms]
174
- (pass) CASE v1.1 Schemas > Extensible vocabularies > should allow standard association types [0.13ms]
202
+ (pass) CASE v1.1 Schemas > Extensible vocabularies > should allow standard association types [0.12ms]
175
203
  (pass) CASE v1.1 Schemas > Extensible vocabularies > should allow ext:* custom extensions [0.06ms]
176
204
  (pass) CASE v1.1 Schemas > Extensible vocabularies > should reject invalid ext: format [0.08ms]
177
205
  (pass) CASE v1.1 Schemas > Derived templates > should expose spec links [0.04ms]
178
- (pass) CASE v1.1 Schemas > Derived templates > should document scope coverage [0.02ms]
206
+ (pass) CASE v1.1 Schemas > Derived templates > should document scope coverage [0.03ms]
179
207
 
180
208
  test/caliper-v1_2.test.ts:
181
- (pass) ActionSchema validates Caliper action vocabulary [0.19ms]
182
- (pass) PersonSchema requires top-level @context for described entity documents [0.76ms]
183
- (pass) EventSchema enforces URN UUID ids and UTC-millisecond eventTime [1.14ms]
184
- (pass) AnnotationEventSchema enforces textual actor/action/object constraints [0.96ms]
185
- (pass) MessageEventSchema accepts MarkedAsUnRead textual alias [0.37ms]
186
- (pass) ViewEventSchema restricts target entity type to Frame [0.45ms]
187
- (pass) EnvelopeSchema enforces required fields and disallows custom top-level properties [5.56ms]
188
- (pass) TextPositionSelectorSchema requires type/start/end [0.49ms]
189
- (pass) SystemIdentifierSchema validates required identifier fields [0.44ms]
209
+ (pass) ActionSchema validates Caliper action vocabulary [0.18ms]
210
+ (pass) PersonSchema requires top-level @context for described entity documents [0.75ms]
211
+ (pass) EventSchema enforces URN UUID ids and UTC-millisecond eventTime [1.12ms]
212
+ (pass) AnnotationEventSchema enforces textual actor/action/object constraints [0.97ms]
213
+ (pass) MessageEventSchema accepts MarkedAsUnRead textual alias [0.38ms]
214
+ (pass) ViewEventSchema restricts target entity type to Frame [0.40ms]
215
+ (pass) EnvelopeSchema enforces required fields and disallows custom top-level properties [5.48ms]
216
+ (pass) TextPositionSelectorSchema requires type/start/end [0.48ms]
217
+ (pass) SystemIdentifierSchema validates required identifier fields [0.49ms]
190
218
  (pass) Caliper12DerivedZodTemplates exposes key Caliper entry points and conformance metadata [0.07ms]
191
219
 
192
- 156 pass
220
+ 182 pass
193
221
  0 fail
194
- 279 expect() calls
195
- Ran 156 tests across 16 files. [452.00ms]
222
+ 313 expect() calls
223
+ Ran 182 tests across 17 files. [478.00ms]
@@ -0,0 +1,124 @@
1
+ # H5P v1 Zod Templates
2
+
3
+ ## Specification Sources
4
+
5
+ | Source | URL |
6
+ | --------------------------- | --------------------------------------------------------------- |
7
+ | H5P Developer Specification | https://h5p.org/documentation/developers/h5p-specification |
8
+ | JSON File Descriptions | https://h5p.org/documentation/developers/json-file-descriptions |
9
+ | Semantics Reference | https://h5p.org/documentation/developers/semantics |
10
+ | PHP Reference Library | https://github.com/h5p/h5p-php-library |
11
+ | NodeJS Reference Library | https://github.com/Lumieducation/h5p-nodejs-library |
12
+
13
+ ## Scope
14
+
15
+ This bundle covers the four JSON schemas that make up the H5P package format:
16
+
17
+ | File | Schema | Description |
18
+ | ---------------- | -------------------------- | ------------------------------------------------------- |
19
+ | `h5p.json` | `H5pPackageManifestSchema` | Top-level package manifest in every `.h5p` archive |
20
+ | `library.json` | `H5pLibraryManifestSchema` | Library directory manifest (runnable or helper library) |
21
+ | `semantics.json` | `H5pSemanticsSchema` | Editor form schema — array of typed field descriptors |
22
+ | `content.json` | `H5pContentParamsSchema` | Permissive base (`z.record`) for per-library content |
23
+
24
+ Not in scope: ZIP extraction, file whitelist checking, content validation against semantics.
25
+
26
+ ## Dependency Decision: Why No External Library
27
+
28
+ | Library | Licence | Verdict |
29
+ | ---------------------------------- | -------------------------------- | ------------------------------------------------------------ |
30
+ | `h5p/h5p-php-library` | GPL-3.0 (HTML purification code) | Cannot use — GPL is viral |
31
+ | `Lumieducation/h5p-nodejs-library` | MIT (JS parts) | Wrong abstraction — it's a full HTTP server, not a validator |
32
+
33
+ We implement our own Zod schemas, referencing both libraries for validation logic and regex patterns.
34
+
35
+ ## Design Decisions
36
+
37
+ ### Machine name validation
38
+
39
+ Pattern from `H5PValidator::isValidRequiredH5pData` in h5p-php-library:
40
+
41
+ ```
42
+ /^[\w0-9\-\.]{1,255}$/i
43
+ ```
44
+
45
+ Used on every `machineName` field and in the library folder name schema.
46
+
47
+ ### Patch version excluded from version references
48
+
49
+ H5P dependency declarations (`preloadedDependencies`, `dynamicDependencies`, `editorDependencies`) use only `{ machineName, majorVersion, minorVersion }`. Any patch release of a matching major.minor is acceptable. `H5pVersionRefSchema` reflects this: it has no `patchVersion` field.
50
+
51
+ `H5pLibraryManifestSchema` does include `patchVersion` because the library itself must declare its full three-part version.
52
+
53
+ ### `runnable` and `fullscreen` are integers, not booleans
54
+
55
+ The H5P spec defines these as `0` or `1`, not `true`/`false`. `z.union([z.literal(0), z.literal(1)])` is used in `H5pLibraryManifestSchema` to match this exactly.
56
+
57
+ ### Semantics: recursive type handling
58
+
59
+ `semantics.json` defines a recursive schema: `group` contains `fields: SemanticsField[]` and `list` contains `field: SemanticsField`. This is handled with the `z.lazy()` / let-then-assign pattern (same approach as cmi5 `Block`/`Au` schemas):
60
+
61
+ ```typescript
62
+ let h5pSemanticsFieldSchemaInternal: z.ZodType<H5pSemanticsField>;
63
+
64
+ const groupFieldSchema = z.object({
65
+ ...baseFieldShape,
66
+ type: z.literal("group"),
67
+ fields: z.lazy(() => z.array(h5pSemanticsFieldSchemaInternal)),
68
+ ...
69
+ });
70
+
71
+ h5pSemanticsFieldSchemaInternal = z.union([textFieldSchema, ..., groupFieldSchema, listFieldSchema]);
72
+ ```
73
+
74
+ TypeScript-side recursive interfaces are declared manually to avoid circular inference, then the Zod schema is cast to `z.ZodType<H5pSemanticsField>`.
75
+
76
+ ### Semantics: non-strict objects
77
+
78
+ Leaf field schemas (`textFieldSchema`, `numberFieldSchema`, etc.) use `z.object(...)` (not `strictObject()`) because:
79
+
80
+ 1. H5P has a large ecosystem of third-party widgets that add arbitrary extra properties to field definitions.
81
+ 2. The spec acknowledges extension properties on field objects.
82
+
83
+ Only `H5pVersionRefSchema` and `H5pMediaFileSchema` use `strictObject()` since their shapes are fully specified.
84
+
85
+ ### Content params: permissive base
86
+
87
+ `content.json` structure is defined per-library by its `semantics.json`. There is no universal content schema. `H5pContentParamsSchema` is `z.record(z.string(), z.unknown())` — structurally valid JSON objects.
88
+
89
+ **Future work**: A `validateContentAgainstSemantics(content, semantics)` function could be built on top of `H5pSemanticsSchema` to perform deep structural validation. This requires a semantics traversal engine (walk the semantics field tree, validate each content value against its corresponding field schema). Out of scope for this bundle.
90
+
91
+ ### License codes
92
+
93
+ Standard H5P license codes from the editor UI:
94
+ `CC BY`, `CC BY-SA`, `CC BY-NC`, `CC BY-NC-SA`, `CC BY-ND`, `CC BY-NC-ND`, `CC0`, `GNU GPL`, `PD`, `ODC PDDL`, `CC PDM`, `U`
95
+
96
+ `"U"` means undisclosed/unknown — the H5P default when no license is specified.
97
+
98
+ ## Validation Logic Derived from PHP Validator
99
+
100
+ The following regex patterns are taken directly from `H5PValidator` in h5p-php-library:
101
+
102
+ | Field | Pattern |
103
+ | --------------- | -------------------------------------------------- |
104
+ | `machineName` | `/^[\w0-9\-\.]{1,255}$/i` |
105
+ | `title` | 1–255 characters |
106
+ | `language` | `/^[-a-zA-Z]{1,10}$/` (ISO-639-1 + region subtags) |
107
+ | version numbers | 1–5 digit integers |
108
+ | embed types | only `"iframe"` or `"div"` allowed |
109
+
110
+ Additional cross-field validations (implemented via `.superRefine()`):
111
+
112
+ - `embedTypes` must not contain duplicates
113
+ - `yearTo` must be ≥ `yearFrom` when both are present
114
+ - Runnable iframe libraries without declared `w`/`h` emit a validation issue
115
+
116
+ ## File Whitelist (Not Validated by This Bundle)
117
+
118
+ Per the H5P spec, only whitelisted file types are allowed in `.h5p` archives. This is a runtime/extraction concern rather than a JSON schema concern. For reference:
119
+
120
+ **Content files**: `.png`, `.jpg`, `.jpeg`, `.gif`, `.bmp`, `.tif`, `.tiff`, `.svg`, `.mp3`, `.wav`, `.m4a`, `.mp4`, `.ogg`, `.webm`, `.pdf`, `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.json`
121
+
122
+ **Library files**: All of the above, plus `.js`, `.css`, `.xml`
123
+
124
+ **Prohibited**: `.html`, `.htm`, any executable format
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conform-ed/contracts",
3
- "version": "0.0.6",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "module": "src/index.ts",
6
6
  "exports": {
@@ -27,10 +27,8 @@
27
27
  "./qti/v2_1": "./src/qti/v2_1/index.ts",
28
28
  "./qti/v2_2": "./src/qti/v2_2/index.ts",
29
29
  "./qti/v3_0_1": "./src/qti/v3_0_1/index.ts",
30
- "./vc-data-model/v2_0": "./src/vc-data-model/v2_0/index.ts"
31
- },
32
- "peerDependencies": {
33
- "zod": ">=4.4.0"
30
+ "./vc-data-model/v2_0": "./src/vc-data-model/v2_0/index.ts",
31
+ "./h5p/v1": "./src/h5p/v1/index.ts"
34
32
  },
35
33
  "scripts": {
36
34
  "build": "tsgo --noEmit",
@@ -41,5 +39,8 @@
41
39
  },
42
40
  "devDependencies": {
43
41
  "fast-xml-parser": "^5.3.1"
42
+ },
43
+ "peerDependencies": {
44
+ "zod": ">=4.4.0"
44
45
  }
45
46
  }
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ import { strictObject } from "./shared";
3
+
4
+ // Copyright information attached to media files in content.json.
5
+ export const H5pCopyrightSchema = z.object({
6
+ license: z.string().optional(),
7
+ author: z.string().optional(),
8
+ year: z.string().optional(),
9
+ source: z.string().optional(),
10
+ title: z.string().optional(),
11
+ version: z.string().optional(),
12
+ });
13
+
14
+ // Media file reference as stored in content.json (image, audio, video, file fields).
15
+ export const H5pMediaFileSchema = strictObject({
16
+ path: z.string().min(1),
17
+ mime: z.string(),
18
+ copyright: H5pCopyrightSchema.optional(),
19
+ // Original filename displayed in the editor
20
+ name: z.string().optional(),
21
+ // Width/height metadata for images
22
+ width: z.number().int().positive().optional(),
23
+ height: z.number().int().positive().optional(),
24
+ });
25
+
26
+ // Embedded H5P library reference as stored in content.json (library-type semantics fields).
27
+ // The library string uses the format "H5P.MachineName majorVersion.minorVersion".
28
+ export const H5pLibraryEmbedSchema = z.object({
29
+ library: z.string().regex(/^[\w0-9-.]+ \d+\.\d+$/u),
30
+ params: z.unknown(),
31
+ subContentId: z.string().optional(),
32
+ metadata: z.unknown().optional(),
33
+ });
34
+
35
+ // Base schema for any content.json document.
36
+ // Content structure is defined per-library by semantics.json and therefore varies widely.
37
+ // Full validation against semantics requires a semantics traversal engine — see notes in
38
+ // h5p-v1-zod-templates.md for guidance on building one on top of H5pSemanticsSchema.
39
+ export const H5pContentParamsSchema = z.record(z.string(), z.unknown());
40
+
41
+ export type H5pCopyright = z.infer<typeof H5pCopyrightSchema>;
42
+ export type H5pMediaFile = z.infer<typeof H5pMediaFileSchema>;
43
+ export type H5pLibraryEmbed = z.infer<typeof H5pLibraryEmbedSchema>;
44
+ export type H5pContentParams = z.infer<typeof H5pContentParamsSchema>;
@@ -0,0 +1,67 @@
1
+ import { z } from "zod";
2
+ import {
3
+ strictObject,
4
+ H5pMachineNameSchema,
5
+ H5pVersionRefSchema,
6
+ H5pLicenseSchema,
7
+ H5pAuthorSchema,
8
+ H5pChangelogEntrySchema,
9
+ } from "./shared";
10
+
11
+ // h5p.json — the top-level package manifest inside every .h5p archive.
12
+ // Schema derived from H5PValidator::isValidRequiredH5pData in h5p-php-library.
13
+ export const H5pPackageManifestSchema = strictObject({
14
+ // Required fields
15
+ title: z.string().min(1).max(255),
16
+ // ISO-639-1 language code with optional region subtag (e.g. "en", "nb", "zh-CN")
17
+ language: z.string().regex(/^[-a-zA-Z]{1,10}$/u),
18
+ machineName: H5pMachineNameSchema,
19
+ mainLibrary: H5pMachineNameSchema,
20
+ preloadedDependencies: z.array(H5pVersionRefSchema).min(1),
21
+ embedTypes: z.array(z.enum(["iframe", "div"])).min(1),
22
+
23
+ // Optional metadata
24
+ contentType: z.string().optional(),
25
+ description: z.string().optional(),
26
+ author: z.string().optional(),
27
+ authors: z.array(H5pAuthorSchema).optional(),
28
+ source: z.string().url().optional(),
29
+ license: H5pLicenseSchema.optional(),
30
+ licenseVersion: z.string().max(10).optional(),
31
+ licenseExtras: z.string().max(5000).optional(),
32
+ authorComments: z.string().max(5000).optional(),
33
+ dynamicDependencies: z.array(H5pVersionRefSchema).optional(),
34
+ // Fixed dimensions for non-responsive content — 1–4 digits per spec
35
+ width: z.number().int().min(1).max(9999).optional(),
36
+ height: z.number().int().min(1).max(9999).optional(),
37
+ metaKeywords: z.string().optional(),
38
+ metaDescription: z.string().optional(),
39
+ yearFrom: z.number().int().optional(),
40
+ yearTo: z.number().int().optional(),
41
+ defaultLanguage: z.string().max(32).optional(),
42
+ a11yTitle: z.string().max(255).optional(),
43
+ changes: z.array(H5pChangelogEntrySchema).optional(),
44
+ }).superRefine((data, ctx) => {
45
+ // embedTypes must not have duplicates
46
+ const seen = new Set<string>();
47
+ for (const et of data.embedTypes) {
48
+ if (seen.has(et)) {
49
+ ctx.addIssue({
50
+ code: z.ZodIssueCode.custom,
51
+ path: ["embedTypes"],
52
+ message: `Duplicate embed type: "${et}"`,
53
+ });
54
+ }
55
+ seen.add(et);
56
+ }
57
+ // yearTo must be >= yearFrom when both are present
58
+ if (data.yearFrom !== undefined && data.yearTo !== undefined && data.yearTo < data.yearFrom) {
59
+ ctx.addIssue({
60
+ code: z.ZodIssueCode.custom,
61
+ path: ["yearTo"],
62
+ message: "yearTo must be >= yearFrom",
63
+ });
64
+ }
65
+ });
66
+
67
+ export type H5pPackageManifest = z.infer<typeof H5pPackageManifestSchema>;