@rathnasgala2/schemas 2.11.0 → 2.13.0

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 (408) hide show
  1. package/README.md +192 -84
  2. package/THIRD_PARTY_LICENSES.json +79 -106
  3. package/diagnostics/diagnostic-map.json +443 -405
  4. package/diagnostics/diagnostic-map.public-runtime-origins.json +5 -0
  5. package/docs/catalogs/app-routes.json +2 -1
  6. package/docs/catalogs/schema-inventory.json +33 -54
  7. package/examples/valid/adapter-capability/rules.json +11 -0
  8. package/examples/valid/artifact-manifest/rules.json +156 -177
  9. package/examples/valid/build-input/rules.json +67 -63
  10. package/examples/valid/build-provenance/rules.json +151 -172
  11. package/examples/valid/deployment-intent/rules.json +152 -148
  12. package/examples/valid/deployment-observation/rules.json +24 -20
  13. package/examples/valid/deployment-receipt/rules.json +152 -148
  14. package/examples/valid/navigation/rules.json +2 -4
  15. package/examples/valid/theme-contract/rules.json +4 -1
  16. package/fixtures/boundary/adapter-capability/rules.json +16 -0
  17. package/fixtures/boundary/artifact-manifest/rules.json +195 -202
  18. package/fixtures/boundary/build-input/rules.json +99 -86
  19. package/fixtures/boundary/build-provenance/rules.json +193 -202
  20. package/fixtures/boundary/deployment-intent/rules.json +186 -140
  21. package/fixtures/boundary/deployment-observation/rules.json +68 -18
  22. package/fixtures/boundary/deployment-receipt/rules.json +186 -140
  23. package/fixtures/boundary/navigation/rules.json +2 -17
  24. package/fixtures/boundary/theme-contract/rules.json +34 -0
  25. package/fixtures/invalid/adapter-capability/SCHEMA_STRING_TOO_LONG/cases.json +11 -0
  26. package/fixtures/invalid/adapter-capability/UTF8_BYTE_LENGTH_INVALID/cases.json +11 -0
  27. package/fixtures/invalid/artifact-manifest/ASCII_BYTE_LENGTH_INVALID/cases.json +13 -0
  28. package/fixtures/invalid/artifact-manifest/INTEGER_RANGE_INVALID/cases.json +24 -0
  29. package/fixtures/invalid/artifact-manifest/REQUEST_FIELD_UNKNOWN/cases.json +19 -19
  30. package/fixtures/invalid/artifact-manifest/SCHEMA_CONSTANT_INVALID/cases.json +48 -48
  31. package/fixtures/invalid/artifact-manifest/SCHEMA_ENUM_INVALID/cases.json +11 -11
  32. package/fixtures/invalid/artifact-manifest/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  33. package/fixtures/invalid/artifact-manifest/SCHEMA_NEGATED_RULE_MATCHED/cases.json +0 -11
  34. package/fixtures/invalid/artifact-manifest/SCHEMA_PATTERN_INVALID/cases.json +0 -22
  35. package/fixtures/invalid/artifact-manifest/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +1105 -1105
  36. package/fixtures/invalid/artifact-manifest/SCHEMA_STRING_TOO_LONG/cases.json +4 -6
  37. package/fixtures/invalid/artifact-manifest/SCHEMA_STRING_TOO_SHORT/cases.json +9 -11
  38. package/fixtures/invalid/artifact-manifest/SCHEMA_TYPE_INVALID/cases.json +22 -31
  39. package/fixtures/invalid/artifact-manifest/UTF8_BYTE_LENGTH_INVALID/cases.json +0 -11
  40. package/fixtures/invalid/build-input/INTEGER_RANGE_INVALID/cases.json +13 -0
  41. package/fixtures/invalid/build-input/REQUEST_FIELD_UNKNOWN/cases.json +32 -32
  42. package/fixtures/invalid/build-input/SCHEMA_CONSTANT_INVALID/cases.json +11 -11
  43. package/fixtures/invalid/build-input/SCHEMA_ENUM_INVALID/cases.json +11 -11
  44. package/fixtures/invalid/build-input/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +78 -78
  45. package/fixtures/invalid/build-input/SCHEMA_TYPE_INVALID/cases.json +51 -18
  46. package/fixtures/invalid/build-provenance/ASCII_BYTE_LENGTH_INVALID/cases.json +13 -0
  47. package/fixtures/invalid/build-provenance/INTEGER_RANGE_INVALID/cases.json +22 -0
  48. package/fixtures/invalid/build-provenance/REQUEST_FIELD_UNKNOWN/cases.json +56 -56
  49. package/fixtures/invalid/build-provenance/SCHEMA_CONSTANT_INVALID/cases.json +37 -37
  50. package/fixtures/invalid/build-provenance/SCHEMA_ENUM_INVALID/cases.json +11 -11
  51. package/fixtures/invalid/build-provenance/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  52. package/fixtures/invalid/build-provenance/SCHEMA_NEGATED_RULE_MATCHED/cases.json +0 -11
  53. package/fixtures/invalid/build-provenance/SCHEMA_PATTERN_INVALID/cases.json +0 -22
  54. package/fixtures/invalid/build-provenance/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +927 -927
  55. package/fixtures/invalid/build-provenance/SCHEMA_STRING_TOO_LONG/cases.json +9 -11
  56. package/fixtures/invalid/build-provenance/SCHEMA_STRING_TOO_SHORT/cases.json +8 -10
  57. package/fixtures/invalid/build-provenance/SCHEMA_TYPE_INVALID/cases.json +20 -25
  58. package/fixtures/invalid/build-provenance/UTF8_BYTE_LENGTH_INVALID/cases.json +0 -11
  59. package/fixtures/invalid/deployment-intent/INTEGER_RANGE_INVALID/cases.json +24 -0
  60. package/fixtures/invalid/deployment-intent/REQUEST_FIELD_UNKNOWN/cases.json +30 -30
  61. package/fixtures/invalid/deployment-intent/SCHEMA_CONSTANT_INVALID/cases.json +41 -41
  62. package/fixtures/invalid/deployment-intent/SCHEMA_ENUM_INVALID/cases.json +11 -11
  63. package/fixtures/invalid/deployment-intent/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  64. package/fixtures/invalid/deployment-intent/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +1209 -1209
  65. package/fixtures/invalid/deployment-intent/SCHEMA_STRING_TOO_LONG/cases.json +9 -0
  66. package/fixtures/invalid/deployment-intent/SCHEMA_STRING_TOO_SHORT/cases.json +9 -0
  67. package/fixtures/invalid/deployment-intent/SCHEMA_TYPE_INVALID/cases.json +22 -20
  68. package/fixtures/invalid/deployment-observation/INTEGER_RANGE_INVALID/cases.json +24 -0
  69. package/fixtures/invalid/deployment-observation/REQUEST_FIELD_UNKNOWN/cases.json +16 -16
  70. package/fixtures/invalid/deployment-observation/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  71. package/fixtures/invalid/deployment-observation/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +39 -39
  72. package/fixtures/invalid/deployment-observation/SCHEMA_STRING_TOO_LONG/cases.json +11 -0
  73. package/fixtures/invalid/deployment-observation/SCHEMA_STRING_TOO_SHORT/cases.json +11 -0
  74. package/fixtures/invalid/deployment-observation/SCHEMA_TYPE_INVALID/cases.json +11 -11
  75. package/fixtures/invalid/deployment-receipt/INTEGER_RANGE_INVALID/cases.json +24 -0
  76. package/fixtures/invalid/deployment-receipt/REQUEST_FIELD_UNKNOWN/cases.json +70 -70
  77. package/fixtures/invalid/deployment-receipt/SCHEMA_CONSTANT_INVALID/cases.json +36 -36
  78. package/fixtures/invalid/deployment-receipt/SCHEMA_ENUM_INVALID/cases.json +11 -11
  79. package/fixtures/invalid/deployment-receipt/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  80. package/fixtures/invalid/deployment-receipt/SCHEMA_REQUIRED_FIELD_MISSING/cases.json +1304 -1304
  81. package/fixtures/invalid/deployment-receipt/SCHEMA_STRING_TOO_LONG/cases.json +9 -0
  82. package/fixtures/invalid/deployment-receipt/SCHEMA_STRING_TOO_SHORT/cases.json +9 -0
  83. package/fixtures/invalid/deployment-receipt/SCHEMA_TYPE_INVALID/cases.json +22 -20
  84. package/fixtures/invalid/navigation/SCHEMA_ARRAY_TOO_LONG/cases.json +0 -15
  85. package/fixtures/invalid/navigation/SCHEMA_CONSTANT_INVALID/cases.json +11 -0
  86. package/fixtures/invalid/navigation/SCHEMA_TYPE_INVALID/cases.json +0 -11
  87. package/fixtures/invalid/theme-contract/INTEGER_RANGE_INVALID/cases.json +11 -0
  88. package/fixtures/invalid/theme-contract/SCHEMA_FORMAT_INVALID/cases.json +0 -9
  89. package/fixtures/invalid/theme-contract/SCHEMA_STRING_TOO_LONG/cases.json +9 -0
  90. package/fixtures/invalid/theme-contract/SCHEMA_STRING_TOO_SHORT/cases.json +9 -0
  91. package/fixtures/manifest.json +3217 -2930
  92. package/fixtures/unknown-field/adapter-capability/rules.json +2 -0
  93. package/fixtures/unknown-field/artifact-manifest/rules.json +111 -114
  94. package/fixtures/unknown-field/build-input/rules.json +36 -32
  95. package/fixtures/unknown-field/build-provenance/rules.json +111 -114
  96. package/fixtures/unknown-field/deployment-intent/rules.json +109 -105
  97. package/fixtures/unknown-field/deployment-observation/rules.json +24 -20
  98. package/fixtures/unknown-field/deployment-receipt/rules.json +109 -105
  99. package/fixtures/unknown-field/navigation/rules.json +1 -3
  100. package/fixtures/unknown-field/theme-contract/rules.json +3 -0
  101. package/generated/browser/runtime-origins-validator-core.d.mts +10 -0
  102. package/generated/browser/runtime-origins-validator-core.mjs +35 -0
  103. package/generated/browser/validator-core.d.mts +10 -0
  104. package/generated/browser/validator-core.mjs +80 -0
  105. package/generated/typescript/contracts/artifact-manifest.d.ts +49 -48
  106. package/generated/typescript/contracts/build-input.d.ts +15 -15
  107. package/generated/typescript/contracts/build-provenance.d.ts +51 -50
  108. package/generated/typescript/contracts/deployment-intent.d.ts +6 -6
  109. package/generated/typescript/contracts/deployment-observation.d.ts +1 -1
  110. package/generated/typescript/contracts/deployment-receipt.d.ts +5 -5
  111. package/generated/typescript/contracts/navigation.d.ts +1 -1
  112. package/generated/typescript/index.js +11 -37
  113. package/openapi/http-catalog.json +14 -2
  114. package/openapi/openapi.yaml +527 -13
  115. package/package.json +19 -16
  116. package/sbom.cdx.json +23 -23
  117. package/schemas/adapter-capability.schema.json +5 -1
  118. package/schemas/appearance.schema.json +1 -1
  119. package/schemas/artifact-manifest.schema.json +15 -18
  120. package/schemas/build-input.schema.json +18 -11
  121. package/schemas/build-provenance.schema.json +15 -18
  122. package/schemas/deployment-intent.schema.json +13 -8
  123. package/schemas/deployment-observation.schema.json +10 -5
  124. package/schemas/deployment-receipt.schema.json +12 -7
  125. package/schemas/navigation.schema.json +5 -26
  126. package/schemas/theme-contract.schema.json +4 -2
  127. package/src/index.js +2 -2
  128. package/src/internal/browser-schema-validator.js +43 -0
  129. package/src/internal/format-validators.js +1 -1
  130. package/src/internal/gala-keywords.js +148 -0
  131. package/src/internal/validator-core.js +244 -108
  132. package/src/runtime-origins.js +18 -18
  133. package/types/index.d.ts +2 -2
  134. package/types/internal/browser-schema-validator.d.ts +16 -0
  135. package/types/internal/format-validators.d.ts +1 -1
  136. package/types/internal/gala-keywords.d.ts +68 -0
  137. package/types/internal/validator-core.d.ts +80 -1
  138. package/compatibility/compatibility.json +0 -594
  139. package/generated/java/src/main/java/io/gala/schema/generated/GalaSchemaRegistry.java +0 -67
  140. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityAdapterConfigurationCapabilities.java +0 -24
  141. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityAdapterIdentity.java +0 -20
  142. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityCapabilityDecision.java +0 -62
  143. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityCredentialEgressProfile.java +0 -35
  144. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityDestinationIdentity.java +0 -25
  145. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityDestinationProviderCoordinates.java +0 -19
  146. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityDocument.java +0 -52
  147. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityFilesystemProviderLimits.java +0 -32
  148. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityGithubActionsOidcOriginCatalog.java +0 -25
  149. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityHttpProviderLimits.java +0 -72
  150. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemAllowlist.java +0 -21
  151. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemAllowlistEntry.java +0 -28
  152. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemCapabilityEvidence.java +0 -42
  153. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemControlPublication.java +0 -26
  154. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemControlRow.java +0 -44
  155. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemFailureEvidence.java +0 -40
  156. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemObservationEvidence.java +0 -40
  157. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemPlatform.java +0 -25
  158. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemProbeSummary.java +0 -32
  159. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemProbeTranscript.java +0 -25
  160. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemReplacement.java +0 -22
  161. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityLocalFilesystemSurfaceIdentity.java +0 -20
  162. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityManagedExecutionBudget.java +0 -26
  163. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityPagesOidcCredentialEgress.java +0 -30
  164. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderCallClassBinding.java +0 -22
  165. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderCredentialEgress.java +0 -26
  166. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderCredentialHeader.java +0 -24
  167. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderDerivedHeader.java +0 -18
  168. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderFixedHeader.java +0 -18
  169. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilityProviderRequestTemplate.java +0 -39
  170. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilitySpacesControlPlaneBinding.java +0 -32
  171. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilitySpacesControlPlaneRequest.java +0 -26
  172. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilitySpacesControlPlaneRequestCatalog.java +0 -31
  173. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilitySpacesControlPlaneResponseCatalog.java +0 -20
  174. package/generated/java/src/main/java/io/gala/schema/generated/model/AdapterCapabilitySpacesWebsiteConfiguration.java +0 -24
  175. package/generated/java/src/main/java/io/gala/schema/generated/model/AppearanceColorMode.java +0 -19
  176. package/generated/java/src/main/java/io/gala/schema/generated/model/AppearanceDocument.java +0 -37
  177. package/generated/java/src/main/java/io/gala/schema/generated/model/AppearanceExtensionMap.java +0 -15
  178. package/generated/java/src/main/java/io/gala/schema/generated/model/AppearanceSemanticTokens.java +0 -15
  179. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestActionPinEvidence.java +0 -20
  180. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestArtifactLicenseConclusion.java +0 -21
  181. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestAssertedWorkload.java +0 -44
  182. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestBuildSandboxEvidence.java +0 -38
  183. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestDocument.java +0 -73
  184. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestFinding.java +0 -22
  185. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestAsset.java +0 -24
  186. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestCompositionIdentity.java +0 -25
  187. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestExcludedInput.java +0 -22
  188. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestIncludedSource.java +0 -22
  189. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestRedirect.java +0 -24
  190. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestRoute.java +0 -28
  191. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestSourceIdentity.java +0 -26
  192. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestManifestValidationEvidence.java +0 -22
  193. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestMeasurement.java +0 -20
  194. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestPackageIdentity.java +0 -22
  195. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestRenderPolicyIdentity.java +0 -20
  196. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestReproducibleBuildRecord.java +0 -56
  197. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestWorkflowCarrierEvidence.java +0 -26
  198. package/generated/java/src/main/java/io/gala/schema/generated/model/ArtifactManifestWorkflowFileEvidence.java +0 -26
  199. package/generated/java/src/main/java/io/gala/schema/generated/model/AuthorDocument.java +0 -33
  200. package/generated/java/src/main/java/io/gala/schema/generated/model/AuthorExtensionMap.java +0 -15
  201. package/generated/java/src/main/java/io/gala/schema/generated/model/AuthorLocalizedAuthor.java +0 -20
  202. package/generated/java/src/main/java/io/gala/schema/generated/model/AuthorSocialLink.java +0 -19
  203. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputAdapterIdentity.java +0 -20
  204. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputAppearanceNormalized.java +0 -33
  205. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputAuthorNormalized.java +0 -31
  206. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputBuildPackages.java +0 -25
  207. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputColorMode.java +0 -19
  208. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputContentBuildRecord.java +0 -33
  209. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputContentFrontmatterNormalized.java +0 -44
  210. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputDestinationCapabilityProfile.java +0 -20
  211. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputDocument.java +0 -51
  212. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputLocalizedAuthor.java +0 -20
  213. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputModuleBuildSelection.java +0 -15
  214. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputNavigationItem.java +0 -23
  215. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputNavigationLeaf.java +0 -22
  216. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputNavigationNormalized.java +0 -21
  217. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputPackageIdentity.java +0 -22
  218. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputPublicationNormalized.java +0 -39
  219. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputRenderPolicyIdentity.java +0 -20
  220. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputRenderableBody.java +0 -26
  221. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputRepositorySnapshot.java +0 -22
  222. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputResolvedFile.java +0 -18
  223. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputResolvedMedia.java +0 -20
  224. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputSemanticTokens.java +0 -15
  225. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildInputSocialLink.java +0 -19
  226. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceActionPinEvidence.java +0 -20
  227. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceArtifactLicenseConclusion.java +0 -21
  228. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceAssertedWorkload.java +0 -44
  229. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceBuildSandboxEvidence.java +0 -38
  230. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceDocument.java +0 -69
  231. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceFinding.java +0 -22
  232. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestAsset.java +0 -24
  233. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestCompositionIdentity.java +0 -25
  234. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestExcludedInput.java +0 -22
  235. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestIncludedSource.java +0 -22
  236. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestRedirect.java +0 -24
  237. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestRoute.java +0 -28
  238. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestSourceIdentity.java +0 -26
  239. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceManifestValidationEvidence.java +0 -22
  240. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceMeasurement.java +0 -20
  241. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenancePackageIdentity.java +0 -22
  242. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceRenderPolicyIdentity.java +0 -20
  243. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceReproducibleBuildRecord.java +0 -56
  244. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceWorkflowCarrierEvidence.java +0 -26
  245. package/generated/java/src/main/java/io/gala/schema/generated/model/BuildProvenanceWorkflowFileEvidence.java +0 -26
  246. package/generated/java/src/main/java/io/gala/schema/generated/model/ContentFrontmatterDocument.java +0 -50
  247. package/generated/java/src/main/java/io/gala/schema/generated/model/ContentFrontmatterExtensionMap.java +0 -15
  248. package/generated/java/src/main/java/io/gala/schema/generated/model/ContentFrontmatterMediaRef.java +0 -20
  249. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentActivationBasis.java +0 -20
  250. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentActivationDetectionObservation.java +0 -33
  251. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentActivationDetectionPlan.java +0 -44
  252. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentActivationDetectionProbe.java +0 -52
  253. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentAdapterIdentity.java +0 -20
  254. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentCancellationFinalizationEvidence.java +0 -41
  255. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDeadlineFinalizationEvidence.java +0 -39
  256. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDeadlineFinalizationStream.java +0 -22
  257. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDeploymentPolicyDecision.java +0 -75
  258. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDestinationIdentity.java +0 -25
  259. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDestinationMutationAuthority.java +0 -36
  260. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDestinationProviderCoordinates.java +0 -19
  261. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentDocument.java +0 -136
  262. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentExpectedRedirectHop.java +0 -23
  263. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentObservedVerificationHeader.java +0 -19
  264. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPackageIdentity.java +0 -22
  265. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPagesInterveningRunAttempt.java +0 -20
  266. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPagesReconciliationCommand.java +0 -48
  267. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPagesReconciliationRecovery.java +0 -41
  268. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPagesRunAttemptGapProof.java +0 -27
  269. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPublicGenerationMarkerPayload.java +0 -24
  270. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentPublicProbeObservation.java +0 -54
  271. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentRecognizedPriorRouteContract.java +0 -30
  272. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentRenderPolicyIdentity.java +0 -20
  273. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentReproducibleBuildRecord.java +0 -56
  274. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentSupersessionFinalizationEvidence.java +0 -45
  275. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentVerificationHeaderExpectation.java +0 -18
  276. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentIntentVerificationPlanTarget.java +0 -52
  277. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationActivationDetectionObservation.java +0 -33
  278. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationActivationDetectionProbe.java +0 -52
  279. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationAdapterIdentity.java +0 -20
  280. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationCancellationFinalizationEvidence.java +0 -41
  281. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationDeadlineFinalizationEvidence.java +0 -39
  282. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationDeadlineFinalizationStream.java +0 -22
  283. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationDestinationIdentity.java +0 -25
  284. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationDestinationProviderCoordinates.java +0 -19
  285. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationDocument.java +0 -57
  286. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationExpectedRedirectHop.java +0 -23
  287. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationObservedVerificationHeader.java +0 -19
  288. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationPackageIdentity.java +0 -22
  289. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationPublicProbeObservation.java +0 -54
  290. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationRecognizedPriorRouteContract.java +0 -30
  291. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationRenderPolicyIdentity.java +0 -20
  292. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationSupersessionFinalizationEvidence.java +0 -45
  293. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationVerificationHeaderExpectation.java +0 -18
  294. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentObservationVerificationPlanTarget.java +0 -52
  295. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptActivationBasis.java +0 -20
  296. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptActivationDetectionObservation.java +0 -33
  297. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptActivationDetectionPlan.java +0 -44
  298. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptActivationDetectionProbe.java +0 -52
  299. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptAdapterIdentity.java +0 -20
  300. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptCancellationFinalizationEvidence.java +0 -41
  301. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeadlineFinalizationEvidence.java +0 -39
  302. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeadlineFinalizationStream.java +0 -22
  303. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentAttempt.java +0 -38
  304. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentFailure.java +0 -24
  305. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentObservation.java +0 -53
  306. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentPolicyDecision.java +0 -75
  307. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentStageEvidence.java +0 -36
  308. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentStageInput.java +0 -26
  309. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentStageObservationEvidenceReference.java +0 -18
  310. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDeploymentStageResult.java +0 -31
  311. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDestinationIdentity.java +0 -25
  312. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDestinationMutationAuthority.java +0 -36
  313. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDestinationProviderCoordinates.java +0 -19
  314. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptDocument.java +0 -105
  315. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptExpectedRedirectHop.java +0 -23
  316. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptFinding.java +0 -22
  317. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptObservedVerificationHeader.java +0 -19
  318. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPackageIdentity.java +0 -22
  319. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPagesInterveningRunAttempt.java +0 -20
  320. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPagesReconciliationCommand.java +0 -48
  321. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPagesReconciliationRecovery.java +0 -41
  322. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPagesRunAttemptGapProof.java +0 -27
  323. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPublicGenerationMarkerPayload.java +0 -24
  324. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptPublicProbeObservation.java +0 -54
  325. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptRecognizedPriorRouteContract.java +0 -30
  326. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptRenderPolicyIdentity.java +0 -20
  327. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptReproducibleBuildRecord.java +0 -56
  328. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptSupersessionFinalizationEvidence.java +0 -45
  329. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptVerificationHeaderExpectation.java +0 -18
  330. package/generated/java/src/main/java/io/gala/schema/generated/model/DeploymentReceiptVerificationPlanTarget.java +0 -52
  331. package/generated/java/src/main/java/io/gala/schema/generated/model/EventEnvelopeDocument.java +0 -46
  332. package/generated/java/src/main/java/io/gala/schema/generated/model/EventEnvelopeRegisteredEventPayload.java +0 -26
  333. package/generated/java/src/main/java/io/gala/schema/generated/model/LockDependencyEdge.java +0 -18
  334. package/generated/java/src/main/java/io/gala/schema/generated/model/LockDocument.java +0 -39
  335. package/generated/java/src/main/java/io/gala/schema/generated/model/LockLockedPackage.java +0 -26
  336. package/generated/java/src/main/java/io/gala/schema/generated/model/LockLockedTemplate.java +0 -28
  337. package/generated/java/src/main/java/io/gala/schema/generated/model/LockPackageIdentity.java +0 -22
  338. package/generated/java/src/main/java/io/gala/schema/generated/model/NavigationDocument.java +0 -25
  339. package/generated/java/src/main/java/io/gala/schema/generated/model/NavigationExtensionMap.java +0 -15
  340. package/generated/java/src/main/java/io/gala/schema/generated/model/NavigationNavigationItem.java +0 -23
  341. package/generated/java/src/main/java/io/gala/schema/generated/model/NavigationNavigationLeaf.java +0 -23
  342. package/generated/java/src/main/java/io/gala/schema/generated/model/ProblemDocument.java +0 -37
  343. package/generated/java/src/main/java/io/gala/schema/generated/model/ProblemFieldError.java +0 -18
  344. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicGenerationMarkerDocument.java +0 -24
  345. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicRuntimeOriginsDocument.java +0 -44
  346. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicationDocument.java +0 -41
  347. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicationExtensionMap.java +0 -15
  348. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicationFooterCard.java +0 -23
  349. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicationPublicationProfile.java +0 -18
  350. package/generated/java/src/main/java/io/gala/schema/generated/model/PublicationSocialLink.java +0 -19
  351. package/generated/java/src/main/java/io/gala/schema/generated/model/RepositoryAssetRoot.java +0 -16
  352. package/generated/java/src/main/java/io/gala/schema/generated/model/RepositoryContentRoot.java +0 -23
  353. package/generated/java/src/main/java/io/gala/schema/generated/model/RepositoryDocument.java +0 -41
  354. package/generated/java/src/main/java/io/gala/schema/generated/model/RepositoryExtensionMap.java +0 -15
  355. package/generated/java/src/main/java/io/gala/schema/generated/model/RepositoryGeneratedRoot.java +0 -20
  356. package/generated/java/src/main/java/io/gala/schema/generated/model/TemplateCompositionDocument.java +0 -18
  357. package/generated/java/src/main/java/io/gala/schema/generated/model/ThemeContractDocument.java +0 -53
  358. package/generated/java/src/main/java/io/gala/schema/generated/model/ThemeContractPassiveAsset.java +0 -24
  359. package/generated/java/src/main/java/io/gala/schema/generated/model/ThemeContractThemeBudgets.java +0 -20
  360. package/generated/java/src/main/java/io/gala/schema/generated/model/ThemeContractThemeToken.java +0 -22
  361. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/adapter-capability.schema.json +0 -2689
  362. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/appearance.schema.json +0 -226
  363. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/artifact-manifest.schema.json +0 -1063
  364. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/author.schema.json +0 -231
  365. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/build-input.schema.json +0 -866
  366. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/build-provenance.schema.json +0 -937
  367. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/content-frontmatter.schema.json +0 -241
  368. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/deployment-intent.schema.json +0 -2067
  369. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/deployment-observation.schema.json +0 -1654
  370. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/deployment-receipt.schema.json +0 -5546
  371. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/event-envelope.schema.json +0 -195
  372. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/lock.schema.json +0 -326
  373. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/navigation.schema.json +0 -229
  374. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/problem.schema.json +0 -194
  375. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/public-generation-marker.schema.json +0 -142
  376. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/public-runtime-origins.schema.json +0 -183
  377. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/publication.schema.json +0 -248
  378. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/repository.schema.json +0 -219
  379. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/template-composition.schema.json +0 -133
  380. package/generated/java/src/main/resources/io/gala/schema/generated/schemas/theme-contract.schema.json +0 -850
  381. package/generated/typescript/validator-core.cjs +0 -17
  382. package/openapi/generator/spring.json +0 -22
  383. package/openapi/generator/typescript-fetch.json +0 -19
  384. package/openapi/source/authentication.yaml +0 -557
  385. package/openapi/source/callbacks.yaml +0 -332
  386. package/openapi/source/components.yaml +0 -5670
  387. package/openapi/source/github.yaml +0 -351
  388. package/openapi/source/internal.yaml +0 -71
  389. package/openapi/source/membership-invitations.yaml +0 -203
  390. package/openapi/source/organizations.yaml +0 -11144
  391. package/openapi/source/root.yaml +0 -20
  392. package/openapi/source/self.yaml +0 -1940
  393. package/openapi/source/session.yaml +0 -308
  394. package/openapi/source/workloads.yaml +0 -744
  395. package/src/internal/build-artifact-semantics.js +0 -4391
  396. package/src/internal/deployment-record-semantics.js +0 -7567
  397. package/src/internal/deployment-stage-semantics.js +0 -1207
  398. package/src/internal/http-problem-contract.js +0 -35
  399. package/src/internal/managed-evidence-journal.js +0 -160
  400. package/src/internal/public-runtime-origins.js +0 -143
  401. package/src/internal/theme-composition-semantics.js +0 -2441
  402. package/types/internal/build-artifact-semantics.d.ts +0 -77
  403. package/types/internal/deployment-record-semantics.d.ts +0 -313
  404. package/types/internal/deployment-stage-semantics.d.ts +0 -108
  405. package/types/internal/http-problem-contract.d.ts +0 -4
  406. package/types/internal/managed-evidence-journal.d.ts +0 -26
  407. package/types/internal/public-runtime-origins.d.ts +0 -22
  408. package/types/internal/theme-composition-semantics.d.ts +0 -143
