@tokens-studio/tokenscript-schemas 0.0.11 → 0.0.13

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 (307) hide show
  1. package/dist/cli/index.cjs +31 -8
  2. package/dist/cli/index.cjs.map +1 -1
  3. package/dist/cli/index.js +30 -8
  4. package/dist/cli/index.js.map +1 -1
  5. package/package.json +2 -1
  6. package/src/bundler/bundle-schema.ts +146 -0
  7. package/src/bundler/index.ts +151 -0
  8. package/src/bundler/schema-dependency-resolver.ts +299 -0
  9. package/src/bundler/selective-bundler.test.ts +94 -0
  10. package/src/bundler/selective-bundler.ts +159 -0
  11. package/src/bundler/types.ts +93 -0
  12. package/src/bundler/utils.ts +74 -0
  13. package/src/cli/commands/bundle.integration.test.ts +153 -0
  14. package/src/cli/commands/bundle.test.ts +57 -0
  15. package/src/cli/commands/bundle.ts +237 -0
  16. package/src/cli/commands/list.ts +109 -0
  17. package/src/cli/config-schema.ts +36 -0
  18. package/src/cli/index.ts +50 -0
  19. package/src/cli/output-generator.ts +63 -0
  20. package/src/downloader/index.ts +248 -0
  21. package/src/downloader/types.ts +48 -0
  22. package/src/index.ts +8 -0
  23. package/src/schemas/functions/adjust_chroma/adjust-chroma.tokenscript +27 -0
  24. package/src/schemas/functions/adjust_chroma/schema.json +48 -0
  25. package/src/schemas/functions/adjust_chroma/unit.test.ts +76 -0
  26. package/src/schemas/functions/adjust_hue/adjust-hue.tokenscript +32 -0
  27. package/src/schemas/functions/adjust_hue/schema.json +48 -0
  28. package/src/schemas/functions/adjust_hue/unit.test.ts +68 -0
  29. package/src/schemas/functions/adjust_lightness/adjust-lightness.tokenscript +31 -0
  30. package/src/schemas/functions/adjust_lightness/schema.json +48 -0
  31. package/src/schemas/functions/adjust_lightness/unit.test.ts +88 -0
  32. package/src/schemas/functions/adjust_to_contrast/adjust-to-contrast.tokenscript +131 -0
  33. package/src/schemas/functions/adjust_to_contrast/schema.json +56 -0
  34. package/src/schemas/functions/adjust_to_contrast/unit.test.ts +81 -0
  35. package/src/schemas/functions/alpha_blend/alpha-blend.tokenscript +46 -0
  36. package/src/schemas/functions/alpha_blend/schema.json +28 -0
  37. package/src/schemas/functions/alpha_blend/unit.test.ts +135 -0
  38. package/src/schemas/functions/alpha_scale/alpha-scale.tokenscript +38 -0
  39. package/src/schemas/functions/alpha_scale/schema.json +24 -0
  40. package/src/schemas/functions/alpha_scale/unit.test.ts +50 -0
  41. package/src/schemas/functions/analogous/analogous.tokenscript +47 -0
  42. package/src/schemas/functions/analogous/schema.json +28 -0
  43. package/src/schemas/functions/analogous/unit.test.ts +64 -0
  44. package/src/schemas/functions/apca_contrast/apca-contrast.tokenscript +129 -0
  45. package/src/schemas/functions/apca_contrast/schema.json +24 -0
  46. package/src/schemas/functions/apca_contrast/unit.test.ts +259 -0
  47. package/src/schemas/functions/are_similar/are-similar.tokenscript +24 -0
  48. package/src/schemas/functions/are_similar/schema.json +57 -0
  49. package/src/schemas/functions/are_similar/unit.test.ts +57 -0
  50. package/src/schemas/functions/auto_text_color/auto-text-color.tokenscript +41 -0
  51. package/src/schemas/functions/auto_text_color/schema.json +53 -0
  52. package/src/schemas/functions/auto_text_color/unit.test.ts +122 -0
  53. package/src/schemas/functions/best_contrast/best-contrast.tokenscript +66 -0
  54. package/src/schemas/functions/best_contrast/schema.json +24 -0
  55. package/src/schemas/functions/best_contrast/unit.test.ts +64 -0
  56. package/src/schemas/functions/chroma/chroma.tokenscript +12 -0
  57. package/src/schemas/functions/chroma/schema.json +44 -0
  58. package/src/schemas/functions/chroma/unit.test.ts +86 -0
  59. package/src/schemas/functions/clamp_chroma/clamp-chroma.tokenscript +21 -0
  60. package/src/schemas/functions/clamp_chroma/schema.json +52 -0
  61. package/src/schemas/functions/clamp_chroma/unit.test.ts +60 -0
  62. package/src/schemas/functions/clamp_lightness/clamp-lightness.tokenscript +21 -0
  63. package/src/schemas/functions/clamp_lightness/schema.json +52 -0
  64. package/src/schemas/functions/clamp_lightness/unit.test.ts +76 -0
  65. package/src/schemas/functions/clamp_to_gamut/clamp-to-gamut.tokenscript +41 -0
  66. package/src/schemas/functions/clamp_to_gamut/schema.json +20 -0
  67. package/src/schemas/functions/clamp_to_gamut/unit.test.ts +167 -0
  68. package/src/schemas/functions/complement/complement.tokenscript +21 -0
  69. package/src/schemas/functions/complement/schema.json +20 -0
  70. package/src/schemas/functions/complement/unit.test.ts +81 -0
  71. package/src/schemas/functions/contrast_ratio/contrast-ratio.tokenscript +36 -0
  72. package/src/schemas/functions/contrast_ratio/schema.json +24 -0
  73. package/src/schemas/functions/contrast_ratio/unit.test.ts +91 -0
  74. package/src/schemas/functions/cooler/cooler.tokenscript +45 -0
  75. package/src/schemas/functions/cooler/schema.json +43 -0
  76. package/src/schemas/functions/cooler/unit.test.ts +69 -0
  77. package/src/schemas/functions/darken/darken.tokenscript +37 -0
  78. package/src/schemas/functions/darken/schema.json +24 -0
  79. package/src/schemas/functions/darken/unit.test.ts +105 -0
  80. package/src/schemas/functions/delta_e_2000/delta-e-2000.tokenscript +184 -0
  81. package/src/schemas/functions/delta_e_2000/schema.json +36 -0
  82. package/src/schemas/functions/delta_e_2000/unit.test.ts +243 -0
  83. package/src/schemas/functions/delta_e_76/delta-e-76.tokenscript +45 -0
  84. package/src/schemas/functions/delta_e_76/schema.json +24 -0
  85. package/src/schemas/functions/delta_e_76/unit.test.ts +123 -0
  86. package/src/schemas/functions/delta_e_ok/delta-e-ok.tokenscript +43 -0
  87. package/src/schemas/functions/delta_e_ok/schema.json +24 -0
  88. package/src/schemas/functions/delta_e_ok/unit.test.ts +235 -0
  89. package/src/schemas/functions/desaturate/desaturate.tokenscript +32 -0
  90. package/src/schemas/functions/desaturate/schema.json +24 -0
  91. package/src/schemas/functions/desaturate/unit.test.ts +54 -0
  92. package/src/schemas/functions/distributed/distributed.tokenscript +54 -0
  93. package/src/schemas/functions/distributed/schema.json +32 -0
  94. package/src/schemas/functions/distributed/unit.test.ts +58 -0
  95. package/src/schemas/functions/diverging/diverging.tokenscript +58 -0
  96. package/src/schemas/functions/diverging/schema.json +32 -0
  97. package/src/schemas/functions/diverging/unit.test.ts +70 -0
  98. package/src/schemas/functions/grayscale/grayscale.tokenscript +17 -0
  99. package/src/schemas/functions/grayscale/schema.json +20 -0
  100. package/src/schemas/functions/grayscale/unit.test.ts +79 -0
  101. package/src/schemas/functions/harmonize/harmonize.tokenscript +61 -0
  102. package/src/schemas/functions/harmonize/schema.json +52 -0
  103. package/src/schemas/functions/harmonize/unit.test.ts +56 -0
  104. package/src/schemas/functions/hue/hue.tokenscript +12 -0
  105. package/src/schemas/functions/hue/schema.json +44 -0
  106. package/src/schemas/functions/hue/unit.test.ts +75 -0
  107. package/src/schemas/functions/hue_difference/hue-difference.tokenscript +42 -0
  108. package/src/schemas/functions/hue_difference/schema.json +24 -0
  109. package/src/schemas/functions/hue_difference/unit.test.ts +125 -0
  110. package/src/schemas/functions/in_gamut/in-gamut.tokenscript +51 -0
  111. package/src/schemas/functions/in_gamut/schema.json +24 -0
  112. package/src/schemas/functions/in_gamut/unit.test.ts +178 -0
  113. package/src/schemas/functions/interpolate/interpolate.tokenscript +61 -0
  114. package/src/schemas/functions/interpolate/schema.json +52 -0
  115. package/src/schemas/functions/interpolate/unit.test.ts +96 -0
  116. package/src/schemas/functions/invert/invert-initializer.tokenscript +29 -0
  117. package/src/schemas/functions/invert/schema.json +20 -0
  118. package/src/schemas/functions/invert/unit.test.ts +216 -0
  119. package/src/schemas/functions/is_cool/is-cool.tokenscript +41 -0
  120. package/src/schemas/functions/is_cool/schema.json +20 -0
  121. package/src/schemas/functions/is_cool/unit.test.ts +189 -0
  122. package/src/schemas/functions/is_dark/is-dark.tokenscript +16 -0
  123. package/src/schemas/functions/is_dark/schema.json +24 -0
  124. package/src/schemas/functions/is_dark/unit.test.ts +87 -0
  125. package/src/schemas/functions/is_light/is-light.tokenscript +16 -0
  126. package/src/schemas/functions/is_light/schema.json +24 -0
  127. package/src/schemas/functions/is_light/unit.test.ts +86 -0
  128. package/src/schemas/functions/is_neutral/is-neutral.tokenscript +16 -0
  129. package/src/schemas/functions/is_neutral/schema.json +53 -0
  130. package/src/schemas/functions/is_neutral/unit.test.ts +85 -0
  131. package/src/schemas/functions/is_warm/is-warm.tokenscript +62 -0
  132. package/src/schemas/functions/is_warm/schema.json +20 -0
  133. package/src/schemas/functions/is_warm/unit.test.ts +161 -0
  134. package/src/schemas/functions/lighten/lighten.tokenscript +37 -0
  135. package/src/schemas/functions/lighten/schema.json +24 -0
  136. package/src/schemas/functions/lighten/unit.test.ts +109 -0
  137. package/src/schemas/functions/lightness/lightness.tokenscript +12 -0
  138. package/src/schemas/functions/lightness/schema.json +49 -0
  139. package/src/schemas/functions/lightness/unit.test.ts +99 -0
  140. package/src/schemas/functions/luminance/luminance.tokenscript +16 -0
  141. package/src/schemas/functions/luminance/schema.json +20 -0
  142. package/src/schemas/functions/luminance/unit.test.ts +105 -0
  143. package/src/schemas/functions/meets_contrast/meets-contrast.tokenscript +49 -0
  144. package/src/schemas/functions/meets_contrast/schema.json +28 -0
  145. package/src/schemas/functions/meets_contrast/unit.test.ts +170 -0
  146. package/src/schemas/functions/mix/mix.tokenscript +47 -0
  147. package/src/schemas/functions/mix/schema.json +28 -0
  148. package/src/schemas/functions/mix/unit.test.ts +95 -0
  149. package/src/schemas/functions/monochromatic/monochromatic.tokenscript +72 -0
  150. package/src/schemas/functions/monochromatic/schema.json +24 -0
  151. package/src/schemas/functions/monochromatic/unit.test.ts +91 -0
  152. package/src/schemas/functions/muted/muted.tokenscript +25 -0
  153. package/src/schemas/functions/muted/schema.json +48 -0
  154. package/src/schemas/functions/muted/unit.test.ts +100 -0
  155. package/src/schemas/functions/neutral_variant/neutral-variant.tokenscript +23 -0
  156. package/src/schemas/functions/neutral_variant/schema.json +48 -0
  157. package/src/schemas/functions/neutral_variant/unit.test.ts +102 -0
  158. package/src/schemas/functions/relative_luminance/relative-luminance.tokenscript +15 -0
  159. package/src/schemas/functions/relative_luminance/schema.json +49 -0
  160. package/src/schemas/functions/relative_luminance/unit.test.ts +104 -0
  161. package/src/schemas/functions/rotate_hue/rotate-hue.tokenscript +20 -0
  162. package/src/schemas/functions/rotate_hue/schema.json +24 -0
  163. package/src/schemas/functions/rotate_hue/unit.test.ts +86 -0
  164. package/src/schemas/functions/saturate/saturate.tokenscript +33 -0
  165. package/src/schemas/functions/saturate/schema.json +24 -0
  166. package/src/schemas/functions/saturate/unit.test.ts +59 -0
  167. package/src/schemas/functions/scale_chroma/scale-chroma.tokenscript +22 -0
  168. package/src/schemas/functions/scale_chroma/schema.json +48 -0
  169. package/src/schemas/functions/scale_chroma/unit.test.ts +79 -0
  170. package/src/schemas/functions/scale_lightness/scale-lightness.tokenscript +23 -0
  171. package/src/schemas/functions/scale_lightness/schema.json +48 -0
  172. package/src/schemas/functions/scale_lightness/unit.test.ts +73 -0
  173. package/src/schemas/functions/sepia/schema.json +48 -0
  174. package/src/schemas/functions/sepia/sepia.tokenscript +54 -0
  175. package/src/schemas/functions/sepia/unit.test.ts +88 -0
  176. package/src/schemas/functions/set_chroma/schema.json +24 -0
  177. package/src/schemas/functions/set_chroma/set-chroma.tokenscript +18 -0
  178. package/src/schemas/functions/set_chroma/unit.test.ts +79 -0
  179. package/src/schemas/functions/set_hue/schema.json +24 -0
  180. package/src/schemas/functions/set_hue/set-hue.tokenscript +18 -0
  181. package/src/schemas/functions/set_hue/unit.test.ts +90 -0
  182. package/src/schemas/functions/set_lightness/schema.json +24 -0
  183. package/src/schemas/functions/set_lightness/set-lightness.tokenscript +18 -0
  184. package/src/schemas/functions/set_lightness/unit.test.ts +80 -0
  185. package/src/schemas/functions/shade_scale/schema.json +24 -0
  186. package/src/schemas/functions/shade_scale/shade-scale.tokenscript +61 -0
  187. package/src/schemas/functions/shade_scale/unit.test.ts +64 -0
  188. package/src/schemas/functions/split_complement/schema.json +24 -0
  189. package/src/schemas/functions/split_complement/split-complement.tokenscript +38 -0
  190. package/src/schemas/functions/split_complement/unit.test.ts +53 -0
  191. package/src/schemas/functions/steps/schema.json +28 -0
  192. package/src/schemas/functions/steps/steps.tokenscript +54 -0
  193. package/src/schemas/functions/steps/unit.test.ts +71 -0
  194. package/src/schemas/functions/tetradic/schema.json +20 -0
  195. package/src/schemas/functions/tetradic/tetradic.tokenscript +40 -0
  196. package/src/schemas/functions/tetradic/unit.test.ts +50 -0
  197. package/src/schemas/functions/tint_scale/schema.json +32 -0
  198. package/src/schemas/functions/tint_scale/tint-scale.tokenscript +71 -0
  199. package/src/schemas/functions/tint_scale/unit.test.ts +64 -0
  200. package/src/schemas/functions/to_gamut/schema.json +48 -0
  201. package/src/schemas/functions/to_gamut/to-gamut.tokenscript +96 -0
  202. package/src/schemas/functions/to_gamut/unit.test.ts +97 -0
  203. package/src/schemas/functions/triadic/schema.json +20 -0
  204. package/src/schemas/functions/triadic/triadic.tokenscript +33 -0
  205. package/src/schemas/functions/triadic/unit.test.ts +64 -0
  206. package/src/schemas/functions/vibrant/schema.json +48 -0
  207. package/src/schemas/functions/vibrant/unit.test.ts +55 -0
  208. package/src/schemas/functions/vibrant/vibrant.tokenscript +29 -0
  209. package/src/schemas/functions/warmer/schema.json +43 -0
  210. package/src/schemas/functions/warmer/unit.test.ts +69 -0
  211. package/src/schemas/functions/warmer/warmer.tokenscript +45 -0
  212. package/src/schemas/functions/wcag_level/schema.json +48 -0
  213. package/src/schemas/functions/wcag_level/unit.test.ts +75 -0
  214. package/src/schemas/functions/wcag_level/wcag-level.tokenscript +50 -0
  215. package/src/schemas/types/css-color/from-hsl-color.tokenscript +16 -0
  216. package/src/schemas/types/css-color/from-hwb-color.tokenscript +16 -0
  217. package/src/schemas/types/css-color/from-lab-color.tokenscript +16 -0
  218. package/src/schemas/types/css-color/from-lch-color.tokenscript +16 -0
  219. package/src/schemas/types/css-color/from-oklab-color.tokenscript +16 -0
  220. package/src/schemas/types/css-color/from-oklch-color.tokenscript +16 -0
  221. package/src/schemas/types/css-color/from-p3-color.tokenscript +16 -0
  222. package/src/schemas/types/css-color/from-rgb-color.tokenscript +15 -0
  223. package/src/schemas/types/css-color/from-srgb-color.tokenscript +16 -0
  224. package/src/schemas/types/css-color/from-srgb-linear-color.tokenscript +16 -0
  225. package/src/schemas/types/css-color/from-xyz-d50-color.tokenscript +16 -0
  226. package/src/schemas/types/css-color/from-xyz-d65-color.tokenscript +16 -0
  227. package/src/schemas/types/css-color/initializer.tokenscript +13 -0
  228. package/src/schemas/types/css-color/schema.json +148 -0
  229. package/src/schemas/types/css-color/unit.test.ts +402 -0
  230. package/src/schemas/types/hex-color/initializer.tokenscript +3 -0
  231. package/src/schemas/types/hex-color/schema.json +24 -0
  232. package/src/schemas/types/hex-color/unit.test.ts +123 -0
  233. package/src/schemas/types/hsl-color/from-srgb.tokenscript +87 -0
  234. package/src/schemas/types/hsl-color/initializer.tokenscript +16 -0
  235. package/src/schemas/types/hsl-color/schema.json +48 -0
  236. package/src/schemas/types/hsl-color/unit.test.ts +201 -0
  237. package/src/schemas/types/hsv-color/from-srgb.tokenscript +80 -0
  238. package/src/schemas/types/hsv-color/initializer.tokenscript +16 -0
  239. package/src/schemas/types/hsv-color/schema.json +48 -0
  240. package/src/schemas/types/hsv-color/unit.test.ts +162 -0
  241. package/src/schemas/types/hwb-color/from-hsv.tokenscript +31 -0
  242. package/src/schemas/types/hwb-color/initializer.tokenscript +16 -0
  243. package/src/schemas/types/hwb-color/schema.json +48 -0
  244. package/src/schemas/types/hwb-color/unit.test.ts +150 -0
  245. package/src/schemas/types/lab-color/from-xyz-d50.tokenscript +78 -0
  246. package/src/schemas/types/lab-color/initializer.tokenscript +16 -0
  247. package/src/schemas/types/lab-color/schema.json +48 -0
  248. package/src/schemas/types/lab-color/unit.test.ts +263 -0
  249. package/src/schemas/types/lch-color/from-lab.tokenscript +44 -0
  250. package/src/schemas/types/lch-color/initializer.tokenscript +16 -0
  251. package/src/schemas/types/lch-color/schema.json +48 -0
  252. package/src/schemas/types/lch-color/unit.test.ts +173 -0
  253. package/src/schemas/types/okhsl-color/from-oklab.tokenscript +410 -0
  254. package/src/schemas/types/okhsl-color/initializer.tokenscript +24 -0
  255. package/src/schemas/types/okhsl-color/schema.json +48 -0
  256. package/src/schemas/types/okhsl-color/unit.test.ts +514 -0
  257. package/src/schemas/types/okhsv-color/from-oklab.tokenscript +286 -0
  258. package/src/schemas/types/okhsv-color/initializer.tokenscript +24 -0
  259. package/src/schemas/types/okhsv-color/schema.json +48 -0
  260. package/src/schemas/types/okhsv-color/unit.test.ts +499 -0
  261. package/src/schemas/types/oklab-color/from-okhsl.tokenscript +195 -0
  262. package/src/schemas/types/oklab-color/from-okhsv.tokenscript +197 -0
  263. package/src/schemas/types/oklab-color/from-oklch.tokenscript +39 -0
  264. package/src/schemas/types/oklab-color/from-xyz-d65.tokenscript +43 -0
  265. package/src/schemas/types/oklab-color/initializer.tokenscript +16 -0
  266. package/src/schemas/types/oklab-color/schema.json +78 -0
  267. package/src/schemas/types/oklab-color/unit.test.ts +345 -0
  268. package/src/schemas/types/oklch-color/from-oklab.tokenscript +45 -0
  269. package/src/schemas/types/oklch-color/initializer.tokenscript +16 -0
  270. package/src/schemas/types/oklch-color/schema.json +48 -0
  271. package/src/schemas/types/oklch-color/unit.test.ts +267 -0
  272. package/src/schemas/types/p3-color/from-p3-linear.tokenscript +59 -0
  273. package/src/schemas/types/p3-color/initializer.tokenscript +16 -0
  274. package/src/schemas/types/p3-color/schema.json +48 -0
  275. package/src/schemas/types/p3-color/unit.test.ts +119 -0
  276. package/src/schemas/types/p3-linear-color/from-xyz-d65.tokenscript +47 -0
  277. package/src/schemas/types/p3-linear-color/initializer.tokenscript +16 -0
  278. package/src/schemas/types/p3-linear-color/schema.json +48 -0
  279. package/src/schemas/types/p3-linear-color/unit.test.ts +82 -0
  280. package/src/schemas/types/rgb-color/from-hex.tokenscript +43 -0
  281. package/src/schemas/types/rgb-color/initializer.tokenscript +16 -0
  282. package/src/schemas/types/rgb-color/schema.json +55 -0
  283. package/src/schemas/types/rgb-color/to-hex.tokenscript +42 -0
  284. package/src/schemas/types/rgb-color/unit.test.ts +302 -0
  285. package/src/schemas/types/srgb-color/from-hsl.tokenscript +106 -0
  286. package/src/schemas/types/srgb-color/from-linear.tokenscript +58 -0
  287. package/src/schemas/types/srgb-color/from-rgb.tokenscript +20 -0
  288. package/src/schemas/types/srgb-color/initializer.tokenscript +16 -0
  289. package/src/schemas/types/srgb-color/schema.json +68 -0
  290. package/src/schemas/types/srgb-color/unit.test.ts +303 -0
  291. package/src/schemas/types/srgb-linear-color/from-srgb.tokenscript +55 -0
  292. package/src/schemas/types/srgb-linear-color/from-xyz-d65.tokenscript +34 -0
  293. package/src/schemas/types/srgb-linear-color/initializer.tokenscript +13 -0
  294. package/src/schemas/types/srgb-linear-color/schema.json +58 -0
  295. package/src/schemas/types/srgb-linear-color/unit.test.ts +291 -0
  296. package/src/schemas/types/xyz-d50-color/from-xyz-d65.tokenscript +36 -0
  297. package/src/schemas/types/xyz-d50-color/initializer.tokenscript +16 -0
  298. package/src/schemas/types/xyz-d50-color/schema.json +48 -0
  299. package/src/schemas/types/xyz-d50-color/unit.test.ts +240 -0
  300. package/src/schemas/types/xyz-d65-color/from-linear-p3.tokenscript +47 -0
  301. package/src/schemas/types/xyz-d65-color/from-linear-srgb.tokenscript +38 -0
  302. package/src/schemas/types/xyz-d65-color/from-oklab.tokenscript +44 -0
  303. package/src/schemas/types/xyz-d65-color/initializer.tokenscript +16 -0
  304. package/src/schemas/types/xyz-d65-color/schema.json +68 -0
  305. package/src/schemas/types/xyz-d65-color/unit.test.ts +319 -0
  306. package/src/utils/schema-uri.ts +192 -0
  307. package/src/utils/type.ts +194 -0
@@ -0,0 +1,38 @@
1
+ // Linear sRGB to XYZ-D65 Conversion
2
+ // Uses the sRGB primaries transformation matrix
3
+ // Reference: IEC 61966-2-1:1999 (sRGB specification)
4
+ //
5
+ // Matrix (Linear RGB → XYZ-D65):
6
+ // [X] [0.4123908 0.3575843 0.1804808] [R]
7
+ // [Y] = [0.2126390 0.7151687 0.0721923] [G]
8
+ // [Z] [0.0193308 0.1191948 0.9505322] [B]
9
+ //
10
+ // Input: Color.LinearSRGB with r, g, b in linear 0-1 range
11
+ // Output: Color.XYZD65 with x, y, z tristimulus values
12
+
13
+ // Get input linear sRGB values
14
+ variable r: Number = {input}.r;
15
+ variable g: Number = {input}.g;
16
+ variable b: Number = {input}.b;
17
+
18
+ // Matrix multiplication: M × [R, G, B]ᵀ
19
+ // Row 1: X = 0.4123908 * R + 0.3575843 * G + 0.1804808 * B
20
+ variable x: Number = r * 0.41239079926595934 + g * 0.357584339383878 + b * 0.1804807884018343;
21
+
22
+ // Row 2: Y = 0.2126390 * R + 0.7151687 * G + 0.0721923 * B
23
+ variable y: Number = r * 0.21263900587151027 + g * 0.715168678767756 + b * 0.07219231536073371;
24
+
25
+ // Row 3: Z = 0.0193308 * R + 0.1191948 * G + 0.9505322 * B
26
+ variable z: Number = r * 0.01933081871559182 + g * 0.11919477979462598 + b * 0.9505321522496607;
27
+
28
+ // Create output
29
+ variable output: Color.XYZD65;
30
+ output.x = x;
31
+ output.y = y;
32
+ output.z = z;
33
+
34
+ return output;
35
+
36
+
37
+
38
+
@@ -0,0 +1,44 @@
1
+ // OKLab to XYZ-D65 Conversion
2
+ // Reference: ColorJS oklab.js (inverse transformation)
3
+ //
4
+ // Algorithm:
5
+ // 1. Apply inverse Lab-to-LMS matrix to get LMS cone responses
6
+ // 2. Cube the LMS values (inverse of cube root)
7
+ // 3. Apply inverse LMS-to-XYZ matrix
8
+ //
9
+ // Input: Color.OKLab with l, a, b perceptual coordinates
10
+ // Output: Color.XYZD65 with x, y, z tristimulus values
11
+
12
+ // Get input OKLab values
13
+ variable ok_l: Number = {input}.l;
14
+ variable ok_a: Number = {input}.a;
15
+ variable ok_b: Number = {input}.b;
16
+
17
+ // Inverse LMStoLab_M matrix (Lab to LMS')
18
+ // These are the inverse of the matrix used in from-xyz-d65.tokenscript
19
+ variable lms_l: Number = 1.0 * ok_l + 0.3963377773761749 * ok_a + 0.2158037573099136 * ok_b;
20
+ variable lms_m: Number = 1.0 * ok_l + -0.1055613458156586 * ok_a + -0.0638541728258133 * ok_b;
21
+ variable lms_s: Number = 1.0 * ok_l + -0.0894841775298119 * ok_a + -1.2914855480194092 * ok_b;
22
+
23
+ // Cube the values (inverse of cube root)
24
+ variable lms_l_cubed: Number = lms_l * lms_l * lms_l;
25
+ variable lms_m_cubed: Number = lms_m * lms_m * lms_m;
26
+ variable lms_s_cubed: Number = lms_s * lms_s * lms_s;
27
+
28
+ // Inverse XYZtoLMS_M matrix (LMS to XYZ)
29
+ // From ColorJS oklab.js
30
+ variable x: Number = 1.2268798758459243 * lms_l_cubed + -0.5578149944602171 * lms_m_cubed + 0.2813910456659647 * lms_s_cubed;
31
+ variable y: Number = -0.0405757452148008 * lms_l_cubed + 1.1122868032803170 * lms_m_cubed + -0.0717110580655164 * lms_s_cubed;
32
+ variable z: Number = -0.0763729366746601 * lms_l_cubed + -0.4214933324022432 * lms_m_cubed + 1.5869240198367816 * lms_s_cubed;
33
+
34
+ // Create output
35
+ variable output: Color.XYZD65;
36
+ output.x = x;
37
+ output.y = y;
38
+ output.z = z;
39
+
40
+ return output;
41
+
42
+
43
+
44
+
@@ -0,0 +1,16 @@
1
+ // XYZ-D65 Color Initializer
2
+ // Creates an XYZ-D65 color from tristimulus values
3
+ // Input: List of [x, y, z] tristimulus values
4
+
5
+ variable xyz_values: List = {input};
6
+ variable output: Color.XYZD65;
7
+
8
+ output.x = xyz_values.get(0);
9
+ output.y = xyz_values.get(1);
10
+ output.z = xyz_values.get(2);
11
+
12
+ return output;
13
+
14
+
15
+
16
+
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "XYZD65",
3
+ "type": "color",
4
+ "description": "CIE XYZ color space with D65 white point. The primary connection hub for color space conversions.",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "x": {
9
+ "type": "number",
10
+ "description": "X tristimulus value"
11
+ },
12
+ "y": {
13
+ "type": "number",
14
+ "description": "Y tristimulus value (luminance)"
15
+ },
16
+ "z": {
17
+ "type": "number",
18
+ "description": "Z tristimulus value"
19
+ }
20
+ },
21
+ "required": ["x", "y", "z"],
22
+ "order": ["x", "y", "z"],
23
+ "additionalProperties": false
24
+ },
25
+ "initializers": [
26
+ {
27
+ "title": "XYZ-D65 Color Initializer",
28
+ "keyword": "xyzd65",
29
+ "description": "Creates an XYZ-D65 color from tristimulus values",
30
+ "script": {
31
+ "type": "/api/v1/core/tokenscript/0/",
32
+ "script": "./initializer.tokenscript"
33
+ }
34
+ }
35
+ ],
36
+ "conversions": [
37
+ {
38
+ "source": "/api/v1/core/srgb-linear-color/0/",
39
+ "target": "$self",
40
+ "description": "Converts Linear sRGB to XYZ-D65 using the sRGB transformation matrix",
41
+ "lossless": true,
42
+ "script": {
43
+ "type": "/api/v1/core/tokenscript/0/",
44
+ "script": "./from-linear-srgb.tokenscript"
45
+ }
46
+ },
47
+ {
48
+ "source": "/api/v1/core/p3-linear-color/0/",
49
+ "target": "$self",
50
+ "description": "Converts Linear P3 to XYZ-D65 using the P3 transformation matrix",
51
+ "lossless": true,
52
+ "script": {
53
+ "type": "/api/v1/core/tokenscript/0/",
54
+ "script": "./from-linear-p3.tokenscript"
55
+ }
56
+ },
57
+ {
58
+ "source": "/api/v1/core/oklab-color/0/",
59
+ "target": "$self",
60
+ "description": "Converts OKLab to XYZ-D65 using inverse LMS transformation",
61
+ "lossless": true,
62
+ "script": {
63
+ "type": "/api/v1/core/tokenscript/0/",
64
+ "script": "./from-oklab.tokenscript"
65
+ }
66
+ }
67
+ ]
68
+ }
@@ -0,0 +1,319 @@
1
+ /**
2
+ * XYZ-D65 Color Schema Tests
3
+ *
4
+ * Tests for the CIE XYZ-D65 color space (the connection hub)
5
+ * Validates against ColorJS for parity
6
+ */
7
+
8
+ import { log } from "@tests/helpers/logger";
9
+ import { executeWithSchema, getBundledSchema } from "@tests/helpers/schema-test-utils";
10
+ import Color from "colorjs.io";
11
+ import { describe, expect, it } from "vitest";
12
+ import type { ColorSpecification } from "@/bundler/types";
13
+
14
+ // ColorJS reference tolerance
15
+ const TOLERANCE = 1e-9;
16
+
17
+ describe("XYZ-D65 Color Schema", () => {
18
+ describe("Schema Definition", () => {
19
+ it("should have correct schema structure", async () => {
20
+ const schema = (await getBundledSchema("xyz-d65-color")) as ColorSpecification;
21
+
22
+ expect(schema.name).toBe("XYZD65");
23
+ expect(schema.type).toBe("color");
24
+ expect(schema.schema).toBeDefined();
25
+ expect(schema.schema?.properties).toHaveProperty("x");
26
+ expect(schema.schema?.properties).toHaveProperty("y");
27
+ expect(schema.schema?.properties).toHaveProperty("z");
28
+ expect(schema.schema?.required).toEqual(["x", "y", "z"]);
29
+ });
30
+
31
+ it("should have xyzd65 initializer", async () => {
32
+ const schema = (await getBundledSchema("xyz-d65-color")) as ColorSpecification;
33
+
34
+ expect(schema.initializers).toHaveLength(1);
35
+ expect(schema.initializers[0].keyword).toBe("xyzd65");
36
+ expect(schema.initializers[0].script.script).toContain("Color.XYZD65");
37
+ });
38
+
39
+ it("should have conversions from Linear sRGB and Linear P3", async () => {
40
+ const schema = (await getBundledSchema("xyz-d65-color")) as ColorSpecification;
41
+
42
+ expect(schema.conversions.length).toBeGreaterThanOrEqual(2);
43
+
44
+ const linearSrgbToXyz = schema.conversions.find((c: { source: string }) =>
45
+ c.source.includes("srgb-linear-color"),
46
+ );
47
+ expect(linearSrgbToXyz).toBeDefined();
48
+ expect(linearSrgbToXyz?.lossless).toBe(true);
49
+
50
+ const linearP3ToXyz = schema.conversions.find((c: { source: string }) =>
51
+ c.source.includes("p3-linear-color"),
52
+ );
53
+ expect(linearP3ToXyz).toBeDefined();
54
+ expect(linearP3ToXyz?.lossless).toBe(true);
55
+ });
56
+ });
57
+
58
+ describe("Initialization", () => {
59
+ it("should create XYZ-D65 color from values", async () => {
60
+ const result = await executeWithSchema(
61
+ "xyz-d65-color",
62
+ "type",
63
+ `
64
+ variable c: Color.XYZD65;
65
+ c.x = 0.5;
66
+ c.y = 0.4;
67
+ c.z = 0.3;
68
+ c
69
+ `,
70
+ );
71
+
72
+ expect(result?.constructor.name).toBe("ColorSymbol");
73
+ expect((result as any).subType).toBe("XYZD65");
74
+ expect((result as any).value.x.value).toBeCloseTo(0.5, 10);
75
+ expect((result as any).value.y.value).toBeCloseTo(0.4, 10);
76
+ expect((result as any).value.z.value).toBeCloseTo(0.3, 10);
77
+ });
78
+ });
79
+
80
+ describe("Conversion from Linear sRGB to XYZ-D65", () => {
81
+ it("should convert linear red to XYZ-D65", async () => {
82
+ const result = await executeWithSchema(
83
+ "xyz-d65-color",
84
+ "type",
85
+ `
86
+ variable linear: Color.LinearSRGB;
87
+ linear.r = 1;
88
+ linear.g = 0;
89
+ linear.b = 0;
90
+ linear.to.xyzd65()
91
+ `,
92
+ );
93
+
94
+ // ColorJS reference
95
+ const colorJS = new Color("srgb-linear", [1, 0, 0]).to("xyz-d65");
96
+
97
+ expect(result?.constructor.name).toBe("ColorSymbol");
98
+ expect((result as any).subType).toBe("XYZD65");
99
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
100
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
101
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
102
+ });
103
+
104
+ it("should convert linear green to XYZ-D65", async () => {
105
+ const result = await executeWithSchema(
106
+ "xyz-d65-color",
107
+ "type",
108
+ `
109
+ variable linear: Color.LinearSRGB;
110
+ linear.r = 0;
111
+ linear.g = 1;
112
+ linear.b = 0;
113
+ linear.to.xyzd65()
114
+ `,
115
+ );
116
+
117
+ const colorJS = new Color("srgb-linear", [0, 1, 0]).to("xyz-d65");
118
+
119
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
120
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
121
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
122
+ });
123
+
124
+ it("should convert linear blue to XYZ-D65", async () => {
125
+ const result = await executeWithSchema(
126
+ "xyz-d65-color",
127
+ "type",
128
+ `
129
+ variable linear: Color.LinearSRGB;
130
+ linear.r = 0;
131
+ linear.g = 0;
132
+ linear.b = 1;
133
+ linear.to.xyzd65()
134
+ `,
135
+ );
136
+
137
+ const colorJS = new Color("srgb-linear", [0, 0, 1]).to("xyz-d65");
138
+
139
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
140
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
141
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
142
+ });
143
+
144
+ it("should convert linear white to XYZ-D65 (D65 white point)", async () => {
145
+ const result = await executeWithSchema(
146
+ "xyz-d65-color",
147
+ "type",
148
+ `
149
+ variable linear: Color.LinearSRGB;
150
+ linear.r = 1;
151
+ linear.g = 1;
152
+ linear.b = 1;
153
+ linear.to.xyzd65()
154
+ `,
155
+ );
156
+
157
+ const colorJS = new Color("srgb-linear", [1, 1, 1]).to("xyz-d65");
158
+
159
+ // D65 white point should sum the matrix rows
160
+ log.info(`\n=== D65 WHITE POINT ===`);
161
+ log.info(
162
+ `TokenScript: { x: ${(result as any).value.x.value}, y: ${(result as any).value.y.value}, z: ${(result as any).value.z.value} }`,
163
+ );
164
+ log.info(
165
+ `ColorJS: { x: ${colorJS.coords[0]}, y: ${colorJS.coords[1]}, z: ${colorJS.coords[2]} }`,
166
+ );
167
+
168
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
169
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
170
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
171
+ });
172
+ });
173
+
174
+ describe("ColorJS Parity", () => {
175
+ const testCases = [
176
+ { name: "red", linear: [1, 0, 0] },
177
+ { name: "green", linear: [0, 1, 0] },
178
+ { name: "blue", linear: [0, 0, 1] },
179
+ { name: "black", linear: [0, 0, 0] },
180
+ { name: "white", linear: [1, 1, 1] },
181
+ { name: "gray-21.4%", linear: [0.214, 0.214, 0.214] }, // sRGB 50% gray
182
+ { name: "coral-linear", linear: [1, 0.095, 0.033] },
183
+ { name: "cyan", linear: [0, 1, 1] },
184
+ { name: "magenta", linear: [1, 0, 1] },
185
+ { name: "yellow", linear: [1, 1, 0] },
186
+ ];
187
+
188
+ for (const { name, linear } of testCases) {
189
+ it(`should match ColorJS for ${name}`, async () => {
190
+ const result = await executeWithSchema(
191
+ "xyz-d65-color",
192
+ "type",
193
+ `
194
+ variable linear: Color.LinearSRGB;
195
+ linear.r = ${linear[0]};
196
+ linear.g = ${linear[1]};
197
+ linear.b = ${linear[2]};
198
+ linear.to.xyzd65()
199
+ `,
200
+ );
201
+
202
+ // ColorJS reference
203
+ const colorJS = new Color("srgb-linear", linear as [number, number, number]).to("xyz-d65");
204
+
205
+ const tsX = (result as any).value.x.value;
206
+ const tsY = (result as any).value.y.value;
207
+ const tsZ = (result as any).value.z.value;
208
+
209
+ const diffX = Math.abs(tsX - colorJS.coords[0]);
210
+ const diffY = Math.abs(tsY - colorJS.coords[1]);
211
+ const diffZ = Math.abs(tsZ - colorJS.coords[2]);
212
+ const maxDiff = Math.max(diffX, diffY, diffZ);
213
+
214
+ log.info(`\n=== ${name.toUpperCase()} ColorJS Parity ===`);
215
+ log.info(`Input Linear: { r: ${linear[0]}, g: ${linear[1]}, b: ${linear[2]} }`);
216
+ log.info(
217
+ `TokenScript: { x: ${tsX.toFixed(9)}, y: ${tsY.toFixed(9)}, z: ${tsZ.toFixed(9)} }`,
218
+ );
219
+ log.info(
220
+ `ColorJS: { x: ${colorJS.coords[0].toFixed(9)}, y: ${colorJS.coords[1].toFixed(9)}, z: ${colorJS.coords[2].toFixed(9)} }`,
221
+ );
222
+ log.info(`Max Diff: ${maxDiff.toExponential(2)}`);
223
+ log.info(`Status: ${maxDiff < TOLERANCE ? "✅ PASS" : "❌ FAIL"}`);
224
+
225
+ expect(maxDiff).toBeLessThan(TOLERANCE);
226
+ });
227
+ }
228
+ });
229
+
230
+ describe("Full Conversion Chain: sRGB → Linear sRGB → XYZ-D65", () => {
231
+ it("should convert sRGB red through to XYZ-D65", async () => {
232
+ const result = await executeWithSchema(
233
+ "xyz-d65-color",
234
+ "type",
235
+ `
236
+ variable srgb: Color.SRGB;
237
+ srgb.r = 1;
238
+ srgb.g = 0;
239
+ srgb.b = 0;
240
+
241
+ variable linear: Color.LinearSRGB = srgb.to.linearsrgb();
242
+ linear.to.xyzd65()
243
+ `,
244
+ );
245
+
246
+ // ColorJS reference: sRGB → XYZ-D65
247
+ const colorJS = new Color("srgb", [1, 0, 0]).to("xyz-d65");
248
+
249
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
250
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
251
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
252
+ });
253
+
254
+ it("should convert sRGB mid-gray through to XYZ-D65", async () => {
255
+ const result = await executeWithSchema(
256
+ "xyz-d65-color",
257
+ "type",
258
+ `
259
+ variable srgb: Color.SRGB;
260
+ srgb.r = 0.5;
261
+ srgb.g = 0.5;
262
+ srgb.b = 0.5;
263
+
264
+ variable linear: Color.LinearSRGB = srgb.to.linearsrgb();
265
+ linear.to.xyzd65()
266
+ `,
267
+ );
268
+
269
+ // ColorJS reference
270
+ const colorJS = new Color("srgb", [0.5, 0.5, 0.5]).to("xyz-d65");
271
+
272
+ log.info(`\n=== sRGB 50% GRAY → XYZ-D65 (full chain) ===`);
273
+ log.info(
274
+ `TokenScript: { x: ${(result as any).value.x.value}, y: ${(result as any).value.y.value}, z: ${(result as any).value.z.value} }`,
275
+ );
276
+ log.info(
277
+ `ColorJS: { x: ${colorJS.coords[0]}, y: ${colorJS.coords[1]}, z: ${colorJS.coords[2]} }`,
278
+ );
279
+
280
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
281
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
282
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
283
+ });
284
+ });
285
+
286
+ describe("Full Conversion Chain: RGB (0-255) → sRGB → Linear sRGB → XYZ-D65", () => {
287
+ it("should convert RGB #ff5733 through entire chain to XYZ-D65", async () => {
288
+ const result = await executeWithSchema(
289
+ "xyz-d65-color",
290
+ "type",
291
+ `
292
+ variable rgb: Color.Rgb;
293
+ rgb.r = 255;
294
+ rgb.g = 87;
295
+ rgb.b = 51;
296
+
297
+ variable srgb: Color.SRGB = rgb.to.srgb();
298
+ variable linear: Color.LinearSRGB = srgb.to.linearsrgb();
299
+ linear.to.xyzd65()
300
+ `,
301
+ );
302
+
303
+ // ColorJS reference: #ff5733 → XYZ-D65
304
+ const colorJS = new Color("#ff5733").to("xyz-d65");
305
+
306
+ log.info(`\n=== RGB #ff5733 → XYZ-D65 (full chain) ===`);
307
+ log.info(
308
+ `TokenScript: { x: ${(result as any).value.x.value}, y: ${(result as any).value.y.value}, z: ${(result as any).value.z.value} }`,
309
+ );
310
+ log.info(
311
+ `ColorJS: { x: ${colorJS.coords[0]}, y: ${colorJS.coords[1]}, z: ${colorJS.coords[2]} }`,
312
+ );
313
+
314
+ expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 8);
315
+ expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 8);
316
+ expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 8);
317
+ });
318
+ });
319
+ });
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Utilities for working with TokenScript schema URIs
3
+ *
4
+ * Handles URI construction, parsing, and manipulation for the TokenScript schema registry.
5
+ */
6
+
7
+ import { isObject } from "./type";
8
+
9
+ export type SemanticVersion =
10
+ | { major: number }
11
+ | { major: number; minor: number }
12
+ | { major: number; minor: number; patch: number };
13
+
14
+ type SchemaVersion = "latest" | SemanticVersion | null;
15
+
16
+ export interface SchemaUriComponents {
17
+ baseUrl: string;
18
+ category: "schema" | "core" | "function";
19
+ name: string;
20
+ version: SchemaVersion;
21
+ }
22
+
23
+ export const DEFAULT_REGISTRY_URL = "https://schema.tokenscript.dev.gcp.tokens.studio";
24
+
25
+ export const DEFAULT_API_PATH = "/api/v1";
26
+
27
+ function safeParseInt(value: string): number | null {
28
+ const parsed = Number.parseInt(value, 10);
29
+ return Number.isNaN(parsed) ? null : parsed;
30
+ }
31
+
32
+ export function parseSemverFromString(versionString: string): SemanticVersion | null {
33
+ const parts = versionString.split(".");
34
+ const numbers: number[] = [];
35
+
36
+ for (const part of parts) {
37
+ const num = safeParseInt(part);
38
+ if (num === null) return null;
39
+ numbers.push(num);
40
+ }
41
+
42
+ if (numbers.length !== parts.length) return null;
43
+
44
+ if (numbers.length === 3) {
45
+ return { major: numbers[0], minor: numbers[1], patch: numbers[2] };
46
+ }
47
+
48
+ if (numbers.length === 2) {
49
+ return { major: numbers[0], minor: numbers[1] };
50
+ }
51
+
52
+ if (numbers.length === 1) {
53
+ return { major: numbers[0] };
54
+ }
55
+
56
+ return null;
57
+ }
58
+
59
+ export function parseVersionString(versionString: string): SchemaVersion {
60
+ return versionString === "latest" ? "latest" : parseSemverFromString(versionString);
61
+ }
62
+
63
+ export function semverToString(version: SchemaVersion | undefined): string {
64
+ if (version === undefined) {
65
+ return "latest";
66
+ }
67
+ if (isObject(version)) {
68
+ if ("patch" in version) return `${version.major}.${version.minor}.${version.patch}`;
69
+ if ("minor" in version) return `${version.major}.${version.minor}`;
70
+ return `${version.major}`;
71
+ }
72
+ return "latest";
73
+ }
74
+
75
+ export function buildSchemaUri(
76
+ params: Partial<SchemaUriComponents> & { category: string; name: string },
77
+ ): string {
78
+ const { baseUrl, category, name, version } = params;
79
+
80
+ const versionString = semverToString(version);
81
+
82
+ const effectiveBaseUrl = baseUrl === undefined ? DEFAULT_REGISTRY_URL : baseUrl;
83
+
84
+ if (effectiveBaseUrl === "") {
85
+ return `${DEFAULT_API_PATH}/${category}/${name}/${versionString}/`;
86
+ }
87
+
88
+ return `${effectiveBaseUrl}${DEFAULT_API_PATH}/${category}/${name}/${versionString}/`;
89
+ }
90
+
91
+ /**
92
+ * Parse a schema URI into its components
93
+ *
94
+ * @example
95
+ * parseSchemaUri("https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/schema/rgb-color/0.0.1/")
96
+ * // => { baseUrl: "...", category: "schema", name: "rgb-color", version: "0.0.1" }
97
+ *
98
+ * parseSchemaUri("/api/v1/schema/rgb-color/0.0.1/")
99
+ * // => { baseUrl: "", category: "schema", name: "rgb-color", version: "0.0.1" }
100
+ */
101
+ export function parseSchemaUri(uri: string): SchemaUriComponents | null {
102
+ let baseUrl = "";
103
+ let pathname = uri;
104
+
105
+ // Try parsing as full URL first
106
+ try {
107
+ const url = new URL(uri);
108
+ baseUrl = `${url.protocol}//${url.host}`;
109
+ pathname = url.pathname;
110
+ } catch {
111
+ // If URL parsing fails, treat as relative path
112
+ // Check if it starts with a protocol (incomplete URL)
113
+ if (uri.includes("://")) {
114
+ return null;
115
+ }
116
+ // It's a relative path, use as-is
117
+ pathname = uri;
118
+ }
119
+
120
+ // Parse pathname: /api/v1/schema/rgb-color/0.0.1/
121
+ const pathParts = pathname.split("/").filter((part) => part !== "");
122
+
123
+ // Expected format: [api, v1, category, name, version]
124
+ if (pathParts.length < 5) {
125
+ return null;
126
+ }
127
+
128
+ // Check if it starts with api/vN
129
+ if (pathParts[0] !== "api" || !pathParts[1].startsWith("v")) {
130
+ return null;
131
+ }
132
+
133
+ const category = pathParts[2];
134
+ if (category !== "schema" && category !== "core" && category !== "function") {
135
+ return null;
136
+ }
137
+
138
+ const name = pathParts[3];
139
+ const version = parseVersionString(pathParts[4]);
140
+
141
+ return {
142
+ baseUrl,
143
+ category: category as "schema" | "core" | "function",
144
+ name,
145
+ version,
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Extract the base URI without version
151
+ *
152
+ * @example
153
+ * getBaseUri("https://.../api/v1/schema/rgb-color/0.0.1/")
154
+ * // => "https://.../api/v1/schema/rgb-color/"
155
+ *
156
+ * getBaseUri("/api/v1/schema/rgb-color/0.0.1/")
157
+ * // => "/api/v1/schema/rgb-color/"
158
+ */
159
+ export function getBaseUri(uri: string): string {
160
+ const components = parseSchemaUri(uri);
161
+
162
+ if (!components) {
163
+ return uri;
164
+ }
165
+
166
+ const { baseUrl, category, name } = components;
167
+
168
+ if (baseUrl === "") {
169
+ return `${DEFAULT_API_PATH}/${category}/${name}/`;
170
+ }
171
+
172
+ return `${baseUrl}${DEFAULT_API_PATH}/${category}/${name}/`;
173
+ }
174
+
175
+ /**
176
+ * Extract schema name from URI
177
+ *
178
+ * @example
179
+ * extractSchemaName("https://.../api/v1/schema/rgb-color/0.0.1/")
180
+ * // => "rgb-color"
181
+ */
182
+ export function extractSchemaName(uri: string): string | null {
183
+ const components = parseSchemaUri(uri);
184
+ return components?.name || null;
185
+ }
186
+
187
+ /**
188
+ * Normalize URI to ensure it ends with trailing slash
189
+ */
190
+ export function normalizeUri(uri: string): string {
191
+ return uri.endsWith("/") ? uri : `${uri}/`;
192
+ }