@@ -1,2441 +0,0 @@
1
- import {
2
- canonicalizeJcs,
3
- canonicalizeJcsBytes,
4
- parseDuplicateFreeIJson,
5
- sha256Tagged,
6
- } from './canonical-jcs.js';
7
- import { ACTIVE_DIGEST_PROFILES } from './digest-profiles.js';
8
- import {
9
- parseSemver,
10
- parseSemverRange,
11
- satisfiesSemverRange,
12
- SemanticValidationError,
13
- } from './semver.js';
14
- import { validateSpdxCatalogEvidence, validateSpdxExpression } from './spdx.js';
15
- import unicodeData from './generated/unicode17.json' with { type: 'json' };
16
- import {
17
- assertUnicodeScalarString,
18
- graphemeLength17,
19
- normalizeNfc17,
20
- unicodeCollisionKey17,
21
- } from './unicode17.js';
22
-
23
- const FATAL_UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
24
- const DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/u;
25
- const STABLE_ID_PATTERN =
26
- /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
27
- const POSITIVE_INT64_PATTERN = /^[1-9][0-9]*$/u;
28
- const NONNEGATIVE_INT64_PATTERN = /^(?:0|[1-9][0-9]*)$/u;
29
- const INT64_MAXIMUM = 9_223_372_036_854_775_807n;
30
- const STYLE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
31
- const COLOR_PATTERN = /^#[0-9a-f]{6}(?:[0-9a-f]{2})?$/u;
32
- const LENGTH_PATTERN =
33
- /^(?:0|(?:[1-9][0-9]*(?:\.[0-9]{0,3}[1-9])?|0\.(?:[0-9]{0,3}[1-9]))(?:px|rem))$/u;
34
- const FONT_FAMILY_PATTERN =
35
- /^[A-Za-z][A-Za-z0-9]*(?:[ -][A-Za-z0-9]+)*(?:, [A-Za-z][A-Za-z0-9]*(?:[ -][A-Za-z0-9]+)*){0,7}$/u;
36
- const FONT_WEIGHT_PATTERN = /^[1-9]00$/u;
37
- const PACKAGE_NAME_PATTERN =
38
- /^(?:[a-z0-9][a-z0-9._-]*|@[a-z0-9][a-z0-9._-]*\/[a-z0-9][a-z0-9._-]*)$/u;
39
-
40
- const TEMPLATE_COMPOSITION_KEYS = ['schemaId', 'schemaVersion'];
41
- const THEME_KEYS = [
42
- 'schemaId',
43
- 'schemaVersion',
44
- 'themeId',
45
- 'package',
46
- 'contractVersion',
47
- 'templateRange',
48
- 'stylesheets',
49
- 'cssLayers',
50
- 'slotHooks',
51
- 'tokens',
52
- 'modes',
53
- 'assets',
54
- 'fixtures',
55
- 'browserPolicyRef',
56
- 'integrity',
57
- 'budgets',
58
- 'fixtureDigest',
59
- 'evidenceDigest',
60
- 'stylingContractDigest',
61
- ];
62
- const TOKEN_KEYS = ['key', 'type', 'light', 'dark'];
63
- const ASSET_KEYS = ['path', 'mediaType', 'byteLength', 'sha256', 'license'];
64
- const BUDGET_KEYS = ['maximumFileBytes', 'maximumTotalBytes', 'maximumFiles'];
65
- const PACKAGE_FILE_KEYS = ['path', 'kind', 'mode', 'bytes'];
66
- const PACKAGE_JSON_KEYS = ['name', 'version', 'license', 'files'];
67
- const LICENSE_EVIDENCE_KEYS = [
68
- 'profile',
69
- 'licenseListVersion',
70
- 'licenseListDigest',
71
- 'packageExpression',
72
- 'assetOverrides',
73
- 'catalogEntries',
74
- ];
75
- const LICENSE_OVERRIDE_KEYS = ['path', 'expression'];
76
-
77
- const STYLING_CONTRACT_KEYS = [
78
- 'profile',
79
- 'contractVersion',
80
- 'templatePackage',
81
- 'templateVersion',
82
- 'orderedLayers',
83
- 'stylesheetLayers',
84
- 'publicationRootSelector',
85
- 'resolvedPaletteSelectors',
86
- 'typeSelectors',
87
- 'classSelectors',
88
- 'idSelectors',
89
- 'attributes',
90
- 'pseudoClasses',
91
- 'pseudoElements',
92
- 'functionalPseudos',
93
- 'combinators',
94
- 'publicThemeSlotHooks',
95
- 'composition',
96
- 'catalogDigest',
97
- ];
98
- const STYLE_ATTRIBUTE_KEYS = ['name', 'match', 'values', 'role'];
99
- const STYLE_HOOK_KEYS = ['hookId', 'selectorAtom'];
100
- const STYLESHEET_LAYER_KEYS = ['tokens', 'components', 'utilities', 'print'];
101
- const PALETTE_SELECTOR_KEYS = ['light', 'dark'];
102
- const COMPOSITION_KEYS = [
103
- 'rootScope',
104
- 'compoundOrder',
105
- 'functionalSelectorArguments',
106
- 'maximumFunctionalDepth',
107
- 'nthExpressionProfile',
108
- ];
109
-
110
- const FIXTURE_RELEASE_KEYS = [
111
- 'profile',
112
- 'fixtureReleaseId',
113
- 'contractVersion',
114
- 'browserPolicyRef',
115
- 'binaryAssetProfile',
116
- 'stylingContractDigest',
117
- 'runners',
118
- 'fixtures',
119
- 'fixtureDigest',
120
- ];
121
- const FIXTURE_RUNNER_KEYS = ['runnerId', 'version', 'executableDigest'];
122
- const FIXTURE_DEFINITION_KEYS = [
123
- 'fixtureId',
124
- 'runnerId',
125
- 'inputDigest',
126
- 'expectedDisposition',
127
- 'expectedEvidenceDigest',
128
- ];
129
- const CONFORMANCE_RESULT_KEYS = [
130
- 'profile',
131
- 'fixtureReleaseId',
132
- 'fixtureDigest',
133
- 'themeConformanceInputDigest',
134
- 'results',
135
- 'overallState',
136
- 'evidenceDigest',
137
- ];
138
- const FIXTURE_RESULT_KEYS = [
139
- 'fixtureId',
140
- 'runnerId',
141
- 'observedDisposition',
142
- 'observedEvidenceDigest',
143
- 'state',
144
- ];
145
-
146
- const CSS_EVIDENCE_BASE_KEYS = [
147
- 'path',
148
- 'profile',
149
- 'disposition',
150
- 'byteLength',
151
- 'sha256',
152
- 'outerLayer',
153
- 'usedSlotHookIds',
154
- 'referencedAssetPaths',
155
- 'fontFaceAssetPaths',
156
- 'metrics',
157
- ];
158
- const CSS_METRIC_KEYS = [
159
- 'tokens',
160
- 'qualifiedRules',
161
- 'declarations',
162
- 'localUrls',
163
- 'fontFaces',
164
- 'pageRules',
165
- 'mediaRules',
166
- 'maximumBlockFunctionDepth',
167
- 'commentBytes',
168
- ];
169
- const TOKEN_DECLARATION_KEYS = [
170
- 'lightSelector',
171
- 'darkSelector',
172
- 'light',
173
- 'dark',
174
- ];
175
- const PASSIVE_EVIDENCE_KEYS = [
176
- 'path',
177
- 'mediaType',
178
- 'profile',
179
- 'disposition',
180
- 'byteLength',
181
- 'sha256',
182
- ];
183
- const RUNNER_EVIDENCE_KEYS = ['runner', 'records'];
184
-
185
- const THEME_TOKEN_CATALOG = Object.freeze([
186
- ['border-width', 'length'],
187
- ['color-accent', 'color'],
188
- ['color-border', 'color'],
189
- ['color-canvas', 'color'],
190
- ['color-code-canvas', 'color'],
191
- ['color-code-text', 'color'],
192
- ['color-danger', 'color'],
193
- ['color-focus', 'color'],
194
- ['color-link', 'color'],
195
- ['color-link-visited', 'color'],
196
- ['color-on-accent', 'color'],
197
- ['color-selection', 'color'],
198
- ['color-success', 'color'],
199
- ['color-surface', 'color'],
200
- ['color-surface-raised', 'color'],
201
- ['color-text', 'color'],
202
- ['color-text-muted', 'color'],
203
- ['color-warning', 'color'],
204
- ['content-measure', 'length'],
205
- ['focus-width', 'length'],
206
- ['font-body', 'font-family'],
207
- ['font-heading', 'font-family'],
208
- ['font-mono', 'font-family'],
209
- ['radius-medium', 'length'],
210
- ['radius-small', 'length'],
211
- ['space-1', 'length'],
212
- ['space-2', 'length'],
213
- ['space-3', 'length'],
214
- ['space-4', 'length'],
215
- ['space-6', 'length'],
216
- ['space-8', 'length'],
217
- ['weight-heading', 'font-weight'],
218
- ['weight-medium', 'font-weight'],
219
- ['weight-normal', 'font-weight'],
220
- ['weight-strong', 'font-weight'],
221
- ]);
222
-
223
- const THEME_IDENTITIES = Object.freeze(
224
- Object.fromEntries(
225
- ['default', 'amaze', 'flashy', 'minimal', 'zebra'].map((themeId) => [
226
- themeId,
227
- `@rathnasgala2/theme-${themeId}`,
228
- ]),
229
- ),
230
- );
231
- const STYLESHEET_VARIANTS = Object.freeze([
232
- Object.freeze({
233
- paths: Object.freeze(['tokens.css', 'components.css', 'print.css']),
234
- layers: Object.freeze(['gala-tokens', 'gala-components', 'gala-print']),
235
- }),
236
- Object.freeze({
237
- paths: Object.freeze([
238
- 'tokens.css',
239
- 'components.css',
240
- 'utilities.css',
241
- 'print.css',
242
- ]),
243
- layers: Object.freeze([
244
- 'gala-tokens',
245
- 'gala-components',
246
- 'gala-utilities',
247
- 'gala-print',
248
- ]),
249
- }),
250
- ]);
251
- const MODES = Object.freeze(['dark', 'light', 'system']);
252
- const ORDERED_LAYERS = Object.freeze([
253
- 'gala-tokens',
254
- 'gala-components',
255
- 'gala-utilities',
256
- 'gala-print',
257
- ]);
258
- const FIXTURE_RUNNER_IDS = Object.freeze([
259
- 'schema',
260
- 'semantic',
261
- 'package',
262
- 'css',
263
- 'binary',
264
- 'browser',
265
- 'a11y',
266
- 'absence',
267
- ]);
268
- /** @type {Readonly<Record<string, string>>} */
269
- const MEDIA_BY_EXTENSION = Object.freeze({
270
- '.woff2': 'font/woff2',
271
- '.png': 'image/png',
272
- '.jpg': 'image/jpeg',
273
- '.webp': 'image/webp',
274
- '.avif': 'image/avif',
275
- '.svg': 'image/svg+xml',
276
- });
277
- /** @type {Readonly<Record<string, number>>} */
278
- const PASSIVE_SOURCE_LIMITS = Object.freeze({
279
- 'font/woff2': 4_194_304,
280
- 'image/png': 16_777_216,
281
- 'image/jpeg': 16_777_216,
282
- 'image/webp': 16_777_216,
283
- 'image/avif': 16_777_216,
284
- 'image/svg+xml': 262_144,
285
- });
286
- /** @type {Readonly<Record<string, number>>} */
287
- const CSS_METRIC_LIMITS = Object.freeze({
288
- tokens: 32_768,
289
- qualifiedRules: 4_096,
290
- declarations: 32_768,
291
- localUrls: 256,
292
- fontFaces: 128,
293
- pageRules: 64,
294
- mediaRules: 512,
295
- maximumBlockFunctionDepth: 16,
296
- commentBytes: 1_048_576,
297
- });
298
-
299
- /** @typedef {[number, number, string]} UnicodePropertyRange */
300
-
301
- const portableUnicodeTable =
302
- /** @type {{ version: string, whiteSpace: UnicodePropertyRange[], noncharacter: UnicodePropertyRange[], defaultIgnorable: UnicodePropertyRange[] }} */ (
303
- /** @type {unknown} */ (unicodeData)
304
- );
305
- if (portableUnicodeTable.version !== '17.0.0') {
306
- throw new TypeError('UNICODE_GENERATED_DATA_INVALID');
307
- }
308
-
309
- /**
310
- * @typedef {Record<string, unknown>} UnknownRecord
311
- */
312
-
313
- /**
314
- * @typedef {{ path: string, kind: 'file', mode: number, bytes: Uint8Array }} ThemePackageFile
315
- */
316
-
317
- /**
318
- * @typedef {{ path: string, byteLength: string, sha256: string }} ThemeEntry
319
- */
320
-
321
- /**
322
- * Throw one stable semantic diagnostic.
323
- *
324
- * @param {string} code diagnostic code
325
- * @returns {never} never returns
326
- */
327
- function invalid(code) {
328
- throw new SemanticValidationError(code);
329
- }
330
-
331
- /**
332
- * Test whether a value is a plain object.
333
- *
334
- * @param {unknown} value candidate value
335
- * @returns {value is UnknownRecord} whether the value is a plain object
336
- */
337
- function isPlainObject(value) {
338
- if (value === null || typeof value !== 'object' || Array.isArray(value)) {
339
- return false;
340
- }
341
- const prototype = Object.getPrototypeOf(value);
342
- return prototype === Object.prototype || prototype === null;
343
- }
344
-
345
- /**
346
- * Require a closed plain object.
347
- *
348
- * @param {unknown} value candidate object
349
- * @param {readonly string[]} keys exact key ledger
350
- * @param {string} code failure diagnostic
351
- * @returns {UnknownRecord} validated record
352
- */
353
- function exactRecord(value, keys, code) {
354
- if (!isPlainObject(value)) invalid(code);
355
- const actual = Object.keys(value).sort();
356
- const expected = [...keys].sort();
357
- if (
358
- actual.length !== expected.length ||
359
- actual.some((key, index) => key !== expected[index])
360
- ) {
361
- invalid(code);
362
- }
363
- return value;
364
- }
365
-
366
- /**
367
- * Compare two values by exact RFC 8785 bytes.
368
- *
369
- * @param {unknown} left first value
370
- * @param {unknown} right second value
371
- * @returns {boolean} whether canonical bytes are equal
372
- */
373
- function jcsEqual(left, right) {
374
- return canonicalizeJcs(left) === canonicalizeJcs(right);
375
- }
376
-
377
- /**
378
- * Compare two strings by unsigned UTF-8 bytes.
379
- *
380
- * @param {string} left first string
381
- * @param {string} right second string
382
- * @returns {number} byte-order comparison
383
- */
384
- function compareUtf8(left, right) {
385
- return Buffer.compare(Buffer.from(left, 'utf8'), Buffer.from(right, 'utf8'));
386
- }
387
-
388
- /**
389
- * Compare JSON values by their complete RFC 8785 encoded bytes.
390
- *
391
- * @param {unknown} left first value
392
- * @param {unknown} right second value
393
- * @returns {number} canonical-byte comparison
394
- */
395
- function compareJcsValues(left, right) {
396
- return Buffer.compare(
397
- canonicalizeJcsBytes(left),
398
- canonicalizeJcsBytes(right),
399
- );
400
- }
401
-
402
- /**
403
- * Compare string values by their complete RFC 8785 encoded bytes.
404
- *
405
- * @param {string} left first string
406
- * @param {string} right second string
407
- * @returns {number} canonical-byte comparison
408
- */
409
- function compareJcsStrings(left, right) {
410
- return Buffer.compare(
411
- Buffer.from(canonicalizeJcs(left), 'utf8'),
412
- Buffer.from(canonicalizeJcs(right), 'utf8'),
413
- );
414
- }
415
-
416
- /**
417
- * Report whether a string contains any code unit in inclusive ranges.
418
- *
419
- * The caller has already rejected lone surrogates, so code-unit iteration is
420
- * exact for the BMP control and bidi ranges used here.
421
- *
422
- * @param {string} value checked string
423
- * @param {readonly (readonly [number, number])[]} ranges inclusive ranges
424
- * @returns {boolean} whether a range contains a code unit
425
- */
426
- function hasCodeUnitInRanges(value, ranges) {
427
- for (let index = 0; index < value.length; index += 1) {
428
- const codeUnit = value.charCodeAt(index);
429
- if (ranges.some(([first, last]) => codeUnit >= first && codeUnit <= last)) {
430
- return true;
431
- }
432
- }
433
- return false;
434
- }
435
-
436
- /**
437
- * Test Unicode scalar membership in one generated sorted inclusive range set.
438
- *
439
- * @param {number} codePoint Unicode scalar value
440
- * @param {UnicodePropertyRange[]} ranges generated property ranges
441
- * @returns {boolean} membership
442
- */
443
- function inUnicodeRanges(codePoint, ranges) {
444
- let low = 0;
445
- let high = ranges.length - 1;
446
- while (low <= high) {
447
- const middle = Math.floor((low + high) / 2);
448
- const range = ranges[middle];
449
- if (!range) return false;
450
- if (codePoint < range[0]) high = middle - 1;
451
- else if (codePoint > range[1]) low = middle + 1;
452
- else return true;
453
- }
454
- return false;
455
- }
456
-
457
- /**
458
- * Test whether a Unicode scalar is forbidden by gala-portable-v2.
459
- *
460
- * @param {string} character one Unicode scalar
461
- * @returns {boolean} whether the scalar is forbidden
462
- */
463
- function forbiddenPortablePathScalar(character) {
464
- const codePoint = character.codePointAt(0);
465
- return (
466
- codePoint === undefined ||
467
- codePoint < 0x20 ||
468
- (codePoint >= 0x7f && codePoint <= 0x9f) ||
469
- ['%', '<', '>', ':', '"', '|', '?', '*', '\\'].includes(character) ||
470
- inUnicodeRanges(codePoint, portableUnicodeTable.defaultIgnorable) ||
471
- inUnicodeRanges(codePoint, portableUnicodeTable.noncharacter)
472
- );
473
- }
474
-
475
- /**
476
- * Test whether one segment begins or ends in Unicode 17 White_Space.
477
- *
478
- * @param {string} segment non-empty path segment
479
- * @returns {boolean} whether either edge is White_Space
480
- */
481
- function hasWhitespaceEdge(segment) {
482
- const scalars = [...segment];
483
- const first = scalars[0]?.codePointAt(0);
484
- const last = scalars.at(-1)?.codePointAt(0);
485
- return (
486
- first === undefined ||
487
- last === undefined ||
488
- inUnicodeRanges(first, portableUnicodeTable.whiteSpace) ||
489
- inUnicodeRanges(last, portableUnicodeTable.whiteSpace)
490
- );
491
- }
492
-
493
- /**
494
- * Test one gala-portable-v2 reserved path stem.
495
- *
496
- * @param {string} segment normalized path segment
497
- * @returns {boolean} whether the stem is reserved
498
- */
499
- function isReservedPortableStem(segment) {
500
- const stem = segment.split('.', 1)[0];
501
- if (!stem) return false;
502
- const folded = unicodeCollisionKey17(stem);
503
- return (
504
- ['con', 'prn', 'aux', 'nul'].includes(folded) ||
505
- /^(?:com|lpt)[1-9]$/u.test(folded) ||
506
- /^(?:com|lpt)[¹²³]$/u.test(folded)
507
- );
508
- }
509
-
510
- /**
511
- * Require exact array contents.
512
- *
513
- * @param {unknown} value candidate array
514
- * @param {readonly unknown[]} expected required sequence
515
- * @param {string} code failure diagnostic
516
- * @returns {void}
517
- */
518
- function requireExactArray(value, expected, code) {
519
- if (
520
- !Array.isArray(value) ||
521
- value.length !== expected.length ||
522
- value.some((entry, index) => !jcsEqual(entry, expected[index]))
523
- ) {
524
- invalid(code);
525
- }
526
- }
527
-
528
- /**
529
- * Require a tagged SHA-256 digest.
530
- *
531
- * @param {unknown} value candidate digest
532
- * @param {string} code failure diagnostic
533
- * @returns {string} checked digest
534
- */
535
- function requireDigest(value, code) {
536
- if (typeof value !== 'string' || !DIGEST_PATTERN.test(value)) invalid(code);
537
- return value;
538
- }
539
-
540
- /**
541
- * Require a canonical UUIDv7 stable ID.
542
- *
543
- * @param {unknown} value candidate identifier
544
- * @param {string} code failure diagnostic
545
- * @returns {string} checked identifier
546
- */
547
- function requireStableId(value, code) {
548
- if (typeof value !== 'string' || !STABLE_ID_PATTERN.test(value))
549
- invalid(code);
550
- return value;
551
- }
552
-
553
- /**
554
- * Require a canonical SemVer scalar while normalizing its diagnostic.
555
- *
556
- * @param {unknown} value candidate version
557
- * @param {string} code failure diagnostic
558
- * @returns {string} checked version
559
- */
560
- function requireSemver(value, code) {
561
- if (typeof value !== 'string') invalid(code);
562
- try {
563
- parseSemver(value);
564
- } catch {
565
- invalid(code);
566
- }
567
- return value;
568
- }
569
-
570
- /**
571
- * Require a canonical SemVer range while normalizing its diagnostic.
572
- *
573
- * @param {unknown} value candidate range
574
- * @param {string} code failure diagnostic
575
- * @returns {string} checked range
576
- */
577
- function requireSemverRange(value, code) {
578
- if (typeof value !== 'string') invalid(code);
579
- try {
580
- parseSemverRange(value);
581
- } catch {
582
- invalid(code);
583
- }
584
- return value;
585
- }
586
-
587
- /**
588
- * Require one canonical signed-64 non-negative or positive decimal.
589
- *
590
- * @param {unknown} value candidate decimal
591
- * @param {boolean} positive whether zero is forbidden
592
- * @param {string} code failure diagnostic
593
- * @returns {bigint} checked numeric value
594
- */
595
- function requireInt64(value, positive, code) {
596
- const pattern = positive ? POSITIVE_INT64_PATTERN : NONNEGATIVE_INT64_PATTERN;
597
- if (typeof value !== 'string' || !pattern.test(value)) invalid(code);
598
- const numeric = BigInt(value);
599
- if (numeric > INT64_MAXIMUM) invalid(code);
600
- return numeric;
601
- }
602
-
603
- /**
604
- * Require normalized bounded plain-label text.
605
- *
606
- * @param {unknown} value candidate label
607
- * @param {string} code failure diagnostic
608
- * @returns {string} checked label
609
- */
610
- function requirePlainLabel(value, code) {
611
- if (typeof value !== 'string') invalid(code);
612
- try {
613
- assertUnicodeScalarString(value);
614
- } catch {
615
- invalid(code);
616
- }
617
- if (
618
- normalizeNfc17(value) !== value ||
619
- graphemeLength17(value) < 1 ||
620
- graphemeLength17(value) > 80 ||
621
- value.includes('<') ||
622
- value.includes('>') ||
623
- hasCodeUnitInRanges(value, [
624
- [0x00, 0x1f],
625
- [0x7f, 0x9f],
626
- [0x202a, 0x202e],
627
- [0x2066, 0x2069],
628
- ])
629
- ) {
630
- invalid(code);
631
- }
632
- return value;
633
- }
634
-
635
- /**
636
- * Require a normalized repository-relative path.
637
- *
638
- * @param {unknown} value candidate path
639
- * @param {string} code failure diagnostic
640
- * @returns {string} checked path
641
- */
642
- function requireRepositoryPath(value, code) {
643
- if (typeof value !== 'string') invalid(code);
644
- try {
645
- assertUnicodeScalarString(value);
646
- } catch {
647
- invalid(code);
648
- }
649
- if (
650
- Buffer.byteLength(value, 'utf8') < 1 ||
651
- Buffer.byteLength(value, 'utf8') > 512 ||
652
- normalizeNfc17(value) !== value ||
653
- value.startsWith('/') ||
654
- value.endsWith('/') ||
655
- value.includes('//') ||
656
- value.includes('\\') ||
657
- value.includes('%') ||
658
- hasCodeUnitInRanges(value, [
659
- [0x00, 0x1f],
660
- [0x7f, 0x9f],
661
- ])
662
- ) {
663
- invalid(code);
664
- }
665
- const segments = value.split('/');
666
- if (
667
- segments.some(
668
- (segment) =>
669
- segment === '.' ||
670
- segment === '..' ||
671
- Buffer.byteLength(segment, 'utf8') > 128 ||
672
- hasWhitespaceEdge(segment) ||
673
- segment.endsWith('.') ||
674
- [...segment].some(forbiddenPortablePathScalar) ||
675
- isReservedPortableStem(segment),
676
- )
677
- ) {
678
- invalid(code);
679
- }
680
- return value;
681
- }
682
-
683
- /**
684
- * Require an array of unique, byte-sorted strings.
685
- *
686
- * @param {unknown} value candidate array
687
- * @param {number} minimum minimum length
688
- * @param {number} maximum maximum length
689
- * @param {(entry: unknown) => string} validateEntry element validator
690
- * @param {string} code failure diagnostic
691
- * @param {(left: string, right: string) => number} [compare] order comparator
692
- * @returns {string[]} checked array
693
- */
694
- function requireSortedStringSet(
695
- value,
696
- minimum,
697
- maximum,
698
- validateEntry,
699
- code,
700
- compare = compareUtf8,
701
- ) {
702
- if (
703
- !Array.isArray(value) ||
704
- value.length < minimum ||
705
- value.length > maximum
706
- ) {
707
- invalid(code);
708
- }
709
- const checked = value.map(validateEntry);
710
- for (let index = 1; index < checked.length; index += 1) {
711
- const previous = checked[index - 1];
712
- const current = checked[index];
713
- if (
714
- previous === undefined ||
715
- current === undefined ||
716
- compare(previous, current) >= 0
717
- ) {
718
- invalid(code);
719
- }
720
- }
721
- return checked;
722
- }
723
-
724
- /**
725
- * Require one 1..64-byte style selector name/value.
726
- *
727
- * @param {unknown} value candidate style scalar
728
- * @param {string} code failure diagnostic
729
- * @returns {string} checked value
730
- */
731
- function requireStyleName(value, code) {
732
- if (
733
- typeof value !== 'string' ||
734
- Buffer.byteLength(value, 'utf8') > 64 ||
735
- !STYLE_NAME_PATTERN.test(value)
736
- ) {
737
- invalid(code);
738
- }
739
- return value;
740
- }
741
-
742
- /**
743
- * Require exact compact JCS bytes for an object.
744
- *
745
- * @param {unknown} bytes candidate retained bytes
746
- * @param {unknown} value represented object
747
- * @param {string} code failure diagnostic
748
- * @returns {void}
749
- */
750
- function requireCanonicalBytes(bytes, value, code) {
751
- if (
752
- !(bytes instanceof Uint8Array) ||
753
- !Buffer.from(bytes).equals(canonicalizeJcsBytes(value))
754
- ) {
755
- invalid(code);
756
- }
757
- }
758
-
759
- /**
760
- * Parse one retained JSON file and normalize all parser failures.
761
- *
762
- * @param {Uint8Array} bytes exact file bytes
763
- * @param {string} code failure diagnostic
764
- * @returns {unknown} parsed I-JSON value
765
- */
766
- function parseRetainedJson(bytes, code) {
767
- try {
768
- return parseDuplicateFreeIJson(bytes);
769
- } catch {
770
- invalid(code);
771
- }
772
- }
773
-
774
- /**
775
- * Return one package's exact package name and version.
776
- *
777
- * @param {unknown} value candidate packageExact scalar
778
- * @param {string} code failure diagnostic
779
- * @returns {{ name: string, version: string }} parsed identity
780
- */
781
- function parsePackageExact(value, code) {
782
- if (typeof value !== 'string') invalid(code);
783
- const separator = value.lastIndexOf('@');
784
- if (separator <= 0) invalid(code);
785
- const name = value.slice(0, separator);
786
- const version = value.slice(separator + 1);
787
- if (
788
- Buffer.byteLength(name, 'utf8') > 214 ||
789
- !PACKAGE_NAME_PATTERN.test(name)
790
- ) {
791
- invalid(code);
792
- }
793
- requireSemver(version, code);
794
- return { name, version };
795
- }
796
-
797
- /**
798
- * Return whether a string array exactly equals another sequence.
799
- *
800
- * @param {unknown} value candidate array
801
- * @param {readonly string[]} expected required strings
802
- * @returns {boolean} exact equality
803
- */
804
- function stringArrayEquals(value, expected) {
805
- return (
806
- Array.isArray(value) &&
807
- value.length === expected.length &&
808
- value.every((entry, index) => entry === expected[index])
809
- );
810
- }
811
-
812
- /**
813
- * Validate the intentionally empty MVP template-composition projection.
814
- *
815
- * @param {unknown} value template-composition instance
816
- * @returns {void}
817
- */
818
- export function validateTemplateComposition(value) {
819
- const record = exactRecord(
820
- value,
821
- TEMPLATE_COMPOSITION_KEYS,
822
- 'TEMPLATE_COMPOSITION_INVALID',
823
- );
824
- if (
825
- record.schemaId !== 'urn:gala:schema:template-composition:2.0.0' ||
826
- record.schemaVersion !== '2.0.0'
827
- ) {
828
- invalid('TEMPLATE_COMPOSITION_INVALID');
829
- }
830
- }
831
-
832
- /**
833
- * Validate one exact 35-row theme token catalog.
834
- *
835
- * @param {unknown} value candidate token array
836
- * @returns {void}
837
- */
838
- function validateThemeTokens(value) {
839
- const code = 'THEME_TOKEN_CATALOG_INVALID';
840
- if (!Array.isArray(value) || value.length !== THEME_TOKEN_CATALOG.length) {
841
- invalid(code);
842
- }
843
- for (let index = 0; index < THEME_TOKEN_CATALOG.length; index += 1) {
844
- const expected = THEME_TOKEN_CATALOG[index];
845
- const token = exactRecord(value[index], TOKEN_KEYS, code);
846
- if (!expected || token.key !== expected[0] || token.type !== expected[1]) {
847
- invalid(code);
848
- }
849
- if (typeof token.light !== 'string' || typeof token.dark !== 'string') {
850
- invalid(code);
851
- }
852
- let valid = false;
853
- if (token.type === 'color') {
854
- valid = COLOR_PATTERN.test(token.light) && COLOR_PATTERN.test(token.dark);
855
- } else if (token.type === 'length') {
856
- valid =
857
- LENGTH_PATTERN.test(token.light) && LENGTH_PATTERN.test(token.dark);
858
- } else if (token.type === 'font-family') {
859
- valid =
860
- FONT_FAMILY_PATTERN.test(token.light) &&
861
- FONT_FAMILY_PATTERN.test(token.dark) &&
862
- token.light
863
- .split(', ')
864
- .every((component) => Buffer.byteLength(component, 'ascii') <= 64) &&
865
- token.dark
866
- .split(', ')
867
- .every((component) => Buffer.byteLength(component, 'ascii') <= 64);
868
- } else if (token.type === 'font-weight') {
869
- valid =
870
- FONT_WEIGHT_PATTERN.test(token.light) &&
871
- FONT_WEIGHT_PATTERN.test(token.dark);
872
- }
873
- if (!valid || (token.type !== 'color' && token.light !== token.dark)) {
874
- invalid(code);
875
- }
876
- }
877
- }
878
-
879
- /**
880
- * Validate one passive-asset manifest row.
881
- *
882
- * @param {unknown} value candidate row
883
- * @returns {UnknownRecord} checked row
884
- */
885
- function validatePassiveAsset(value) {
886
- const code = 'THEME_PASSIVE_ASSET_INVALID';
887
- const asset = exactRecord(value, ASSET_KEYS, code);
888
- requireRepositoryPath(asset.path, code);
889
- if (
890
- typeof asset.mediaType !== 'string' ||
891
- ![
892
- 'text/css',
893
- 'font/woff2',
894
- 'image/png',
895
- 'image/jpeg',
896
- 'image/webp',
897
- 'image/avif',
898
- 'image/svg+xml',
899
- ].includes(asset.mediaType)
900
- ) {
901
- invalid(code);
902
- }
903
- requireInt64(asset.byteLength, false, code);
904
- requireDigest(asset.sha256, code);
905
- if (typeof asset.license !== 'string') invalid(code);
906
- try {
907
- validateSpdxExpression(asset.license);
908
- } catch {
909
- invalid(code);
910
- }
911
- return asset;
912
- }
913
-
914
- /**
915
- * Validate declared theme budgets and return their numeric values.
916
- *
917
- * @param {unknown} value candidate budget record
918
- * @param {string} [code] failure diagnostic
919
- * @returns {{ maximumFileBytes: bigint, maximumTotalBytes: bigint, maximumFiles: number }} numeric budgets
920
- */
921
- function validateBudgets(value, code = 'THEME_BUDGET_INVALID') {
922
- const budgets = exactRecord(value, BUDGET_KEYS, code);
923
- const maximumFileBytes = requireInt64(budgets.maximumFileBytes, true, code);
924
- const maximumTotalBytes = requireInt64(budgets.maximumTotalBytes, true, code);
925
- if (
926
- !Number.isInteger(budgets.maximumFiles) ||
927
- Number(budgets.maximumFiles) < 1 ||
928
- Number(budgets.maximumFiles) > 512
929
- ) {
930
- invalid(code);
931
- }
932
- return {
933
- maximumFileBytes,
934
- maximumTotalBytes,
935
- maximumFiles: Number(budgets.maximumFiles),
936
- };
937
- }
938
-
939
- /**
940
- * Validate the closed theme-contract instance independent of external records.
941
- *
942
- * @param {unknown} value theme-contract instance
943
- * @returns {void}
944
- */
945
- export function validateThemeContract(value) {
946
- const code = 'THEME_CONTRACT_INVALID';
947
- const theme = exactRecord(value, THEME_KEYS, code);
948
- if (
949
- theme.schemaId !== 'urn:gala:schema:theme-contract:2.0.0' ||
950
- theme.schemaVersion !== '2.0.0' ||
951
- typeof theme.themeId !== 'string' ||
952
- !Object.hasOwn(THEME_IDENTITIES, theme.themeId)
953
- ) {
954
- invalid(code);
955
- }
956
-
957
- const identity = parsePackageExact(theme.package, code);
958
- if (identity.name !== THEME_IDENTITIES[theme.themeId]) invalid(code);
959
- requireSemver(theme.contractVersion, code);
960
- requireSemverRange(theme.templateRange, code);
961
-
962
- const variant = STYLESHEET_VARIANTS.find(
963
- ({ paths, layers }) =>
964
- stringArrayEquals(theme.stylesheets, paths) &&
965
- stringArrayEquals(theme.cssLayers, layers),
966
- );
967
- if (!variant) invalid(code);
968
-
969
- requireSortedStringSet(
970
- theme.slotHooks,
971
- 1,
972
- 64,
973
- (entry) => requirePlainLabel(entry, code),
974
- code,
975
- compareJcsStrings,
976
- );
977
- validateThemeTokens(theme.tokens);
978
- requireExactArray(theme.modes, MODES, code);
979
-
980
- if (!Array.isArray(theme.assets) || theme.assets.length > 256) invalid(code);
981
- const assetPaths = new Set();
982
- for (const candidate of theme.assets) {
983
- const asset = validatePassiveAsset(candidate);
984
- if (typeof asset.path !== 'string' || assetPaths.has(asset.path))
985
- invalid(code);
986
- assetPaths.add(asset.path);
987
- }
988
-
989
- requireSortedStringSet(
990
- theme.fixtures,
991
- 1,
992
- 64,
993
- (entry) => requirePlainLabel(entry, code),
994
- code,
995
- );
996
- if (theme.browserPolicyRef !== 'gala-theme-css-v2-20211224') invalid(code);
997
- requireDigest(theme.integrity, code);
998
- validateBudgets(theme.budgets);
999
- requireDigest(theme.fixtureDigest, code);
1000
- requireDigest(theme.evidenceDigest, code);
1001
- requireDigest(theme.stylingContractDigest, code);
1002
- }
1003
-
1004
- /**
1005
- * Compute the exact self-excluding template styling-contract digest.
1006
- *
1007
- * @param {unknown} contract complete styling-contract record
1008
- * @returns {string} tagged digest
1009
- */
1010
- export function computeTemplateStylingContractDigest(contract) {
1011
- const profile = ACTIVE_DIGEST_PROFILES.templateStylingContract;
1012
- if (!profile) throw new TypeError('DIGEST_PROFILE_MISSING');
1013
- return profile.digest(contract);
1014
- }
1015
-
1016
- /**
1017
- * Build the exact public-hook atom catalog and count each atom occurrence.
1018
- *
1019
- * @param {UnknownRecord} contract validated styling contract
1020
- * @param {string} code failure diagnostic
1021
- * @returns {Map<string, number>} atom occurrence counts
1022
- */
1023
- function buildHookAtomCatalog(contract, code) {
1024
- /** @type {Map<string, number>} */
1025
- const atoms = new Map();
1026
- /**
1027
- * @param {string} atom selector atom
1028
- * @returns {void}
1029
- */
1030
- const add = (atom) => {
1031
- atoms.set(atom, (atoms.get(atom) ?? 0) + 1);
1032
- };
1033
-
1034
- for (const selector of /** @type {string[]} */ (contract.typeSelectors)) {
1035
- add(selector);
1036
- }
1037
- for (const selector of /** @type {string[]} */ (contract.classSelectors)) {
1038
- add(`.${selector}`);
1039
- }
1040
- for (const selector of /** @type {string[]} */ (contract.idSelectors)) {
1041
- add(`#${selector}`);
1042
- }
1043
- for (const candidate of /** @type {unknown[]} */ (contract.attributes)) {
1044
- const attribute = exactRecord(candidate, STYLE_ATTRIBUTE_KEYS, code);
1045
- if (
1046
- attribute.name === 'data-gala-publication-root' ||
1047
- attribute.name === 'data-gala-resolved-color-mode'
1048
- ) {
1049
- continue;
1050
- }
1051
- if (attribute.match === 'presence') {
1052
- add(`[${String(attribute.name)}]`);
1053
- } else {
1054
- for (const value of /** @type {string[]} */ (attribute.values)) {
1055
- add(`[${String(attribute.name)}="${value}"]`);
1056
- }
1057
- }
1058
- }
1059
- return atoms;
1060
- }
1061
-
1062
- /**
1063
- * Validate the selected template's exact immutable styling contract.
1064
- *
1065
- * `context.bytes` is the retained
1066
- * `contracts/theme-styling-contract.jcs` member. `context.lockedTemplate` is
1067
- * the integrity-verified selected lock row; no package or digest fields are
1068
- * synthesized by this validator.
1069
- *
1070
- * @param {unknown} value template styling-contract record
1071
- * @param {unknown} context retained bytes and selected template identity
1072
- * @returns {void}
1073
- */
1074
- export function validateTemplateStylingContract(value, context) {
1075
- const code = 'TEMPLATE_STYLING_CONTRACT_INVALID';
1076
- const contract = exactRecord(value, STYLING_CONTRACT_KEYS, code);
1077
- const retained = exactRecord(context, ['bytes', 'lockedTemplate'], code);
1078
- const lockedTemplate = isPlainObject(retained.lockedTemplate)
1079
- ? retained.lockedTemplate
1080
- : invalid(code);
1081
-
1082
- if (
1083
- contract.profile !== 'gala-template-styling-contract-v2' ||
1084
- contract.contractVersion !== '2.0.0' ||
1085
- contract.templatePackage !== '@rathnasgala2/template' ||
1086
- contract.templatePackage !== lockedTemplate.package ||
1087
- contract.templateVersion !== lockedTemplate.version
1088
- ) {
1089
- invalid(code);
1090
- }
1091
- requireSemver(contract.templateVersion, code);
1092
- requireCanonicalBytes(retained.bytes, contract, code);
1093
- requireExactArray(contract.orderedLayers, ORDERED_LAYERS, code);
1094
-
1095
- const layers = exactRecord(
1096
- contract.stylesheetLayers,
1097
- STYLESHEET_LAYER_KEYS,
1098
- code,
1099
- );
1100
- if (
1101
- layers.tokens !== 'gala-tokens' ||
1102
- layers.components !== 'gala-components' ||
1103
- layers.utilities !== 'gala-utilities' ||
1104
- layers.print !== 'gala-print'
1105
- ) {
1106
- invalid(code);
1107
- }
1108
- if (contract.publicationRootSelector !== '[data-gala-publication-root]') {
1109
- invalid(code);
1110
- }
1111
- const palettes = exactRecord(
1112
- contract.resolvedPaletteSelectors,
1113
- PALETTE_SELECTOR_KEYS,
1114
- code,
1115
- );
1116
- if (
1117
- palettes.light !==
1118
- '[data-gala-publication-root][data-gala-resolved-color-mode="light"]' ||
1119
- palettes.dark !==
1120
- '[data-gala-publication-root][data-gala-resolved-color-mode="dark"]'
1121
- ) {
1122
- invalid(code);
1123
- }
1124
-
1125
- /** @type {readonly (readonly [string, number])[]} */
1126
- const selectorSets = [
1127
- ['typeSelectors', 64],
1128
- ['classSelectors', 256],
1129
- ['idSelectors', 64],
1130
- ['pseudoClasses', 64],
1131
- ];
1132
- for (const [member, maximum] of selectorSets) {
1133
- requireSortedStringSet(
1134
- contract[member],
1135
- 0,
1136
- maximum,
1137
- (entry) => requireStyleName(entry, code),
1138
- code,
1139
- );
1140
- }
1141
-
1142
- if (
1143
- !Array.isArray(contract.attributes) ||
1144
- contract.attributes.length < 2 ||
1145
- contract.attributes.length > 128
1146
- ) {
1147
- invalid(code);
1148
- }
1149
- /** @type {UnknownRecord | undefined} */
1150
- let previousAttribute;
1151
- const attributeNames = new Set();
1152
- let rootRows = 0;
1153
- let paletteRows = 0;
1154
- for (const candidate of contract.attributes) {
1155
- const attribute = exactRecord(candidate, STYLE_ATTRIBUTE_KEYS, code);
1156
- const name = requireStyleName(attribute.name, code);
1157
- if (attributeNames.has(name)) invalid(code);
1158
- if (!['presence', 'exact-value'].includes(String(attribute.match))) {
1159
- invalid(code);
1160
- }
1161
- if (!['root', 'state', 'semantic'].includes(String(attribute.role))) {
1162
- invalid(code);
1163
- }
1164
- const values = requireSortedStringSet(
1165
- attribute.values,
1166
- attribute.match === 'presence' ? 0 : 1,
1167
- 32,
1168
- (entry) => requireStyleName(entry, code),
1169
- code,
1170
- );
1171
- if (attribute.match === 'presence' && values.length !== 0) invalid(code);
1172
-
1173
- if (
1174
- name === 'data-gala-publication-root' &&
1175
- attribute.match === 'presence' &&
1176
- attribute.role === 'root' &&
1177
- values.length === 0
1178
- ) {
1179
- rootRows += 1;
1180
- } else if (attribute.role === 'root') {
1181
- invalid(code);
1182
- }
1183
- if (
1184
- name === 'data-gala-resolved-color-mode' &&
1185
- attribute.match === 'exact-value' &&
1186
- attribute.role === 'state' &&
1187
- stringArrayEquals(values, ['dark', 'light'])
1188
- ) {
1189
- paletteRows += 1;
1190
- } else if (name === 'data-gala-resolved-color-mode') {
1191
- invalid(code);
1192
- }
1193
- if (
1194
- previousAttribute !== undefined &&
1195
- compareJcsValues(previousAttribute, attribute) >= 0
1196
- ) {
1197
- invalid(code);
1198
- }
1199
- previousAttribute = attribute;
1200
- attributeNames.add(name);
1201
- }
1202
- if (rootRows !== 1 || paletteRows !== 1) invalid(code);
1203
-
1204
- requireExactArray(
1205
- contract.pseudoElements,
1206
- ['after', 'before', 'marker', 'selection'],
1207
- code,
1208
- );
1209
- requireExactArray(
1210
- contract.functionalPseudos,
1211
- ['is', 'where', 'not', 'nth-child', 'nth-last-child'],
1212
- code,
1213
- );
1214
- requireExactArray(contract.combinators, [' ', ' > ', ' + ', ' ~ '], code);
1215
-
1216
- const composition = exactRecord(contract.composition, COMPOSITION_KEYS, code);
1217
- if (
1218
- composition.rootScope !== 'first-compound-required' ||
1219
- !stringArrayEquals(composition.compoundOrder, [
1220
- 'type',
1221
- 'id',
1222
- 'class',
1223
- 'attribute',
1224
- 'pseudo-class',
1225
- 'pseudo-element',
1226
- ]) ||
1227
- composition.functionalSelectorArguments !== 'compound-only' ||
1228
- composition.maximumFunctionalDepth !== 1 ||
1229
- composition.nthExpressionProfile !== 'gala-positive-an-plus-b-v2'
1230
- ) {
1231
- invalid(code);
1232
- }
1233
-
1234
- if (
1235
- !Array.isArray(contract.publicThemeSlotHooks) ||
1236
- contract.publicThemeSlotHooks.length < 1 ||
1237
- contract.publicThemeSlotHooks.length > 64
1238
- ) {
1239
- invalid(code);
1240
- }
1241
- const atomCatalog = buildHookAtomCatalog(contract, code);
1242
- let previousHookId = '';
1243
- const hookAtoms = new Set();
1244
- for (const candidate of contract.publicThemeSlotHooks) {
1245
- const hook = exactRecord(candidate, STYLE_HOOK_KEYS, code);
1246
- const hookId = requirePlainLabel(hook.hookId, code);
1247
- if (previousHookId !== '' && compareUtf8(previousHookId, hookId) >= 0) {
1248
- invalid(code);
1249
- }
1250
- previousHookId = hookId;
1251
- if (
1252
- typeof hook.selectorAtom !== 'string' ||
1253
- Buffer.byteLength(hook.selectorAtom, 'utf8') < 1 ||
1254
- Buffer.byteLength(hook.selectorAtom, 'utf8') > 132 ||
1255
- atomCatalog.get(hook.selectorAtom) !== 1 ||
1256
- hookAtoms.has(hook.selectorAtom)
1257
- ) {
1258
- invalid(code);
1259
- }
1260
- hookAtoms.add(hook.selectorAtom);
1261
- }
1262
-
1263
- requireDigest(contract.catalogDigest, code);
1264
- let expectedDigest;
1265
- try {
1266
- expectedDigest = computeTemplateStylingContractDigest(contract);
1267
- } catch {
1268
- invalid(code);
1269
- }
1270
- if (contract.catalogDigest !== expectedDigest) invalid(code);
1271
- }
1272
-
1273
- /**
1274
- * Compute the exact self-excluding shared theme-fixture release digest.
1275
- *
1276
- * @param {unknown} release complete fixture-release record
1277
- * @returns {string} tagged digest
1278
- */
1279
- export function computeThemeFixtureReleaseDigest(release) {
1280
- const profile = ACTIVE_DIGEST_PROFILES.themeFixtureRelease;
1281
- if (!profile) throw new TypeError('DIGEST_PROFILE_MISSING');
1282
- return profile.digest(release);
1283
- }
1284
-
1285
- /**
1286
- * Validate an immutable shared theme fixture release.
1287
- *
1288
- * `context.bytes` are its retained compact-JCS bytes and
1289
- * `context.stylingContractDigest` is the independently validated template
1290
- * styling catalog digest.
1291
- *
1292
- * @param {unknown} value fixture-release record
1293
- * @param {unknown} context retained bytes and styling digest
1294
- * @returns {void}
1295
- */
1296
- export function validateThemeFixtureRelease(value, context) {
1297
- const code = 'THEME_FIXTURE_RELEASE_INVALID';
1298
- const release = exactRecord(value, FIXTURE_RELEASE_KEYS, code);
1299
- const retained = exactRecord(
1300
- context,
1301
- ['bytes', 'stylingContractDigest'],
1302
- code,
1303
- );
1304
- if (
1305
- release.profile !== 'gala-theme-fixture-release-v2' ||
1306
- release.contractVersion !== '2.0.0' ||
1307
- release.browserPolicyRef !== 'gala-theme-css-v2-20211224' ||
1308
- release.binaryAssetProfile !== 'gala-theme-binary-assets-v2'
1309
- ) {
1310
- invalid(code);
1311
- }
1312
- requireStableId(release.fixtureReleaseId, code);
1313
- requireDigest(release.stylingContractDigest, code);
1314
- if (release.stylingContractDigest !== retained.stylingContractDigest) {
1315
- invalid(code);
1316
- }
1317
- requireCanonicalBytes(retained.bytes, release, code);
1318
-
1319
- if (
1320
- !Array.isArray(release.runners) ||
1321
- release.runners.length < 1 ||
1322
- release.runners.length > 16
1323
- ) {
1324
- invalid(code);
1325
- }
1326
- const runnerIds = new Set();
1327
- let previousRunner = '';
1328
- for (const candidate of release.runners) {
1329
- const runner = exactRecord(candidate, FIXTURE_RUNNER_KEYS, code);
1330
- if (
1331
- typeof runner.runnerId !== 'string' ||
1332
- !FIXTURE_RUNNER_IDS.includes(runner.runnerId) ||
1333
- runnerIds.has(runner.runnerId) ||
1334
- (previousRunner !== '' &&
1335
- compareUtf8(previousRunner, runner.runnerId) >= 0)
1336
- ) {
1337
- invalid(code);
1338
- }
1339
- runnerIds.add(runner.runnerId);
1340
- previousRunner = runner.runnerId;
1341
- requireSemver(runner.version, code);
1342
- requireDigest(runner.executableDigest, code);
1343
- }
1344
-
1345
- if (
1346
- !Array.isArray(release.fixtures) ||
1347
- release.fixtures.length < 1 ||
1348
- release.fixtures.length > 64
1349
- ) {
1350
- invalid(code);
1351
- }
1352
- let previousFixture = '';
1353
- for (const candidate of release.fixtures) {
1354
- const fixture = exactRecord(candidate, FIXTURE_DEFINITION_KEYS, code);
1355
- const fixtureId = requirePlainLabel(fixture.fixtureId, code);
1356
- if (
1357
- (previousFixture !== '' &&
1358
- compareUtf8(previousFixture, fixtureId) >= 0) ||
1359
- typeof fixture.runnerId !== 'string' ||
1360
- !runnerIds.has(fixture.runnerId) ||
1361
- !FIXTURE_RUNNER_IDS.includes(fixture.runnerId) ||
1362
- !['accepted', 'rejected'].includes(String(fixture.expectedDisposition))
1363
- ) {
1364
- invalid(code);
1365
- }
1366
- previousFixture = fixtureId;
1367
- requireDigest(fixture.inputDigest, code);
1368
- requireDigest(fixture.expectedEvidenceDigest, code);
1369
- }
1370
-
1371
- requireDigest(release.fixtureDigest, code);
1372
- let expectedDigest;
1373
- try {
1374
- expectedDigest = computeThemeFixtureReleaseDigest(release);
1375
- } catch {
1376
- invalid(code);
1377
- }
1378
- if (release.fixtureDigest !== expectedDigest) invalid(code);
1379
- }
1380
-
1381
- /**
1382
- * Compute the exact self-excluding theme conformance-evidence digest.
1383
- *
1384
- * @param {unknown} result complete conformance result
1385
- * @returns {string} tagged digest
1386
- */
1387
- export function computeThemeConformanceEvidenceDigest(result) {
1388
- const profile = ACTIVE_DIGEST_PROFILES.themeConformanceEvidence;
1389
- if (!profile) throw new TypeError('DIGEST_PROFILE_MISSING');
1390
- return profile.digest(result);
1391
- }
1392
-
1393
- /**
1394
- * Validate one retained theme conformance result against its fixture release.
1395
- *
1396
- * `context.bytes` are exact compact-JCS result bytes. The other context fields
1397
- * are independently validated retained inputs, not additional wire members.
1398
- *
1399
- * @param {unknown} value conformance-result record
1400
- * @param {unknown} context release, input digest, and retained result bytes
1401
- * @returns {void}
1402
- */
1403
- export function validateThemeConformanceResult(value, context) {
1404
- const code = 'THEME_CONFORMANCE_RESULT_INVALID';
1405
- const result = exactRecord(value, CONFORMANCE_RESULT_KEYS, code);
1406
- const retained = exactRecord(
1407
- context,
1408
- ['bytes', 'fixtureRelease', 'themeConformanceInputDigest'],
1409
- code,
1410
- );
1411
- const release = isPlainObject(retained.fixtureRelease)
1412
- ? retained.fixtureRelease
1413
- : invalid(code);
1414
- if (
1415
- result.profile !== 'gala-theme-conformance-result-v2' ||
1416
- result.fixtureReleaseId !== release.fixtureReleaseId ||
1417
- result.fixtureDigest !== release.fixtureDigest ||
1418
- result.themeConformanceInputDigest !== retained.themeConformanceInputDigest
1419
- ) {
1420
- invalid(code);
1421
- }
1422
- requireStableId(result.fixtureReleaseId, code);
1423
- requireDigest(result.fixtureDigest, code);
1424
- requireDigest(result.themeConformanceInputDigest, code);
1425
- requireCanonicalBytes(retained.bytes, result, code);
1426
-
1427
- if (
1428
- !Array.isArray(release.fixtures) ||
1429
- !Array.isArray(result.results) ||
1430
- result.results.length !== release.fixtures.length
1431
- ) {
1432
- invalid(code);
1433
- }
1434
- let everyPasses = true;
1435
- let previousFixtureId = '';
1436
- for (let index = 0; index < release.fixtures.length; index += 1) {
1437
- const definition = exactRecord(
1438
- release.fixtures[index],
1439
- FIXTURE_DEFINITION_KEYS,
1440
- code,
1441
- );
1442
- const row = exactRecord(result.results[index], FIXTURE_RESULT_KEYS, code);
1443
- const fixtureId = requirePlainLabel(row.fixtureId, code);
1444
- if (
1445
- row.fixtureId !== definition.fixtureId ||
1446
- (previousFixtureId !== '' &&
1447
- compareUtf8(previousFixtureId, fixtureId) >= 0) ||
1448
- typeof row.runnerId !== 'string' ||
1449
- !FIXTURE_RUNNER_IDS.includes(row.runnerId) ||
1450
- !['accepted', 'rejected'].includes(String(row.observedDisposition)) ||
1451
- !['pass', 'fail'].includes(String(row.state))
1452
- ) {
1453
- invalid(code);
1454
- }
1455
- previousFixtureId = fixtureId;
1456
- requireDigest(row.observedEvidenceDigest, code);
1457
- const passes =
1458
- row.runnerId === definition.runnerId &&
1459
- row.observedDisposition === definition.expectedDisposition &&
1460
- row.observedEvidenceDigest === definition.expectedEvidenceDigest;
1461
- if (row.state !== (passes ? 'pass' : 'fail')) invalid(code);
1462
- everyPasses &&= passes;
1463
- }
1464
- if (result.overallState !== (everyPasses ? 'pass' : 'fail')) invalid(code);
1465
- requireDigest(result.evidenceDigest, code);
1466
- let expectedDigest;
1467
- try {
1468
- expectedDigest = computeThemeConformanceEvidenceDigest(result);
1469
- } catch {
1470
- invalid(code);
1471
- }
1472
- if (result.evidenceDigest !== expectedDigest) invalid(code);
1473
- }
1474
-
1475
- /**
1476
- * Validate extracted package-file observations shared by both digest profiles.
1477
- *
1478
- * The extraction layer supplies one row for every stripped `package/` tar
1479
- * member. Requiring `kind:file` and mode 0644 here makes links, devices,
1480
- * directories, and executable members unrepresentable as admitted input.
1481
- *
1482
- * @param {unknown} value candidate file rows
1483
- * @param {string} code failure diagnostic
1484
- * @returns {ThemePackageFile[]} checked copied rows
1485
- */
1486
- function validatePackageFiles(value, code) {
1487
- if (!Array.isArray(value) || value.length === 0 || value.length > 512) {
1488
- invalid(code);
1489
- }
1490
- /** @type {ThemePackageFile[]} */
1491
- const files = [];
1492
- const paths = new Set();
1493
- const collisionKeys = new Set();
1494
- for (const candidate of value) {
1495
- const row = exactRecord(candidate, PACKAGE_FILE_KEYS, code);
1496
- const path = requireRepositoryPath(row.path, code);
1497
- let collisionKey;
1498
- try {
1499
- collisionKey = unicodeCollisionKey17(path);
1500
- } catch {
1501
- invalid(code);
1502
- }
1503
- if (
1504
- paths.has(path) ||
1505
- collisionKeys.has(collisionKey) ||
1506
- row.kind !== 'file' ||
1507
- row.mode !== 0o644 ||
1508
- !(row.bytes instanceof Uint8Array)
1509
- ) {
1510
- invalid(code);
1511
- }
1512
- paths.add(path);
1513
- collisionKeys.add(collisionKey);
1514
- files.push({
1515
- path,
1516
- kind: 'file',
1517
- mode: 0o644,
1518
- bytes: row.bytes,
1519
- });
1520
- }
1521
- return files;
1522
- }
1523
-
1524
- /**
1525
- * Replace theme.json with one of the two authority-defined virtual manifests
1526
- * and return the complete path-sorted entry projection.
1527
- *
1528
- * @param {unknown} theme complete theme contract
1529
- * @param {unknown} packageFiles extracted package-file rows
1530
- * @param {readonly string[]} excludedManifestMembers self fields to omit
1531
- * @returns {ThemeEntry[]} exact digest entries
1532
- */
1533
- function projectThemeEntries(theme, packageFiles, excludedManifestMembers) {
1534
- validateThemeContract(theme);
1535
- const source = /** @type {UnknownRecord} */ (theme);
1536
- const files = validatePackageFiles(packageFiles, 'THEME_PACKAGE_INVALID');
1537
- if (files.filter((file) => file.path === 'theme.json').length !== 1) {
1538
- invalid('THEME_PACKAGE_INVALID');
1539
- }
1540
- const excluded = new Set(excludedManifestMembers);
1541
- const virtualTheme = Object.fromEntries(
1542
- Object.entries(source).filter(([key]) => !excluded.has(key)),
1543
- );
1544
- const virtualBytes = canonicalizeJcsBytes(virtualTheme);
1545
- return files
1546
- .map((file) => {
1547
- const bytes = file.path === 'theme.json' ? virtualBytes : file.bytes;
1548
- return {
1549
- path: file.path,
1550
- byteLength: String(bytes.byteLength),
1551
- sha256: sha256Tagged(bytes),
1552
- };
1553
- })
1554
- .sort((left, right) => compareUtf8(left.path, right.path));
1555
- }
1556
-
1557
- /**
1558
- * Compute the pre-finalization conformance-input digest.
1559
- *
1560
- * `theme.json` is virtualized with exactly `integrity` and `evidenceDigest`
1561
- * omitted; every other package member is represented by its raw bytes.
1562
- *
1563
- * @param {unknown} theme complete theme contract
1564
- * @param {unknown} packageFiles extracted package-file rows
1565
- * @returns {string} tagged digest
1566
- */
1567
- export function computeThemeConformanceInputDigest(theme, packageFiles) {
1568
- const profile = ACTIVE_DIGEST_PROFILES.themeConformanceInput;
1569
- if (!profile) throw new TypeError('DIGEST_PROFILE_MISSING');
1570
- return profile.digest(
1571
- projectThemeEntries(theme, packageFiles, ['integrity', 'evidenceDigest']),
1572
- );
1573
- }
1574
-
1575
- /**
1576
- * Compute final semantic theme-package integrity.
1577
- *
1578
- * `theme.json` is virtualized with exactly `integrity` omitted; every other
1579
- * package member is represented by its raw bytes.
1580
- *
1581
- * @param {unknown} theme complete theme contract
1582
- * @param {unknown} packageFiles extracted package-file rows
1583
- * @returns {string} tagged digest
1584
- */
1585
- export function computeThemePackageIntegrity(theme, packageFiles) {
1586
- const profile = ACTIVE_DIGEST_PROFILES.themePackageIntegrity;
1587
- if (!profile) throw new TypeError('DIGEST_PROFILE_MISSING');
1588
- return profile.digest(
1589
- projectThemeEntries(theme, packageFiles, ['integrity']),
1590
- );
1591
- }
1592
-
1593
- /**
1594
- * Find one checked package file by path.
1595
- *
1596
- * @param {ThemePackageFile[]} files checked file rows
1597
- * @param {string} path exact path
1598
- * @param {string} code failure diagnostic
1599
- * @returns {ThemePackageFile} required file
1600
- */
1601
- function requirePackageFile(files, path, code) {
1602
- const file = files.find((candidate) => candidate.path === path);
1603
- if (!file) invalid(code);
1604
- return file;
1605
- }
1606
-
1607
- /**
1608
- * Return one non-CSS asset's required media type.
1609
- *
1610
- * @param {string} path asset path
1611
- * @param {string} code failure diagnostic
1612
- * @returns {string} required media type
1613
- */
1614
- function mediaTypeForAssetPath(path, code) {
1615
- if (!path.startsWith('assets/') || path.length === 'assets/'.length) {
1616
- invalid(code);
1617
- }
1618
- const dot = path.lastIndexOf('.');
1619
- const extension = dot < 0 ? '' : path.slice(dot);
1620
- const mediaType = MEDIA_BY_EXTENSION[extension];
1621
- if (!mediaType) invalid(code);
1622
- return mediaType;
1623
- }
1624
-
1625
- /**
1626
- * Validate an exact asset-to-file byte binding.
1627
- *
1628
- * @param {UnknownRecord} asset checked manifest asset
1629
- * @param {ThemePackageFile} file retained package file
1630
- * @param {string} code failure diagnostic
1631
- * @returns {void}
1632
- */
1633
- function validateAssetBytes(asset, file, code) {
1634
- if (
1635
- asset.byteLength !== String(file.bytes.byteLength) ||
1636
- asset.sha256 !== sha256Tagged(file.bytes)
1637
- ) {
1638
- invalid(code);
1639
- }
1640
- }
1641
-
1642
- /**
1643
- * Validate the exact package.json projection and return its package expression.
1644
- *
1645
- * @param {ThemePackageFile[]} files checked package files
1646
- * @param {UnknownRecord} theme checked theme root
1647
- * @param {string[]} manifestFilePaths package.json `files` closure
1648
- * @param {string} code failure diagnostic
1649
- * @returns {string} canonical package SPDX expression
1650
- */
1651
- function validatePackageJson(files, theme, manifestFilePaths, code) {
1652
- const packageFile = requirePackageFile(files, 'package.json', code);
1653
- const parsed = exactRecord(
1654
- parseRetainedJson(packageFile.bytes, code),
1655
- PACKAGE_JSON_KEYS,
1656
- code,
1657
- );
1658
- const identity = parsePackageExact(theme.package, code);
1659
- if (parsed.name !== identity.name || parsed.version !== identity.version) {
1660
- invalid(code);
1661
- }
1662
- if (typeof parsed.license !== 'string') invalid(code);
1663
- try {
1664
- validateSpdxExpression(parsed.license);
1665
- } catch {
1666
- invalid(code);
1667
- }
1668
- if (!stringArrayEquals(parsed.files, manifestFilePaths)) invalid(code);
1669
- return parsed.license;
1670
- }
1671
-
1672
- /**
1673
- * Validate compact-JCS LICENSE evidence and exact per-asset inheritance.
1674
- *
1675
- * @param {ThemePackageFile[]} files checked package files
1676
- * @param {UnknownRecord[]} assets checked manifest assets
1677
- * @param {string} packageExpression package.json expression
1678
- * @param {string} code failure diagnostic
1679
- * @returns {void}
1680
- */
1681
- function validateLicenseEvidence(files, assets, packageExpression, code) {
1682
- const licenseFile = requirePackageFile(files, 'LICENSE', code);
1683
- const evidence = exactRecord(
1684
- parseRetainedJson(licenseFile.bytes, code),
1685
- LICENSE_EVIDENCE_KEYS,
1686
- code,
1687
- );
1688
- requireCanonicalBytes(licenseFile.bytes, evidence, code);
1689
- if (
1690
- evidence.profile !== 'gala-theme-license-evidence-v2' ||
1691
- evidence.packageExpression !== packageExpression ||
1692
- !Array.isArray(evidence.assetOverrides) ||
1693
- evidence.assetOverrides.length > 256 ||
1694
- !Array.isArray(evidence.catalogEntries) ||
1695
- evidence.catalogEntries.length < 1 ||
1696
- evidence.catalogEntries.length > 512
1697
- ) {
1698
- invalid(code);
1699
- }
1700
-
1701
- /** @type {Map<string, string>} */
1702
- const overrides = new Map();
1703
- /** @type {UnknownRecord | undefined} */
1704
- let previousOverride;
1705
- for (const candidate of evidence.assetOverrides) {
1706
- const override = exactRecord(candidate, LICENSE_OVERRIDE_KEYS, code);
1707
- const path = requireRepositoryPath(override.path, code);
1708
- if (overrides.has(path)) invalid(code);
1709
- if (typeof override.expression !== 'string') invalid(code);
1710
- try {
1711
- validateSpdxExpression(override.expression);
1712
- } catch {
1713
- invalid(code);
1714
- }
1715
- if (
1716
- previousOverride !== undefined &&
1717
- compareJcsValues(previousOverride, override) >= 0
1718
- ) {
1719
- invalid(code);
1720
- }
1721
- previousOverride = override;
1722
- overrides.set(path, override.expression);
1723
- }
1724
-
1725
- const expressions = [packageExpression];
1726
- for (const asset of assets) {
1727
- if (typeof asset.path !== 'string' || typeof asset.license !== 'string') {
1728
- invalid(code);
1729
- }
1730
- expressions.push(asset.license);
1731
- const inherited = asset.license === packageExpression;
1732
- const override = overrides.get(asset.path);
1733
- if (asset.mediaType === 'text/css') {
1734
- if (!inherited || override !== undefined) invalid(code);
1735
- } else if (
1736
- (inherited && override !== undefined) ||
1737
- (!inherited && override !== asset.license)
1738
- ) {
1739
- invalid(code);
1740
- }
1741
- }
1742
- const nonCssPaths = new Set(
1743
- assets
1744
- .filter((asset) => asset.mediaType !== 'text/css')
1745
- .map((asset) => String(asset.path)),
1746
- );
1747
- if ([...overrides.keys()].some((path) => !nonCssPaths.has(path))) {
1748
- invalid(code);
1749
- }
1750
-
1751
- try {
1752
- validateSpdxCatalogEvidence(expressions, evidence);
1753
- } catch {
1754
- invalid(code);
1755
- }
1756
- }
1757
-
1758
- /**
1759
- * Validate the retained source byte subset that precedes pinned CSS parsing.
1760
- *
1761
- * @param {Uint8Array} bytes stylesheet bytes
1762
- * @param {string} code failure diagnostic
1763
- * @returns {void}
1764
- */
1765
- function validateCssSourceBytes(bytes, code) {
1766
- if (
1767
- bytes.byteLength === 0 ||
1768
- bytes.byteLength > 1_048_576 ||
1769
- (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf)
1770
- ) {
1771
- invalid(code);
1772
- }
1773
- let source;
1774
- try {
1775
- source = FATAL_UTF8_DECODER.decode(bytes);
1776
- } catch {
1777
- invalid(code);
1778
- }
1779
- const forbiddenSourceByte = [...source].some((character) => {
1780
- const codePoint = character.codePointAt(0);
1781
- return (
1782
- codePoint === undefined ||
1783
- (codePoint !== 0x09 &&
1784
- codePoint !== 0x0a &&
1785
- (codePoint < 0x20 || codePoint > 0x7e))
1786
- );
1787
- });
1788
- if (
1789
- normalizeNfc17(source) !== source ||
1790
- source.includes('\\') ||
1791
- forbiddenSourceByte
1792
- ) {
1793
- invalid(code);
1794
- }
1795
- }
1796
-
1797
- /**
1798
- * Validate parser-reported CSS resource counters.
1799
- *
1800
- * @param {unknown} value metric record
1801
- * @param {Record<string, number>} totals package aggregate counters
1802
- * @param {string} code failure diagnostic
1803
- * @returns {void}
1804
- */
1805
- function validateCssMetrics(value, totals, code) {
1806
- const metrics = exactRecord(value, CSS_METRIC_KEYS, code);
1807
- for (const key of CSS_METRIC_KEYS) {
1808
- const metric = metrics[key];
1809
- const limit = CSS_METRIC_LIMITS[key];
1810
- if (
1811
- !Number.isInteger(metric) ||
1812
- Number(metric) < 0 ||
1813
- limit === undefined ||
1814
- Number(metric) > limit
1815
- ) {
1816
- invalid(code);
1817
- }
1818
- if (key === 'maximumBlockFunctionDepth') {
1819
- totals[key] = Math.max(totals[key] ?? 0, Number(metric));
1820
- } else {
1821
- totals[key] = (totals[key] ?? 0) + Number(metric);
1822
- }
1823
- }
1824
- }
1825
-
1826
- /**
1827
- * Validate tokens.css's exact parser-derived custom-property projection.
1828
- *
1829
- * @param {unknown} value token declaration evidence
1830
- * @param {UnknownRecord} theme checked theme
1831
- * @param {UnknownRecord} stylingContract checked styling contract
1832
- * @param {string} code failure diagnostic
1833
- * @returns {void}
1834
- */
1835
- function validateTokenDeclarations(value, theme, stylingContract, code) {
1836
- const declarations = exactRecord(value, TOKEN_DECLARATION_KEYS, code);
1837
- const palettes = exactRecord(
1838
- stylingContract.resolvedPaletteSelectors,
1839
- PALETTE_SELECTOR_KEYS,
1840
- code,
1841
- );
1842
- if (
1843
- declarations.lightSelector !== palettes.light ||
1844
- declarations.darkSelector !== palettes.dark
1845
- ) {
1846
- invalid(code);
1847
- }
1848
- const light = isPlainObject(declarations.light)
1849
- ? declarations.light
1850
- : invalid(code);
1851
- const dark = isPlainObject(declarations.dark)
1852
- ? declarations.dark
1853
- : invalid(code);
1854
- const expectedProperties = THEME_TOKEN_CATALOG.map(
1855
- ([key]) => `--gala-${key}`,
1856
- ).sort();
1857
- if (
1858
- !stringArrayEquals(Object.keys(light).sort(), expectedProperties) ||
1859
- !stringArrayEquals(Object.keys(dark).sort(), expectedProperties)
1860
- ) {
1861
- invalid(code);
1862
- }
1863
- if (!Array.isArray(theme.tokens)) invalid(code);
1864
- for (const candidate of theme.tokens) {
1865
- const token = exactRecord(candidate, TOKEN_KEYS, code);
1866
- const property = `--gala-${String(token.key)}`;
1867
- if (light[property] !== token.light || dark[property] !== token.dark) {
1868
- invalid(code);
1869
- }
1870
- }
1871
- }
1872
-
1873
- /**
1874
- * Bind one retained parser-evidence envelope to the exact runner identity in
1875
- * the independently validated immutable fixture release.
1876
- *
1877
- * This is the T03/S2 trust boundary: S2 owns isolated execution of the
1878
- * integrity-verified runner bytes; T03 accepts only that runner's retained
1879
- * evidence envelope and verifies its identity against the release ledger.
1880
- * Copying these fields is not proof that the runner executed, so callers must
1881
- * obtain the envelope directly from the trusted S2 parser runner.
1882
- *
1883
- * @param {unknown} value parser-evidence envelope
1884
- * @param {UnknownRecord} fixtureRelease independently validated fixture release
1885
- * @param {'css' | 'binary'} expectedRunnerId required runner identity
1886
- * @param {string} code failure diagnostic
1887
- * @returns {unknown[]} retained parser-evidence records
1888
- */
1889
- function requireTrustedRunnerEvidence(
1890
- value,
1891
- fixtureRelease,
1892
- expectedRunnerId,
1893
- code,
1894
- ) {
1895
- const envelope = exactRecord(value, RUNNER_EVIDENCE_KEYS, code);
1896
- const runner = exactRecord(envelope.runner, FIXTURE_RUNNER_KEYS, code);
1897
- if (runner.runnerId !== expectedRunnerId) invalid(code);
1898
- requireSemver(runner.version, code);
1899
- requireDigest(runner.executableDigest, code);
1900
- if (!Array.isArray(fixtureRelease.runners)) invalid(code);
1901
- const matchingRunners = fixtureRelease.runners
1902
- .map((candidate) => exactRecord(candidate, FIXTURE_RUNNER_KEYS, code))
1903
- .filter((candidate) => candidate.runnerId === expectedRunnerId);
1904
- if (
1905
- matchingRunners.length !== 1 ||
1906
- !jcsEqual(runner, matchingRunners[0]) ||
1907
- !Array.isArray(envelope.records)
1908
- ) {
1909
- invalid(code);
1910
- }
1911
- return envelope.records;
1912
- }
1913
-
1914
- /**
1915
- * Validate trusted pinned-parser CSS evidence and all semantic relations it
1916
- * exposes. The parser owns CSS tokenization, selector/property grammar, and
1917
- * at-rule context; this validator owns exact source bytes, counts, layers,
1918
- * token values, hooks, and local asset closure.
1919
- *
1920
- * @param {unknown} value trusted CSS parser-evidence envelope
1921
- * @param {UnknownRecord} theme checked theme
1922
- * @param {UnknownRecord} stylingContract checked styling contract
1923
- * @param {UnknownRecord} fixtureRelease independently validated fixture release
1924
- * @param {ThemePackageFile[]} files checked package files
1925
- * @param {UnknownRecord[]} assets checked manifest assets
1926
- * @returns {void}
1927
- */
1928
- function validateCssEvidence(
1929
- value,
1930
- theme,
1931
- stylingContract,
1932
- fixtureRelease,
1933
- files,
1934
- assets,
1935
- ) {
1936
- const code = 'THEME_CSS_EVIDENCE_INVALID';
1937
- const records = requireTrustedRunnerEvidence(
1938
- value,
1939
- fixtureRelease,
1940
- 'css',
1941
- code,
1942
- );
1943
- if (
1944
- !Array.isArray(theme.stylesheets) ||
1945
- !Array.isArray(theme.cssLayers) ||
1946
- records.length !== theme.stylesheets.length
1947
- ) {
1948
- invalid(code);
1949
- }
1950
- /** @type {Record<string, number>} */
1951
- const metricTotals = {};
1952
- const usedHooks = new Set();
1953
- const referencedAssets = new Set();
1954
- const fontAssets = new Set();
1955
- let cssBytes = 0;
1956
- for (let index = 0; index < records.length; index += 1) {
1957
- const path = theme.stylesheets[index];
1958
- const layer = theme.cssLayers[index];
1959
- if (typeof path !== 'string' || typeof layer !== 'string') invalid(code);
1960
- const keys =
1961
- path === 'tokens.css'
1962
- ? [...CSS_EVIDENCE_BASE_KEYS, 'tokenDeclarations']
1963
- : CSS_EVIDENCE_BASE_KEYS;
1964
- const evidence = exactRecord(records[index], keys, code);
1965
- const file = requirePackageFile(files, path, code);
1966
- validateCssSourceBytes(file.bytes, code);
1967
- cssBytes += file.bytes.byteLength;
1968
- if (
1969
- evidence.path !== path ||
1970
- evidence.profile !== fixtureRelease.browserPolicyRef ||
1971
- evidence.disposition !== 'accepted' ||
1972
- evidence.byteLength !== String(file.bytes.byteLength) ||
1973
- evidence.sha256 !== sha256Tagged(file.bytes) ||
1974
- evidence.outerLayer !== layer
1975
- ) {
1976
- invalid(code);
1977
- }
1978
- validateCssMetrics(evidence.metrics, metricTotals, code);
1979
- const rowHooks = requireSortedStringSet(
1980
- evidence.usedSlotHookIds,
1981
- 0,
1982
- 64,
1983
- (entry) => requirePlainLabel(entry, code),
1984
- code,
1985
- );
1986
- const rowAssets = requireSortedStringSet(
1987
- evidence.referencedAssetPaths,
1988
- 0,
1989
- 256,
1990
- (entry) => requireRepositoryPath(entry, code),
1991
- code,
1992
- );
1993
- const rowFonts = requireSortedStringSet(
1994
- evidence.fontFaceAssetPaths,
1995
- 0,
1996
- 128,
1997
- (entry) => requireRepositoryPath(entry, code),
1998
- code,
1999
- );
2000
- rowHooks.forEach((hook) => usedHooks.add(hook));
2001
- rowAssets.forEach((asset) => referencedAssets.add(asset));
2002
- rowFonts.forEach((asset) => fontAssets.add(asset));
2003
- if (path === 'tokens.css') {
2004
- validateTokenDeclarations(
2005
- evidence.tokenDeclarations,
2006
- theme,
2007
- stylingContract,
2008
- code,
2009
- );
2010
- }
2011
- }
2012
- if (cssBytes > 4_194_304) invalid(code);
2013
- for (const [key, limit] of Object.entries(CSS_METRIC_LIMITS)) {
2014
- if ((metricTotals[key] ?? 0) > limit) invalid(code);
2015
- }
2016
-
2017
- const hooks = [...usedHooks].sort(compareJcsStrings);
2018
- if (!stringArrayEquals(theme.slotHooks, hooks)) invalid(code);
2019
- const publicHooks = new Set(
2020
- Array.isArray(stylingContract.publicThemeSlotHooks)
2021
- ? stylingContract.publicThemeSlotHooks.map((candidate) =>
2022
- String(exactRecord(candidate, STYLE_HOOK_KEYS, code).hookId),
2023
- )
2024
- : invalid(code),
2025
- );
2026
- if (hooks.some((hook) => !publicHooks.has(hook))) invalid(code);
2027
-
2028
- const nonCssAssets = assets.filter((asset) => asset.mediaType !== 'text/css');
2029
- const nonCssPaths = nonCssAssets
2030
- .map((asset) => String(asset.path))
2031
- .sort(compareUtf8);
2032
- if (
2033
- !stringArrayEquals([...referencedAssets].sort(compareUtf8), nonCssPaths)
2034
- ) {
2035
- invalid(code);
2036
- }
2037
- const mediaByPath = new Map(
2038
- nonCssAssets.map((asset) => [String(asset.path), String(asset.mediaType)]),
2039
- );
2040
- if (
2041
- [...fontAssets].some((path) => mediaByPath.get(path) !== 'font/woff2') ||
2042
- nonCssAssets
2043
- .filter((asset) => asset.mediaType === 'font/woff2')
2044
- .some((asset) => !fontAssets.has(String(asset.path)))
2045
- ) {
2046
- invalid(code);
2047
- }
2048
- }
2049
-
2050
- /**
2051
- * Validate trusted pinned SVG/binary parser evidence against exact members.
2052
- *
2053
- * @param {unknown} value trusted passive parser-evidence envelope
2054
- * @param {ThemePackageFile[]} files checked package files
2055
- * @param {UnknownRecord[]} assets checked manifest assets
2056
- * @param {UnknownRecord} fixtureRelease independently validated fixture release
2057
- * @returns {void}
2058
- */
2059
- function validatePassiveAssetEvidence(value, files, assets, fixtureRelease) {
2060
- const code = 'THEME_PASSIVE_ASSET_EVIDENCE_INVALID';
2061
- const records = requireTrustedRunnerEvidence(
2062
- value,
2063
- fixtureRelease,
2064
- 'binary',
2065
- code,
2066
- );
2067
- const nonCssAssets = assets
2068
- .filter((asset) => asset.mediaType !== 'text/css')
2069
- .sort((left, right) => compareUtf8(String(left.path), String(right.path)));
2070
- if (records.length !== nonCssAssets.length) invalid(code);
2071
- for (let index = 0; index < nonCssAssets.length; index += 1) {
2072
- const asset = nonCssAssets[index];
2073
- if (!asset || typeof asset.path !== 'string') invalid(code);
2074
- const evidence = exactRecord(records[index], PASSIVE_EVIDENCE_KEYS, code);
2075
- const file = requirePackageFile(files, asset.path, code);
2076
- const limit = PASSIVE_SOURCE_LIMITS[String(asset.mediaType)];
2077
- if (
2078
- limit === undefined ||
2079
- file.bytes.byteLength === 0 ||
2080
- file.bytes.byteLength > limit ||
2081
- evidence.path !== asset.path ||
2082
- evidence.mediaType !== asset.mediaType ||
2083
- evidence.profile !==
2084
- (asset.mediaType === 'image/svg+xml'
2085
- ? 'gala-passive-svg-v2'
2086
- : fixtureRelease.binaryAssetProfile) ||
2087
- evidence.disposition !== 'accepted' ||
2088
- evidence.byteLength !== String(file.bytes.byteLength) ||
2089
- evidence.sha256 !== sha256Tagged(file.bytes)
2090
- ) {
2091
- invalid(code);
2092
- }
2093
- }
2094
- }
2095
-
2096
- /**
2097
- * Validate the exact closed theme package, byte budgets, manifests, licenses,
2098
- * and trusted parser-evidence projections.
2099
- *
2100
- * `context.packageFiles` is the extraction layer's complete stripped-member
2101
- * observation. `fixtureRelease` is independently validated immutable shared
2102
- * infra state. `cssEvidence` and `passiveAssetEvidence` are retained envelopes
2103
- * emitted by the isolated S2 runners whose exact identities appear in that
2104
- * release; they are context, never fields invented on theme.json. The caller
2105
- * owns the trusted execution boundary: copying a runner ledger row into an
2106
- * envelope does not prove execution. `acceptedBudgetCeilings` is the
2107
- * independently selected release-policy ceiling and is mandatory because
2108
- * DEC-097 deliberately defines no evidence-free universal package ceiling.
2109
- *
2110
- * @param {unknown} value complete theme contract
2111
- * @param {unknown} context package files, parser evidence, styling catalog, and policy ceilings
2112
- * @returns {void}
2113
- */
2114
- export function validateThemePackage(value, context) {
2115
- validateThemeContract(value);
2116
- const code = 'THEME_PACKAGE_INVALID';
2117
- const theme = /** @type {UnknownRecord} */ (value);
2118
- const retained = exactRecord(
2119
- context,
2120
- [
2121
- 'packageFiles',
2122
- 'cssEvidence',
2123
- 'passiveAssetEvidence',
2124
- 'stylingContract',
2125
- 'fixtureRelease',
2126
- 'acceptedBudgetCeilings',
2127
- ],
2128
- code,
2129
- );
2130
- const stylingContract = isPlainObject(retained.stylingContract)
2131
- ? retained.stylingContract
2132
- : invalid(code);
2133
- const fixtureRelease = isPlainObject(retained.fixtureRelease)
2134
- ? retained.fixtureRelease
2135
- : invalid(code);
2136
- try {
2137
- validateThemeFixtureRelease(fixtureRelease, {
2138
- bytes: canonicalizeJcsBytes(fixtureRelease),
2139
- stylingContractDigest: theme.stylingContractDigest,
2140
- });
2141
- } catch {
2142
- invalid(code);
2143
- }
2144
- if (
2145
- stylingContract.catalogDigest !== theme.stylingContractDigest ||
2146
- fixtureRelease.contractVersion !== theme.contractVersion ||
2147
- fixtureRelease.browserPolicyRef !== theme.browserPolicyRef ||
2148
- fixtureRelease.fixtureDigest !== theme.fixtureDigest ||
2149
- fixtureRelease.stylingContractDigest !== theme.stylingContractDigest ||
2150
- !Array.isArray(fixtureRelease.fixtures) ||
2151
- !stringArrayEquals(
2152
- theme.fixtures,
2153
- fixtureRelease.fixtures.map((candidate) =>
2154
- String(exactRecord(candidate, FIXTURE_DEFINITION_KEYS, code).fixtureId),
2155
- ),
2156
- )
2157
- ) {
2158
- invalid(code);
2159
- }
2160
- const files = validatePackageFiles(retained.packageFiles, code);
2161
- const budgets = validateBudgets(theme.budgets, code);
2162
- const ceilings = validateBudgets(retained.acceptedBudgetCeilings, code);
2163
- if (
2164
- budgets.maximumFileBytes > ceilings.maximumFileBytes ||
2165
- budgets.maximumTotalBytes > ceilings.maximumTotalBytes ||
2166
- budgets.maximumFiles > ceilings.maximumFiles
2167
- ) {
2168
- invalid(code);
2169
- }
2170
-
2171
- let totalBytes = 0n;
2172
- for (const file of files) {
2173
- const byteLength = BigInt(file.bytes.byteLength);
2174
- totalBytes += byteLength;
2175
- if (byteLength > budgets.maximumFileBytes) invalid(code);
2176
- }
2177
- if (
2178
- files.length > budgets.maximumFiles ||
2179
- totalBytes > budgets.maximumTotalBytes
2180
- ) {
2181
- invalid(code);
2182
- }
2183
-
2184
- if (!Array.isArray(theme.assets) || !Array.isArray(theme.stylesheets)) {
2185
- invalid(code);
2186
- }
2187
- const assets = theme.assets.map(validatePassiveAsset);
2188
- const stylesheetSet = new Set(theme.stylesheets.map((path) => String(path)));
2189
- const assetByPath = new Map(
2190
- assets.map((asset) => [String(asset.path), asset]),
2191
- );
2192
- if (assetByPath.size !== assets.length) invalid(code);
2193
- for (const path of stylesheetSet) {
2194
- const asset = assetByPath.get(path);
2195
- if (!asset || asset.mediaType !== 'text/css') invalid(code);
2196
- }
2197
- const nonCssAssets = assets.filter((asset) => {
2198
- if (typeof asset.path !== 'string') invalid(code);
2199
- if (stylesheetSet.has(asset.path)) return false;
2200
- if (
2201
- asset.mediaType === 'text/css' ||
2202
- mediaTypeForAssetPath(asset.path, code) !== asset.mediaType
2203
- ) {
2204
- invalid(code);
2205
- }
2206
- return true;
2207
- });
2208
- if (assets.length !== stylesheetSet.size + nonCssAssets.length) invalid(code);
2209
-
2210
- const expectedPaths = [
2211
- 'package.json',
2212
- 'theme.json',
2213
- ...theme.stylesheets.map((path) => String(path)),
2214
- 'LICENSE',
2215
- 'README.md',
2216
- ...nonCssAssets.map((asset) => String(asset.path)),
2217
- ].sort(compareUtf8);
2218
- const actualPaths = files.map((file) => file.path).sort(compareUtf8);
2219
- if (!stringArrayEquals(actualPaths, expectedPaths)) invalid(code);
2220
-
2221
- for (const asset of assets) {
2222
- if (typeof asset.path !== 'string') invalid(code);
2223
- const file = requirePackageFile(files, asset.path, code);
2224
- validateAssetBytes(asset, file, code);
2225
- if (asset.mediaType !== 'text/css') {
2226
- const limit = PASSIVE_SOURCE_LIMITS[String(asset.mediaType)];
2227
- if (
2228
- limit === undefined ||
2229
- file.bytes.byteLength === 0 ||
2230
- file.bytes.byteLength > limit
2231
- ) {
2232
- invalid(code);
2233
- }
2234
- }
2235
- }
2236
-
2237
- const themeFile = requirePackageFile(files, 'theme.json', code);
2238
- requireCanonicalBytes(themeFile.bytes, theme, code);
2239
- const packageManifestPaths = [
2240
- 'theme.json',
2241
- ...theme.stylesheets.map((path) => String(path)),
2242
- ...nonCssAssets.map((asset) => String(asset.path)),
2243
- ].sort(compareUtf8);
2244
- const packageExpression = validatePackageJson(
2245
- files,
2246
- theme,
2247
- packageManifestPaths,
2248
- code,
2249
- );
2250
- validateLicenseEvidence(files, assets, packageExpression, code);
2251
- validateCssEvidence(
2252
- retained.cssEvidence,
2253
- theme,
2254
- stylingContract,
2255
- fixtureRelease,
2256
- files,
2257
- assets,
2258
- );
2259
- validatePassiveAssetEvidence(
2260
- retained.passiveAssetEvidence,
2261
- files,
2262
- assets,
2263
- fixtureRelease,
2264
- );
2265
-
2266
- let integrity;
2267
- try {
2268
- integrity = computeThemePackageIntegrity(theme, files);
2269
- } catch {
2270
- invalid(code);
2271
- }
2272
- if (theme.integrity !== integrity) invalid(code);
2273
- }
2274
-
2275
- /**
2276
- * Validate the complete DEC-097 theme composition across the selected lock,
2277
- * template catalog, package, fixture release, and conformance result.
2278
- *
2279
- * The context ledger is exact and consists only of independently retained or
2280
- * integrity-verified inputs:
2281
- *
2282
- * - `lockedTheme` and `lockedTemplate`: selected lock rows;
2283
- * - styling/fixture/result objects plus their exact compact-JCS bytes;
2284
- * - complete extracted package-file observations;
2285
- * - pinned CSS/passive parser evidence; and
2286
- * - selected release-policy budget ceilings.
2287
- *
2288
- * @param {unknown} value complete theme-contract instance
2289
- * @param {unknown} context exact retained validation context
2290
- * @returns {void}
2291
- */
2292
- export function validateThemeComposition(value, context) {
2293
- validateThemeContract(value);
2294
- const code = 'THEME_COMPOSITION_INVALID';
2295
- const theme = /** @type {UnknownRecord} */ (value);
2296
- const retained = exactRecord(
2297
- context,
2298
- [
2299
- 'lockedTheme',
2300
- 'lockedTemplate',
2301
- 'templateStylingContract',
2302
- 'templateStylingContractBytes',
2303
- 'fixtureRelease',
2304
- 'fixtureReleaseBytes',
2305
- 'conformanceResult',
2306
- 'conformanceResultBytes',
2307
- 'packageFiles',
2308
- 'cssEvidence',
2309
- 'passiveAssetEvidence',
2310
- 'acceptedBudgetCeilings',
2311
- ],
2312
- code,
2313
- );
2314
- const lockedTheme = exactRecord(
2315
- retained.lockedTheme,
2316
- [
2317
- 'package',
2318
- 'version',
2319
- 'integrity',
2320
- 'registry',
2321
- 'contractVersion',
2322
- 'compatibleWith',
2323
- ],
2324
- code,
2325
- );
2326
- const lockedTemplate = exactRecord(
2327
- retained.lockedTemplate,
2328
- [
2329
- 'package',
2330
- 'version',
2331
- 'integrity',
2332
- 'registry',
2333
- 'contractVersion',
2334
- 'compatibleWith',
2335
- 'templateModules',
2336
- ],
2337
- code,
2338
- );
2339
- const stylingContract = isPlainObject(retained.templateStylingContract)
2340
- ? retained.templateStylingContract
2341
- : invalid(code);
2342
- const fixtureRelease = isPlainObject(retained.fixtureRelease)
2343
- ? retained.fixtureRelease
2344
- : invalid(code);
2345
- const conformanceResult = isPlainObject(retained.conformanceResult)
2346
- ? retained.conformanceResult
2347
- : invalid(code);
2348
-
2349
- const themeIdentity = parsePackageExact(theme.package, code);
2350
- if (
2351
- lockedTheme.package !== themeIdentity.name ||
2352
- lockedTheme.version !== themeIdentity.version ||
2353
- lockedTheme.contractVersion !== theme.contractVersion ||
2354
- lockedTheme.compatibleWith !== theme.templateRange ||
2355
- lockedTemplate.package !== '@rathnasgala2/template' ||
2356
- !stringArrayEquals(lockedTemplate.templateModules, [])
2357
- ) {
2358
- invalid(code);
2359
- }
2360
- requireSemver(lockedTheme.version, code);
2361
- requireDigest(lockedTheme.integrity, code);
2362
- requireSemver(lockedTheme.contractVersion, code);
2363
- requireSemverRange(lockedTheme.compatibleWith, code);
2364
- requireSemver(lockedTemplate.version, code);
2365
- requireDigest(lockedTemplate.integrity, code);
2366
- requireSemver(lockedTemplate.contractVersion, code);
2367
- requireSemverRange(lockedTemplate.compatibleWith, code);
2368
- try {
2369
- if (
2370
- !satisfiesSemverRange(
2371
- String(lockedTemplate.version),
2372
- String(theme.templateRange),
2373
- )
2374
- ) {
2375
- invalid(code);
2376
- }
2377
- } catch {
2378
- invalid(code);
2379
- }
2380
- if (theme.integrity === lockedTheme.integrity) invalid(code);
2381
-
2382
- validateTemplateStylingContract(stylingContract, {
2383
- bytes: retained.templateStylingContractBytes,
2384
- lockedTemplate,
2385
- });
2386
- if (
2387
- stylingContract.contractVersion !== lockedTemplate.contractVersion ||
2388
- theme.stylingContractDigest !== stylingContract.catalogDigest
2389
- ) {
2390
- invalid(code);
2391
- }
2392
- validateThemeFixtureRelease(fixtureRelease, {
2393
- bytes: retained.fixtureReleaseBytes,
2394
- stylingContractDigest: stylingContract.catalogDigest,
2395
- });
2396
- if (
2397
- theme.contractVersion !== fixtureRelease.contractVersion ||
2398
- theme.browserPolicyRef !== fixtureRelease.browserPolicyRef ||
2399
- theme.fixtureDigest !== fixtureRelease.fixtureDigest ||
2400
- theme.stylingContractDigest !== fixtureRelease.stylingContractDigest ||
2401
- !Array.isArray(fixtureRelease.fixtures) ||
2402
- !stringArrayEquals(
2403
- theme.fixtures,
2404
- fixtureRelease.fixtures.map((candidate) =>
2405
- String(exactRecord(candidate, FIXTURE_DEFINITION_KEYS, code).fixtureId),
2406
- ),
2407
- )
2408
- ) {
2409
- invalid(code);
2410
- }
2411
-
2412
- validateThemePackage(theme, {
2413
- packageFiles: retained.packageFiles,
2414
- cssEvidence: retained.cssEvidence,
2415
- passiveAssetEvidence: retained.passiveAssetEvidence,
2416
- stylingContract,
2417
- fixtureRelease,
2418
- acceptedBudgetCeilings: retained.acceptedBudgetCeilings,
2419
- });
2420
-
2421
- let inputDigest;
2422
- try {
2423
- inputDigest = computeThemeConformanceInputDigest(
2424
- theme,
2425
- retained.packageFiles,
2426
- );
2427
- } catch {
2428
- invalid(code);
2429
- }
2430
- validateThemeConformanceResult(conformanceResult, {
2431
- bytes: retained.conformanceResultBytes,
2432
- fixtureRelease,
2433
- themeConformanceInputDigest: inputDigest,
2434
- });
2435
- if (
2436
- conformanceResult.overallState !== 'pass' ||
2437
- theme.evidenceDigest !== conformanceResult.evidenceDigest
2438
- ) {
2439
- invalid(code);
2440
- }
2441
- }