@ggts-sh/spec 0.43.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 (679) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/dist/artifact.d.ts +11 -0
  4. package/dist/artifact.d.ts.map +1 -0
  5. package/dist/artifact.js +71 -0
  6. package/dist/artifact.js.map +1 -0
  7. package/dist/builder-core.d.ts +100 -0
  8. package/dist/builder-core.d.ts.map +1 -0
  9. package/dist/builder-core.js +176 -0
  10. package/dist/builder-core.js.map +1 -0
  11. package/dist/builder-data.d.ts +36 -0
  12. package/dist/builder-data.d.ts.map +1 -0
  13. package/dist/builder-data.js +123 -0
  14. package/dist/builder-data.js.map +1 -0
  15. package/dist/builder-geoms.d.ts +128 -0
  16. package/dist/builder-geoms.d.ts.map +1 -0
  17. package/dist/builder-geoms.js +221 -0
  18. package/dist/builder-geoms.js.map +1 -0
  19. package/dist/builder-options.d.ts +323 -0
  20. package/dist/builder-options.d.ts.map +1 -0
  21. package/dist/builder-options.js +2 -0
  22. package/dist/builder-options.js.map +1 -0
  23. package/dist/builder-scales.d.ts +160 -0
  24. package/dist/builder-scales.d.ts.map +1 -0
  25. package/dist/builder-scales.js +322 -0
  26. package/dist/builder-scales.js.map +1 -0
  27. package/dist/builder.d.ts +242 -0
  28. package/dist/builder.d.ts.map +1 -0
  29. package/dist/builder.js +42 -0
  30. package/dist/builder.js.map +1 -0
  31. package/dist/capabilities-data.d.ts +72 -0
  32. package/dist/capabilities-data.d.ts.map +1 -0
  33. package/dist/capabilities-data.js +337 -0
  34. package/dist/capabilities-data.js.map +1 -0
  35. package/dist/capabilities-helpers.d.ts +31 -0
  36. package/dist/capabilities-helpers.d.ts.map +1 -0
  37. package/dist/capabilities-helpers.js +41 -0
  38. package/dist/capabilities-helpers.js.map +1 -0
  39. package/dist/capabilities.d.ts +22 -0
  40. package/dist/capabilities.d.ts.map +1 -0
  41. package/dist/capabilities.js +154 -0
  42. package/dist/capabilities.js.map +1 -0
  43. package/dist/coord-helpers.d.ts +102 -0
  44. package/dist/coord-helpers.d.ts.map +1 -0
  45. package/dist/coord-helpers.js +90 -0
  46. package/dist/coord-helpers.js.map +1 -0
  47. package/dist/coord-reference-build.d.ts +50 -0
  48. package/dist/coord-reference-build.d.ts.map +1 -0
  49. package/dist/coord-reference-build.js +211 -0
  50. package/dist/coord-reference-build.js.map +1 -0
  51. package/dist/coord-reference.d.ts +52 -0
  52. package/dist/coord-reference.d.ts.map +1 -0
  53. package/dist/coord-reference.js +27 -0
  54. package/dist/coord-reference.js.map +1 -0
  55. package/dist/error-catalog.d.ts +258 -0
  56. package/dist/error-catalog.d.ts.map +1 -0
  57. package/dist/error-catalog.js +235 -0
  58. package/dist/error-catalog.js.map +1 -0
  59. package/dist/error-prose-shared.d.ts +100 -0
  60. package/dist/error-prose-shared.d.ts.map +1 -0
  61. package/dist/error-prose-shared.js +95 -0
  62. package/dist/error-prose-shared.js.map +1 -0
  63. package/dist/errors.d.ts +39 -0
  64. package/dist/errors.d.ts.map +1 -0
  65. package/dist/errors.js +52 -0
  66. package/dist/errors.js.map +1 -0
  67. package/dist/generated/coord-reference-data.d.ts +189 -0
  68. package/dist/generated/coord-reference-data.d.ts.map +1 -0
  69. package/dist/generated/coord-reference-data.js +212 -0
  70. package/dist/generated/coord-reference-data.js.map +1 -0
  71. package/dist/generated/geom-param-keys.d.ts +58 -0
  72. package/dist/generated/geom-param-keys.d.ts.map +1 -0
  73. package/dist/generated/geom-param-keys.js +223 -0
  74. package/dist/generated/geom-param-keys.js.map +1 -0
  75. package/dist/generated/geom-reference-data.d.ts +2377 -0
  76. package/dist/generated/geom-reference-data.d.ts.map +1 -0
  77. package/dist/generated/geom-reference-data.js +2790 -0
  78. package/dist/generated/geom-reference-data.js.map +1 -0
  79. package/dist/generated/guide-reference-data.d.ts +219 -0
  80. package/dist/generated/guide-reference-data.d.ts.map +1 -0
  81. package/dist/generated/guide-reference-data.js +253 -0
  82. package/dist/generated/guide-reference-data.js.map +1 -0
  83. package/dist/generated/plot-spec-validator.d.ts +2 -0
  84. package/dist/generated/plot-spec-validator.d.ts.map +1 -0
  85. package/dist/generated/plot-spec-validator.js +6274 -0
  86. package/dist/generated/plot-spec-validator.js.map +1 -0
  87. package/dist/generated/position-reference-data.d.ts +82 -0
  88. package/dist/generated/position-reference-data.d.ts.map +1 -0
  89. package/dist/generated/position-reference-data.js +181 -0
  90. package/dist/generated/position-reference-data.js.map +1 -0
  91. package/dist/generated/scale-reference-data.d.ts +8366 -0
  92. package/dist/generated/scale-reference-data.d.ts.map +1 -0
  93. package/dist/generated/scale-reference-data.js +9821 -0
  94. package/dist/generated/scale-reference-data.js.map +1 -0
  95. package/dist/generated/stat-reference-data.d.ts +241 -0
  96. package/dist/generated/stat-reference-data.d.ts.map +1 -0
  97. package/dist/generated/stat-reference-data.js +311 -0
  98. package/dist/generated/stat-reference-data.js.map +1 -0
  99. package/dist/geom-params-build.d.ts +3 -0
  100. package/dist/geom-params-build.d.ts.map +1 -0
  101. package/dist/geom-params-build.js +80 -0
  102. package/dist/geom-params-build.js.map +1 -0
  103. package/dist/geom-params.d.ts +13 -0
  104. package/dist/geom-params.d.ts.map +1 -0
  105. package/dist/geom-params.js +7 -0
  106. package/dist/geom-params.js.map +1 -0
  107. package/dist/geom-reference-build.d.ts +29 -0
  108. package/dist/geom-reference-build.d.ts.map +1 -0
  109. package/dist/geom-reference-build.js +221 -0
  110. package/dist/geom-reference-build.js.map +1 -0
  111. package/dist/geom-reference.d.ts +52 -0
  112. package/dist/geom-reference.d.ts.map +1 -0
  113. package/dist/geom-reference.js +72 -0
  114. package/dist/geom-reference.js.map +1 -0
  115. package/dist/guide-helpers.d.ts +20 -0
  116. package/dist/guide-helpers.d.ts.map +1 -0
  117. package/dist/guide-helpers.js +25 -0
  118. package/dist/guide-helpers.js.map +1 -0
  119. package/dist/guide-reference-build.d.ts +42 -0
  120. package/dist/guide-reference-build.d.ts.map +1 -0
  121. package/dist/guide-reference-build.js +180 -0
  122. package/dist/guide-reference-build.js.map +1 -0
  123. package/dist/guide-reference.d.ts +48 -0
  124. package/dist/guide-reference.d.ts.map +1 -0
  125. package/dist/guide-reference.js +32 -0
  126. package/dist/guide-reference.js.map +1 -0
  127. package/dist/hue-grey-palettes.d.ts +15 -0
  128. package/dist/hue-grey-palettes.d.ts.map +1 -0
  129. package/dist/hue-grey-palettes.js +77 -0
  130. package/dist/hue-grey-palettes.js.map +1 -0
  131. package/dist/index.d.ts +364 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +225 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/lint-catalog.d.ts +47 -0
  136. package/dist/lint-catalog.d.ts.map +1 -0
  137. package/dist/lint-catalog.js +39 -0
  138. package/dist/lint-catalog.js.map +1 -0
  139. package/dist/lint-layer-rules.d.ts +18 -0
  140. package/dist/lint-layer-rules.d.ts.map +1 -0
  141. package/dist/lint-layer-rules.js +145 -0
  142. package/dist/lint-layer-rules.js.map +1 -0
  143. package/dist/lint-scale-rules.d.ts +8 -0
  144. package/dist/lint-scale-rules.d.ts.map +1 -0
  145. package/dist/lint-scale-rules.js +254 -0
  146. package/dist/lint-scale-rules.js.map +1 -0
  147. package/dist/lint.d.ts +62 -0
  148. package/dist/lint.d.ts.map +1 -0
  149. package/dist/lint.js +57 -0
  150. package/dist/lint.js.map +1 -0
  151. package/dist/normalize-coord.d.ts +8 -0
  152. package/dist/normalize-coord.d.ts.map +1 -0
  153. package/dist/normalize-coord.js +123 -0
  154. package/dist/normalize-coord.js.map +1 -0
  155. package/dist/normalize-input-layers-marks.d.ts +208 -0
  156. package/dist/normalize-input-layers-marks.d.ts.map +1 -0
  157. package/dist/normalize-input-layers-marks.js +2 -0
  158. package/dist/normalize-input-layers-marks.js.map +1 -0
  159. package/dist/normalize-input-layers-special.d.ts +131 -0
  160. package/dist/normalize-input-layers-special.d.ts.map +1 -0
  161. package/dist/normalize-input-layers-special.js +2 -0
  162. package/dist/normalize-input-layers-special.js.map +1 -0
  163. package/dist/normalize-input.d.ts +81 -0
  164. package/dist/normalize-input.d.ts.map +1 -0
  165. package/dist/normalize-input.js +2 -0
  166. package/dist/normalize-input.js.map +1 -0
  167. package/dist/normalize-paint.d.ts +8 -0
  168. package/dist/normalize-paint.d.ts.map +1 -0
  169. package/dist/normalize-paint.js +83 -0
  170. package/dist/normalize-paint.js.map +1 -0
  171. package/dist/normalize-scales.d.ts +7 -0
  172. package/dist/normalize-scales.d.ts.map +1 -0
  173. package/dist/normalize-scales.js +90 -0
  174. package/dist/normalize-scales.js.map +1 -0
  175. package/dist/normalize.d.ts +46 -0
  176. package/dist/normalize.d.ts.map +1 -0
  177. package/dist/normalize.js +279 -0
  178. package/dist/normalize.js.map +1 -0
  179. package/dist/paint-helpers.d.ts +45 -0
  180. package/dist/paint-helpers.d.ts.map +1 -0
  181. package/dist/paint-helpers.js +48 -0
  182. package/dist/paint-helpers.js.map +1 -0
  183. package/dist/pipeline-error-catalog.d.ts +319 -0
  184. package/dist/pipeline-error-catalog.d.ts.map +1 -0
  185. package/dist/pipeline-error-catalog.js +260 -0
  186. package/dist/pipeline-error-catalog.js.map +1 -0
  187. package/dist/portability-check.d.ts +35 -0
  188. package/dist/portability-check.d.ts.map +1 -0
  189. package/dist/portability-check.js +121 -0
  190. package/dist/portability-check.js.map +1 -0
  191. package/dist/portability-lossy.d.ts +19 -0
  192. package/dist/portability-lossy.d.ts.map +1 -0
  193. package/dist/portability-lossy.js +65 -0
  194. package/dist/portability-lossy.js.map +1 -0
  195. package/dist/portability.d.ts +23 -0
  196. package/dist/portability.d.ts.map +1 -0
  197. package/dist/portability.js +23 -0
  198. package/dist/portability.js.map +1 -0
  199. package/dist/portable-builder.d.ts +215 -0
  200. package/dist/portable-builder.d.ts.map +1 -0
  201. package/dist/portable-builder.js +27 -0
  202. package/dist/portable-builder.js.map +1 -0
  203. package/dist/portable-entry.d.ts +16 -0
  204. package/dist/portable-entry.d.ts.map +1 -0
  205. package/dist/portable-entry.js +14 -0
  206. package/dist/portable-entry.js.map +1 -0
  207. package/dist/position-reference-build.d.ts +29 -0
  208. package/dist/position-reference-build.d.ts.map +1 -0
  209. package/dist/position-reference-build.js +115 -0
  210. package/dist/position-reference-build.js.map +1 -0
  211. package/dist/position-reference.d.ts +34 -0
  212. package/dist/position-reference.d.ts.map +1 -0
  213. package/dist/position-reference.js +19 -0
  214. package/dist/position-reference.js.map +1 -0
  215. package/dist/runtime.d.ts +139 -0
  216. package/dist/runtime.d.ts.map +1 -0
  217. package/dist/runtime.js +2 -0
  218. package/dist/runtime.js.map +1 -0
  219. package/dist/scale-color-helpers.d.ts +121 -0
  220. package/dist/scale-color-helpers.d.ts.map +1 -0
  221. package/dist/scale-color-helpers.js +188 -0
  222. package/dist/scale-color-helpers.js.map +1 -0
  223. package/dist/scale-color-hue-grey.d.ts +52 -0
  224. package/dist/scale-color-hue-grey.d.ts.map +1 -0
  225. package/dist/scale-color-hue-grey.js +60 -0
  226. package/dist/scale-color-hue-grey.js.map +1 -0
  227. package/dist/scale-color-stops.d.ts +85 -0
  228. package/dist/scale-color-stops.d.ts.map +1 -0
  229. package/dist/scale-color-stops.js +87 -0
  230. package/dist/scale-color-stops.js.map +1 -0
  231. package/dist/scale-color-viridis.d.ts +52 -0
  232. package/dist/scale-color-viridis.d.ts.map +1 -0
  233. package/dist/scale-color-viridis.js +68 -0
  234. package/dist/scale-color-viridis.js.map +1 -0
  235. package/dist/scale-helpers.d.ts +20 -0
  236. package/dist/scale-helpers.d.ts.map +1 -0
  237. package/dist/scale-helpers.js +20 -0
  238. package/dist/scale-helpers.js.map +1 -0
  239. package/dist/scale-multi-helpers.d.ts +61 -0
  240. package/dist/scale-multi-helpers.d.ts.map +1 -0
  241. package/dist/scale-multi-helpers.js +147 -0
  242. package/dist/scale-multi-helpers.js.map +1 -0
  243. package/dist/scale-position-helpers.d.ts +81 -0
  244. package/dist/scale-position-helpers.d.ts.map +1 -0
  245. package/dist/scale-position-helpers.js +133 -0
  246. package/dist/scale-position-helpers.js.map +1 -0
  247. package/dist/scale-reference-build.d.ts +50 -0
  248. package/dist/scale-reference-build.d.ts.map +1 -0
  249. package/dist/scale-reference-build.js +147 -0
  250. package/dist/scale-reference-build.js.map +1 -0
  251. package/dist/scale-reference-classify-color.d.ts +11 -0
  252. package/dist/scale-reference-classify-color.d.ts.map +1 -0
  253. package/dist/scale-reference-classify-color.js +292 -0
  254. package/dist/scale-reference-classify-color.js.map +1 -0
  255. package/dist/scale-reference-classify-position.d.ts +10 -0
  256. package/dist/scale-reference-classify-position.d.ts.map +1 -0
  257. package/dist/scale-reference-classify-position.js +137 -0
  258. package/dist/scale-reference-classify-position.js.map +1 -0
  259. package/dist/scale-reference-classify-style.d.ts +10 -0
  260. package/dist/scale-reference-classify-style.d.ts.map +1 -0
  261. package/dist/scale-reference-classify-style.js +194 -0
  262. package/dist/scale-reference-classify-style.js.map +1 -0
  263. package/dist/scale-reference-classify-support.d.ts +37 -0
  264. package/dist/scale-reference-classify-support.d.ts.map +1 -0
  265. package/dist/scale-reference-classify-support.js +206 -0
  266. package/dist/scale-reference-classify-support.js.map +1 -0
  267. package/dist/scale-reference.d.ts +80 -0
  268. package/dist/scale-reference.d.ts.map +1 -0
  269. package/dist/scale-reference.js +85 -0
  270. package/dist/scale-reference.js.map +1 -0
  271. package/dist/scale-style-helpers.d.ts +158 -0
  272. package/dist/scale-style-helpers.d.ts.map +1 -0
  273. package/dist/scale-style-helpers.js +197 -0
  274. package/dist/scale-style-helpers.js.map +1 -0
  275. package/dist/schema-catalog.d.ts +62 -0
  276. package/dist/schema-catalog.d.ts.map +1 -0
  277. package/dist/schema-catalog.js +208 -0
  278. package/dist/schema-catalog.js.map +1 -0
  279. package/dist/schema-declarations.d.ts +1585 -0
  280. package/dist/schema-declarations.d.ts.map +1 -0
  281. package/dist/schema-declarations.js +51 -0
  282. package/dist/schema-declarations.js.map +1 -0
  283. package/dist/schema-decls-data.d.ts +58 -0
  284. package/dist/schema-decls-data.d.ts.map +1 -0
  285. package/dist/schema-decls-data.js +151 -0
  286. package/dist/schema-decls-data.js.map +1 -0
  287. package/dist/schema-decls-guides-theme.d.ts +166 -0
  288. package/dist/schema-decls-guides-theme.d.ts.map +1 -0
  289. package/dist/schema-decls-guides-theme.js +251 -0
  290. package/dist/schema-decls-guides-theme.js.map +1 -0
  291. package/dist/schema-decls-layers-col-boxplot.d.ts +118 -0
  292. package/dist/schema-decls-layers-col-boxplot.d.ts.map +1 -0
  293. package/dist/schema-decls-layers-col-boxplot.js +239 -0
  294. package/dist/schema-decls-layers-col-boxplot.js.map +1 -0
  295. package/dist/schema-decls-layers-count-pointrange.d.ts +89 -0
  296. package/dist/schema-decls-layers-count-pointrange.d.ts.map +1 -0
  297. package/dist/schema-decls-layers-count-pointrange.js +190 -0
  298. package/dist/schema-decls-layers-count-pointrange.js.map +1 -0
  299. package/dist/schema-decls-layers-crossbar-jitter.d.ts +129 -0
  300. package/dist/schema-decls-layers-crossbar-jitter.d.ts.map +1 -0
  301. package/dist/schema-decls-layers-crossbar-jitter.js +255 -0
  302. package/dist/schema-decls-layers-crossbar-jitter.js.map +1 -0
  303. package/dist/schema-decls-layers-point-path.d.ts +49 -0
  304. package/dist/schema-decls-layers-point-path.d.ts.map +1 -0
  305. package/dist/schema-decls-layers-point-path.js +156 -0
  306. package/dist/schema-decls-layers-point-path.js.map +1 -0
  307. package/dist/schema-decls-layers-sf-blank.d.ts +89 -0
  308. package/dist/schema-decls-layers-sf-blank.d.ts.map +1 -0
  309. package/dist/schema-decls-layers-sf-blank.js +254 -0
  310. package/dist/schema-decls-layers-sf-blank.js.map +1 -0
  311. package/dist/schema-decls-layers-text-map.d.ts +80 -0
  312. package/dist/schema-decls-layers-text-map.d.ts.map +1 -0
  313. package/dist/schema-decls-layers-text-map.js +151 -0
  314. package/dist/schema-decls-layers-text-map.js.map +1 -0
  315. package/dist/schema-decls-paint.d.ts +39 -0
  316. package/dist/schema-decls-paint.d.ts.map +1 -0
  317. package/dist/schema-decls-paint.js +95 -0
  318. package/dist/schema-decls-paint.js.map +1 -0
  319. package/dist/schema-decls-params-area-abline.d.ts +85 -0
  320. package/dist/schema-decls-params-area-abline.d.ts.map +1 -0
  321. package/dist/schema-decls-params-area-abline.js +259 -0
  322. package/dist/schema-decls-params-area-abline.js.map +1 -0
  323. package/dist/schema-decls-params-col-contour.d.ts +61 -0
  324. package/dist/schema-decls-params-col-contour.d.ts.map +1 -0
  325. package/dist/schema-decls-params-col-contour.js +202 -0
  326. package/dist/schema-decls-params-col-contour.js.map +1 -0
  327. package/dist/schema-decls-params-curve-label.d.ts +64 -0
  328. package/dist/schema-decls-params-curve-label.d.ts.map +1 -0
  329. package/dist/schema-decls-params-curve-label.js +204 -0
  330. package/dist/schema-decls-params-curve-label.js.map +1 -0
  331. package/dist/schema-decls-params-errorbar-position.d.ts +83 -0
  332. package/dist/schema-decls-params-errorbar-position.d.ts.map +1 -0
  333. package/dist/schema-decls-params-errorbar-position.js +264 -0
  334. package/dist/schema-decls-params-errorbar-position.js.map +1 -0
  335. package/dist/schema-decls-params-point-path.d.ts +61 -0
  336. package/dist/schema-decls-params-point-path.d.ts.map +1 -0
  337. package/dist/schema-decls-params-point-path.js +240 -0
  338. package/dist/schema-decls-params-point-path.js.map +1 -0
  339. package/dist/schema-decls-params-violin-summary.d.ts +78 -0
  340. package/dist/schema-decls-params-violin-summary.d.ts.map +1 -0
  341. package/dist/schema-decls-params-violin-summary.js +272 -0
  342. package/dist/schema-decls-params-violin-summary.js.map +1 -0
  343. package/dist/schema-decls-plot.d.ts +80 -0
  344. package/dist/schema-decls-plot.d.ts.map +1 -0
  345. package/dist/schema-decls-plot.js +222 -0
  346. package/dist/schema-decls-plot.js.map +1 -0
  347. package/dist/schema-decls-scales-guides.d.ts +291 -0
  348. package/dist/schema-decls-scales-guides.d.ts.map +1 -0
  349. package/dist/schema-decls-scales-guides.js +189 -0
  350. package/dist/schema-decls-scales-guides.js.map +1 -0
  351. package/dist/schema-decls-scales.d.ts +124 -0
  352. package/dist/schema-decls-scales.d.ts.map +1 -0
  353. package/dist/schema-decls-scales.js +279 -0
  354. package/dist/schema-decls-scales.js.map +1 -0
  355. package/dist/schema-imports.d.ts +1679 -0
  356. package/dist/schema-imports.d.ts.map +1 -0
  357. package/dist/schema-imports.js +104 -0
  358. package/dist/schema-imports.js.map +1 -0
  359. package/dist/schema-name-schemas.d.ts +18 -0
  360. package/dist/schema-name-schemas.d.ts.map +1 -0
  361. package/dist/schema-name-schemas.js +13 -0
  362. package/dist/schema-name-schemas.js.map +1 -0
  363. package/dist/schema-names.d.ts +53 -0
  364. package/dist/schema-names.d.ts.map +1 -0
  365. package/dist/schema-names.js +229 -0
  366. package/dist/schema-names.js.map +1 -0
  367. package/dist/schema-types.d.ts +336 -0
  368. package/dist/schema-types.d.ts.map +1 -0
  369. package/dist/schema-types.js +10 -0
  370. package/dist/schema-types.js.map +1 -0
  371. package/dist/schema.d.ts +38 -0
  372. package/dist/schema.d.ts.map +1 -0
  373. package/dist/schema.js +34 -0
  374. package/dist/schema.js.map +1 -0
  375. package/dist/stat-reference-build.d.ts +24 -0
  376. package/dist/stat-reference-build.d.ts.map +1 -0
  377. package/dist/stat-reference-build.js +86 -0
  378. package/dist/stat-reference-build.js.map +1 -0
  379. package/dist/stat-reference.d.ts +33 -0
  380. package/dist/stat-reference.d.ts.map +1 -0
  381. package/dist/stat-reference.js +19 -0
  382. package/dist/stat-reference.js.map +1 -0
  383. package/dist/structural-gate.d.ts +15 -0
  384. package/dist/structural-gate.d.ts.map +1 -0
  385. package/dist/structural-gate.js +34 -0
  386. package/dist/structural-gate.js.map +1 -0
  387. package/dist/temporal-column.d.ts +33 -0
  388. package/dist/temporal-column.d.ts.map +1 -0
  389. package/dist/temporal-column.js +247 -0
  390. package/dist/temporal-column.js.map +1 -0
  391. package/dist/temporal-guides.d.ts +11 -0
  392. package/dist/temporal-guides.d.ts.map +1 -0
  393. package/dist/temporal-guides.js +11 -0
  394. package/dist/temporal-guides.js.map +1 -0
  395. package/dist/temporal-interval-schema.d.ts +9 -0
  396. package/dist/temporal-interval-schema.d.ts.map +1 -0
  397. package/dist/temporal-interval-schema.js +25 -0
  398. package/dist/temporal-interval-schema.js.map +1 -0
  399. package/dist/temporal-interval.d.ts +29 -0
  400. package/dist/temporal-interval.d.ts.map +1 -0
  401. package/dist/temporal-interval.js +122 -0
  402. package/dist/temporal-interval.js.map +1 -0
  403. package/dist/temporal-parse-core.d.ts +96 -0
  404. package/dist/temporal-parse-core.d.ts.map +1 -0
  405. package/dist/temporal-parse-core.js +235 -0
  406. package/dist/temporal-parse-core.js.map +1 -0
  407. package/dist/temporal-parse-format.d.ts +13 -0
  408. package/dist/temporal-parse-format.d.ts.map +1 -0
  409. package/dist/temporal-parse-format.js +180 -0
  410. package/dist/temporal-parse-format.js.map +1 -0
  411. package/dist/temporal-parse-schema.d.ts +11 -0
  412. package/dist/temporal-parse-schema.d.ts.map +1 -0
  413. package/dist/temporal-parse-schema.js +23 -0
  414. package/dist/temporal-parse-schema.js.map +1 -0
  415. package/dist/temporal-parse.d.ts +18 -0
  416. package/dist/temporal-parse.d.ts.map +1 -0
  417. package/dist/temporal-parse.js +183 -0
  418. package/dist/temporal-parse.js.map +1 -0
  419. package/dist/temporal-polyfill.d.ts +3 -0
  420. package/dist/temporal-polyfill.d.ts.map +1 -0
  421. package/dist/temporal-polyfill.js +25 -0
  422. package/dist/temporal-polyfill.js.map +1 -0
  423. package/dist/temporal-ticks.d.ts +20 -0
  424. package/dist/temporal-ticks.d.ts.map +1 -0
  425. package/dist/temporal-ticks.js +246 -0
  426. package/dist/temporal-ticks.js.map +1 -0
  427. package/dist/temporal.d.ts +109 -0
  428. package/dist/temporal.d.ts.map +1 -0
  429. package/dist/temporal.js +73 -0
  430. package/dist/temporal.js.map +1 -0
  431. package/dist/validate-data-checks-color-temporal.d.ts +58 -0
  432. package/dist/validate-data-checks-color-temporal.d.ts.map +1 -0
  433. package/dist/validate-data-checks-color-temporal.js +168 -0
  434. package/dist/validate-data-checks-color-temporal.js.map +1 -0
  435. package/dist/validate-data-checks-color.d.ts +28 -0
  436. package/dist/validate-data-checks-color.d.ts.map +1 -0
  437. package/dist/validate-data-checks-color.js +296 -0
  438. package/dist/validate-data-checks-color.js.map +1 -0
  439. package/dist/validate-data-checks-layer.d.ts +37 -0
  440. package/dist/validate-data-checks-layer.d.ts.map +1 -0
  441. package/dist/validate-data-checks-layer.js +343 -0
  442. package/dist/validate-data-checks-layer.js.map +1 -0
  443. package/dist/validate-data-checks-position.d.ts +45 -0
  444. package/dist/validate-data-checks-position.d.ts.map +1 -0
  445. package/dist/validate-data-checks-position.js +204 -0
  446. package/dist/validate-data-checks-position.js.map +1 -0
  447. package/dist/validate-data-checks-style-finite.d.ts +24 -0
  448. package/dist/validate-data-checks-style-finite.d.ts.map +1 -0
  449. package/dist/validate-data-checks-style-finite.js +40 -0
  450. package/dist/validate-data-checks-style-finite.js.map +1 -0
  451. package/dist/validate-data-checks-style-numeric.d.ts +26 -0
  452. package/dist/validate-data-checks-style-numeric.d.ts.map +1 -0
  453. package/dist/validate-data-checks-style-numeric.js +327 -0
  454. package/dist/validate-data-checks-style-numeric.js.map +1 -0
  455. package/dist/validate-data-checks-style.d.ts +14 -0
  456. package/dist/validate-data-checks-style.d.ts.map +1 -0
  457. package/dist/validate-data-checks-style.js +14 -0
  458. package/dist/validate-data-checks-style.js.map +1 -0
  459. package/dist/validate-data-checks-temporal.d.ts +57 -0
  460. package/dist/validate-data-checks-temporal.d.ts.map +1 -0
  461. package/dist/validate-data-checks-temporal.js +83 -0
  462. package/dist/validate-data-checks-temporal.js.map +1 -0
  463. package/dist/validate-data-checks.d.ts +31 -0
  464. package/dist/validate-data-checks.d.ts.map +1 -0
  465. package/dist/validate-data-checks.js +110 -0
  466. package/dist/validate-data-checks.js.map +1 -0
  467. package/dist/validate-data-evidence.d.ts +69 -0
  468. package/dist/validate-data-evidence.d.ts.map +1 -0
  469. package/dist/validate-data-evidence.js +329 -0
  470. package/dist/validate-data-evidence.js.map +1 -0
  471. package/dist/validate-data.d.ts +73 -0
  472. package/dist/validate-data.d.ts.map +1 -0
  473. package/dist/validate-data.js +30 -0
  474. package/dist/validate-data.js.map +1 -0
  475. package/dist/validate-map-errors.d.ts +32 -0
  476. package/dist/validate-map-errors.d.ts.map +1 -0
  477. package/dist/validate-map-errors.js +76 -0
  478. package/dist/validate-map-errors.js.map +1 -0
  479. package/dist/validate-map-forms.d.ts +43 -0
  480. package/dist/validate-map-forms.d.ts.map +1 -0
  481. package/dist/validate-map-forms.js +125 -0
  482. package/dist/validate-map-forms.js.map +1 -0
  483. package/dist/validate-map-path-keywords.d.ts +11 -0
  484. package/dist/validate-map-path-keywords.d.ts.map +1 -0
  485. package/dist/validate-map-path-keywords.js +95 -0
  486. package/dist/validate-map-path-keywords.js.map +1 -0
  487. package/dist/validate-map-path-unions.d.ts +27 -0
  488. package/dist/validate-map-path-unions.d.ts.map +1 -0
  489. package/dist/validate-map-path-unions.js +200 -0
  490. package/dist/validate-map-path-unions.js.map +1 -0
  491. package/dist/validate-schema-shape.d.ts +69 -0
  492. package/dist/validate-schema-shape.d.ts.map +1 -0
  493. package/dist/validate-schema-shape.js +156 -0
  494. package/dist/validate-schema-shape.js.map +1 -0
  495. package/dist/validate-schema-walk.d.ts +34 -0
  496. package/dist/validate-schema-walk.d.ts.map +1 -0
  497. package/dist/validate-schema-walk.js +216 -0
  498. package/dist/validate-schema-walk.js.map +1 -0
  499. package/dist/validate-structure-facet.d.ts +8 -0
  500. package/dist/validate-structure-facet.d.ts.map +1 -0
  501. package/dist/validate-structure-facet.js +67 -0
  502. package/dist/validate-structure-facet.js.map +1 -0
  503. package/dist/validate-structure-layer-computed-y.d.ts +14 -0
  504. package/dist/validate-structure-layer-computed-y.d.ts.map +1 -0
  505. package/dist/validate-structure-layer-computed-y.js +70 -0
  506. package/dist/validate-structure-layer-computed-y.js.map +1 -0
  507. package/dist/validate-structure-layer-ribbon.d.ts +8 -0
  508. package/dist/validate-structure-layer-ribbon.d.ts.map +1 -0
  509. package/dist/validate-structure-layer-ribbon.js +36 -0
  510. package/dist/validate-structure-layer-ribbon.js.map +1 -0
  511. package/dist/validate-structure-layer-rule.d.ts +14 -0
  512. package/dist/validate-structure-layer-rule.d.ts.map +1 -0
  513. package/dist/validate-structure-layer-rule.js +90 -0
  514. package/dist/validate-structure-layer-rule.js.map +1 -0
  515. package/dist/validate-structure-layer-shared.d.ts +12 -0
  516. package/dist/validate-structure-layer-shared.d.ts.map +1 -0
  517. package/dist/validate-structure-layer-shared.js +17 -0
  518. package/dist/validate-structure-layer-shared.js.map +1 -0
  519. package/dist/validate-structure-layers.d.ts +12 -0
  520. package/dist/validate-structure-layers.d.ts.map +1 -0
  521. package/dist/validate-structure-layers.js +292 -0
  522. package/dist/validate-structure-layers.js.map +1 -0
  523. package/dist/validate-structure-paint.d.ts +12 -0
  524. package/dist/validate-structure-paint.d.ts.map +1 -0
  525. package/dist/validate-structure-paint.js +87 -0
  526. package/dist/validate-structure-paint.js.map +1 -0
  527. package/dist/validate-structure-scales.d.ts +10 -0
  528. package/dist/validate-structure-scales.d.ts.map +1 -0
  529. package/dist/validate-structure-scales.js +242 -0
  530. package/dist/validate-structure-scales.js.map +1 -0
  531. package/dist/validate-structure-style-bins.d.ts +17 -0
  532. package/dist/validate-structure-style-bins.d.ts.map +1 -0
  533. package/dist/validate-structure-style-bins.js +150 -0
  534. package/dist/validate-structure-style-bins.js.map +1 -0
  535. package/dist/validate-structure.d.ts +16 -0
  536. package/dist/validate-structure.d.ts.map +1 -0
  537. package/dist/validate-structure.js +16 -0
  538. package/dist/validate-structure.js.map +1 -0
  539. package/dist/validate.d.ts +24 -0
  540. package/dist/validate.d.ts.map +1 -0
  541. package/dist/validate.js +178 -0
  542. package/dist/validate.js.map +1 -0
  543. package/package.json +59 -0
  544. package/schema/v0.json +9618 -0
  545. package/src/artifact.ts +81 -0
  546. package/src/builder-core.ts +276 -0
  547. package/src/builder-data.ts +156 -0
  548. package/src/builder-geoms.ts +290 -0
  549. package/src/builder-options.ts +428 -0
  550. package/src/builder-scales.ts +443 -0
  551. package/src/builder.ts +125 -0
  552. package/src/capabilities-data.ts +336 -0
  553. package/src/capabilities-helpers.ts +54 -0
  554. package/src/capabilities.ts +159 -0
  555. package/src/coord-helpers.ts +177 -0
  556. package/src/coord-reference-build.ts +283 -0
  557. package/src/coord-reference.ts +75 -0
  558. package/src/error-catalog.ts +253 -0
  559. package/src/error-prose-shared.ts +106 -0
  560. package/src/errors.ts +87 -0
  561. package/src/generated/coord-reference-data.ts +235 -0
  562. package/src/generated/geom-param-keys.ts +222 -0
  563. package/src/generated/geom-reference-data.ts +3024 -0
  564. package/src/generated/guide-reference-data.ts +284 -0
  565. package/src/generated/plot-spec-validator.ts +7335 -0
  566. package/src/generated/position-reference-data.ts +191 -0
  567. package/src/generated/scale-reference-data.ts +10665 -0
  568. package/src/generated/stat-reference-data.ts +339 -0
  569. package/src/geom-params-build.ts +92 -0
  570. package/src/geom-params.ts +14 -0
  571. package/src/geom-reference-build.ts +292 -0
  572. package/src/geom-reference.ts +127 -0
  573. package/src/guide-helpers.ts +44 -0
  574. package/src/guide-reference-build.ts +249 -0
  575. package/src/guide-reference.ts +76 -0
  576. package/src/hue-grey-palettes.ts +82 -0
  577. package/src/index.ts +400 -0
  578. package/src/lint-catalog.ts +57 -0
  579. package/src/lint-layer-rules.ts +175 -0
  580. package/src/lint-scale-rules.ts +285 -0
  581. package/src/lint.ts +126 -0
  582. package/src/normalize-coord.ts +126 -0
  583. package/src/normalize-input-layers-marks.ts +276 -0
  584. package/src/normalize-input-layers-special.ts +252 -0
  585. package/src/normalize-input.ts +154 -0
  586. package/src/normalize-paint.ts +98 -0
  587. package/src/normalize-scales.ts +108 -0
  588. package/src/normalize.ts +403 -0
  589. package/src/paint-helpers.ts +98 -0
  590. package/src/pipeline-error-catalog.ts +278 -0
  591. package/src/portability-check.ts +155 -0
  592. package/src/portability-lossy.ts +81 -0
  593. package/src/portability.ts +31 -0
  594. package/src/portable-builder.ts +35 -0
  595. package/src/portable-entry.ts +37 -0
  596. package/src/position-reference-build.ts +164 -0
  597. package/src/position-reference.ts +41 -0
  598. package/src/runtime.ts +198 -0
  599. package/src/scale-color-helpers.ts +263 -0
  600. package/src/scale-color-hue-grey.ts +95 -0
  601. package/src/scale-color-stops.ts +177 -0
  602. package/src/scale-color-viridis.ts +99 -0
  603. package/src/scale-helpers.ts +264 -0
  604. package/src/scale-multi-helpers.ts +220 -0
  605. package/src/scale-position-helpers.ts +231 -0
  606. package/src/scale-reference-build.ts +222 -0
  607. package/src/scale-reference-classify-color.ts +334 -0
  608. package/src/scale-reference-classify-position.ts +164 -0
  609. package/src/scale-reference-classify-style.ts +229 -0
  610. package/src/scale-reference-classify-support.ts +298 -0
  611. package/src/scale-reference.ts +165 -0
  612. package/src/scale-style-helpers.ts +316 -0
  613. package/src/schema-catalog.ts +222 -0
  614. package/src/schema-declarations.ts +51 -0
  615. package/src/schema-decls-data.ts +272 -0
  616. package/src/schema-decls-guides-theme.ts +395 -0
  617. package/src/schema-decls-layers-col-boxplot.ts +410 -0
  618. package/src/schema-decls-layers-count-pointrange.ts +321 -0
  619. package/src/schema-decls-layers-crossbar-jitter.ts +443 -0
  620. package/src/schema-decls-layers-point-path.ts +246 -0
  621. package/src/schema-decls-layers-sf-blank.ts +408 -0
  622. package/src/schema-decls-layers-text-map.ts +259 -0
  623. package/src/schema-decls-paint.ts +124 -0
  624. package/src/schema-decls-params-area-abline.ts +440 -0
  625. package/src/schema-decls-params-col-contour.ts +333 -0
  626. package/src/schema-decls-params-curve-label.ts +343 -0
  627. package/src/schema-decls-params-errorbar-position.ts +446 -0
  628. package/src/schema-decls-params-point-path.ts +394 -0
  629. package/src/schema-decls-params-violin-summary.ts +447 -0
  630. package/src/schema-decls-plot.ts +385 -0
  631. package/src/schema-decls-scales-guides.ts +233 -0
  632. package/src/schema-decls-scales.ts +432 -0
  633. package/src/schema-imports.ts +116 -0
  634. package/src/schema-name-schemas.ts +37 -0
  635. package/src/schema-names.ts +243 -0
  636. package/src/schema-types.ts +400 -0
  637. package/src/schema.ts +287 -0
  638. package/src/stat-reference-build.ts +145 -0
  639. package/src/stat-reference.ts +38 -0
  640. package/src/structural-gate.ts +42 -0
  641. package/src/temporal-column.ts +323 -0
  642. package/src/temporal-guides.ts +32 -0
  643. package/src/temporal-interval-schema.ts +40 -0
  644. package/src/temporal-interval.ts +151 -0
  645. package/src/temporal-parse-core.ts +331 -0
  646. package/src/temporal-parse-format.ts +222 -0
  647. package/src/temporal-parse-schema.ts +38 -0
  648. package/src/temporal-parse.ts +259 -0
  649. package/src/temporal-polyfill.ts +27 -0
  650. package/src/temporal-ticks.ts +312 -0
  651. package/src/temporal.ts +112 -0
  652. package/src/validate-data-checks-color-temporal.ts +261 -0
  653. package/src/validate-data-checks-color.ts +395 -0
  654. package/src/validate-data-checks-layer.ts +492 -0
  655. package/src/validate-data-checks-position.ts +275 -0
  656. package/src/validate-data-checks-style-finite.ts +55 -0
  657. package/src/validate-data-checks-style-numeric.ts +455 -0
  658. package/src/validate-data-checks-style.ts +13 -0
  659. package/src/validate-data-checks-temporal.ts +129 -0
  660. package/src/validate-data-checks.ts +157 -0
  661. package/src/validate-data-evidence.ts +419 -0
  662. package/src/validate-data.ts +114 -0
  663. package/src/validate-map-errors.ts +126 -0
  664. package/src/validate-map-forms.ts +148 -0
  665. package/src/validate-map-path-keywords.ts +129 -0
  666. package/src/validate-map-path-unions.ts +281 -0
  667. package/src/validate-schema-shape.ts +232 -0
  668. package/src/validate-schema-walk.ts +219 -0
  669. package/src/validate-structure-facet.ts +76 -0
  670. package/src/validate-structure-layer-computed-y.ts +94 -0
  671. package/src/validate-structure-layer-ribbon.ts +61 -0
  672. package/src/validate-structure-layer-rule.ts +98 -0
  673. package/src/validate-structure-layer-shared.ts +29 -0
  674. package/src/validate-structure-layers.ts +395 -0
  675. package/src/validate-structure-paint.ts +101 -0
  676. package/src/validate-structure-scales.ts +278 -0
  677. package/src/validate-structure-style-bins.ts +167 -0
  678. package/src/validate-structure.ts +16 -0
  679. package/src/validate.ts +212 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Scale-level lint rules: transform-domain-data, fractional-calendar-years.
3
+ * Layer rules: lint-layer-rules.ts. Orchestrator: lint.ts.
4
+ */
5
+ import type { Aes, CellValue, PositionScaleSpec } from "./schema.js";
6
+ import type { SpecAdvisory } from "./lint.js";
7
+ import type { LintFieldOf } from "./lint-layer-rules.js";
8
+
9
+ function isRecord(v: unknown): v is Record<string, unknown> {
10
+ return typeof v === "object" && v !== null && !Array.isArray(v);
11
+ }
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // fractional-calendar-years
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /** Four-digit Gregorian-ish years; excludes tiny indices and huge scientific IDs. */
18
+ const YEAR_LIKE_MIN = 1000;
19
+ const YEAR_LIKE_MAX = 10_000;
20
+ /**
21
+ * Distance from k/12 allowed when classifying a fractional part as a month
22
+ * index. ~0.02 ≈ one week of a year — tight enough to reject day-of-year
23
+ * fractions (i/365) while accepting float noise on m/12 (e.g. 1854.333).
24
+ */
25
+ const MONTH_FRAC_TOLERANCE = 0.02;
26
+ /** Need several points so a lone mid-year value cannot trip the rule. */
27
+ const MIN_YEAR_LIKE_SAMPLES = 6;
28
+ /** At least this many non-integer year-likes (months between Januaries). */
29
+ const MIN_FRACTIONAL_SAMPLES = 3;
30
+ /** Share of finite numbers that must be year-like (column is a calendar series). */
31
+ const MIN_YEAR_LIKE_DOMINANCE = 0.8;
32
+ /** Share of year-like values that must sit on the month grid (k/12). */
33
+ const MIN_MONTH_GRID_HIT_RATE = 0.7;
34
+ /** Share of year-like values that must be non-integer. */
35
+ const MIN_FRACTIONAL_RATE = 0.2;
36
+ /**
37
+ * Distinct month indices (0–11) required. Real monthly series hit many; pure
38
+ * quarter/half quantizations only hit {0,3,6,9} and must stay silent.
39
+ */
40
+ const MIN_DISTINCT_MONTHS = 4;
41
+ /** Quarter-only grids (Jan/Apr/Jul/Oct) — not enough to call "months". */
42
+ const QUARTER_MONTH_INDEX = new Set([0, 3, 6, 9]);
43
+
44
+ /**
45
+ * True when values look like calendar months encoded as year + month/12 on a
46
+ * linear scale — the Nightingale theme-specimen pitfall that labels axes and
47
+ * Inspect pins as decimals (`1855.9`). Pure integer years and ordinary
48
+ * 1000–9999 measurements quantized to quarters/halves stay silent.
49
+ * Exercised through lintSpec (lint-rules.test.ts), not as a public export.
50
+ */
51
+ function looksLikeFractionalCalendarYears(values: readonly CellValue[]): boolean {
52
+ const yearLike: number[] = [];
53
+ let finiteCount = 0;
54
+ for (const value of values) {
55
+ if (typeof value !== "number" || !Number.isFinite(value)) continue;
56
+ finiteCount++;
57
+ if (value >= YEAR_LIKE_MIN && value < YEAR_LIKE_MAX) yearLike.push(value);
58
+ }
59
+ if (yearLike.length < MIN_YEAR_LIKE_SAMPLES) return false;
60
+ if (yearLike.length / finiteCount < MIN_YEAR_LIKE_DOMINANCE) return false;
61
+
62
+ const stats = fractionalYearStats(yearLike);
63
+ if (stats.fractional < MIN_FRACTIONAL_SAMPLES) return false;
64
+ if (stats.fractional / yearLike.length < MIN_FRACTIONAL_RATE) return false;
65
+ if (stats.monthGridHits / yearLike.length < MIN_MONTH_GRID_HIT_RATE) return false;
66
+ if (stats.monthIndices.size < MIN_DISTINCT_MONTHS) return false;
67
+ return stats.nonQuarterFractional >= 1;
68
+ }
69
+
70
+ function fractionalYearStats(yearLike: readonly number[]): {
71
+ fractional: number;
72
+ monthGridHits: number;
73
+ monthIndices: Set<number>;
74
+ nonQuarterFractional: number;
75
+ } {
76
+ let fractional = 0;
77
+ let monthGridHits = 0;
78
+ const monthIndices = new Set<number>();
79
+ let nonQuarterFractional = 0;
80
+ for (const value of yearLike) {
81
+ const floor = Math.floor(value);
82
+ const frac = value - floor;
83
+ const isFractional = frac > 1e-9 && frac < 1 - 1e-9;
84
+ if (isFractional) fractional++;
85
+
86
+ // Nearest twelfth; k=12 (frac≈1) collapses to the integer edge.
87
+ const kRaw = Math.round(frac * 12);
88
+ const k = kRaw >= 12 ? 0 : kRaw;
89
+ const expected = kRaw >= 12 ? 1 : k / 12;
90
+ const dist = Math.abs(frac - expected);
91
+ // Integers always hit month 0; fractions must be within tolerance of k/12.
92
+ if (!isFractional || dist <= MONTH_FRAC_TOLERANCE) {
93
+ monthGridHits++;
94
+ monthIndices.add(isFractional ? k : 0);
95
+ if (isFractional && !QUARTER_MONTH_INDEX.has(k)) nonQuarterFractional++;
96
+ }
97
+ }
98
+ return { fractional, monthGridHits, monthIndices, nonQuarterFractional };
99
+ }
100
+
101
+ /** First year-like fractional sample for advisory copy, or null. */
102
+ function sampleFractionalYear(values: readonly CellValue[]): number | null {
103
+ for (const value of values) {
104
+ if (typeof value !== "number" || !Number.isFinite(value)) continue;
105
+ if (value < YEAR_LIKE_MIN || value >= YEAR_LIKE_MAX) continue;
106
+ const frac = value - Math.floor(value);
107
+ if (frac > 1e-9 && frac < 1 - 1e-9) return value;
108
+ }
109
+ return null;
110
+ }
111
+
112
+ /** Position scale types that already treat the channel as non-linear-year. */
113
+ function isNonLinearYearScale(config: PositionScaleSpec | undefined): boolean {
114
+ if (config === undefined) return false;
115
+ return config.type === "time" || config.type === "band";
116
+ }
117
+
118
+ /** Forward-transform domain rule for a position scale, or null if none. */
119
+ interface TransformDomain {
120
+ valid(value: number): boolean;
121
+ /** Human label for the message ("log10" | "sqrt"). */
122
+ label: string;
123
+ /** Short description of the valid range ("positive" | "non-negative"). */
124
+ rule: string;
125
+ }
126
+
127
+ /**
128
+ * Resolve the effective forward-transform domain for a position scale, reading
129
+ * the spec BEFORE normalization. Both the authored legacy `type: "log"` and the
130
+ * canonical `transform: "log10" | "sqrt"` are covered; identity/time/untyped
131
+ * scales return null.
132
+ */
133
+ function transformDomainOf(config: PositionScaleSpec | undefined): TransformDomain | null {
134
+ if (config === undefined) return null;
135
+ const transform = config.transform ?? (config.type === "log" ? "log10" : undefined);
136
+ if (transform === "log10") {
137
+ return { valid: (v) => v > 0, label: "log10", rule: "positive" };
138
+ }
139
+ if (transform === "sqrt") {
140
+ return { valid: (v) => v >= 0, label: "sqrt", rule: "non-negative" };
141
+ }
142
+ return null;
143
+ }
144
+
145
+ /** Count numeric values inside/outside a transform domain (non-numbers ignored). */
146
+ function countTransformDomain(
147
+ values: readonly unknown[],
148
+ domain: TransformDomain,
149
+ ): readonly [inDomain: number, outOfDomain: number] {
150
+ let inDomain = 0;
151
+ let outOfDomain = 0;
152
+ for (const v of values) {
153
+ if (typeof v !== "number") continue;
154
+ if (domain.valid(v)) inDomain++;
155
+ else outOfDomain++;
156
+ }
157
+ return [inDomain, outOfDomain];
158
+ }
159
+
160
+ /** Scale-scoped advisories for one lintSpec pass. */
161
+ function collectTransformAdvisories(input: {
162
+ layers: unknown[];
163
+ scales: Record<string, unknown> | undefined;
164
+ fieldOf: LintFieldOf;
165
+ }): SpecAdvisory[] {
166
+ const { layers, scales, fieldOf } = input;
167
+ const advisories: SpecAdvisory[] = [];
168
+
169
+ // --- transform-domain-data (scale-level, once per axis) ---------------------
170
+ // Detect the effective forward-transform domain BEFORE normalization: an
171
+ // authored `type: "log"` (canonicalizes to transform: "log10") and a
172
+ // canonical `transform: "log10" | "sqrt"` are all handled here. log10 rejects
173
+ // values <= 0; sqrt rejects values < 0. The advisory fires only on MIXED data
174
+ // (some in-domain, some out) — all-invalid is the pipeline's error/warning.
175
+ //
176
+ // Multi-layer charts often share the same mapped field (plot aes or repeated
177
+ // layer aes). Memoize [inDomain, outOfDomain] per field name for this axis so
178
+ // a shared column is scanned once (O(n)), not once per layer (O(L·n)).
179
+ for (const axis of ["x", "y"] as const) {
180
+ const raw = scales?.[axis];
181
+ // Schema-invalid scale entries must not throw — lint never blocks.
182
+ if (raw !== undefined && !isRecord(raw)) continue;
183
+ const config = raw as PositionScaleSpec | undefined;
184
+ const domain = transformDomainOf(config);
185
+ if (domain === null) continue;
186
+ const fieldDomainCounts = new Map<string, readonly [inDomain: number, outOfDomain: number]>();
187
+ for (let i = 0; i < layers.length; i++) {
188
+ const layer = layers[i];
189
+ if (!isRecord(layer)) continue;
190
+ const layerAes = isRecord(layer["aes"]) ? (layer["aes"] as Aes) : undefined;
191
+ const use = fieldOf(layerAes, axis);
192
+ const values = use?.info.values;
193
+ if (use === null || values === null || values === undefined) continue;
194
+ let counts = fieldDomainCounts.get(use.field);
195
+ if (counts === undefined) {
196
+ counts = countTransformDomain(values, domain);
197
+ fieldDomainCounts.set(use.field, counts);
198
+ }
199
+ const [inDomain, outOfDomain] = counts;
200
+ if (inDomain > 0 && outOfDomain > 0) {
201
+ advisories.push({
202
+ code: "transform-domain-data",
203
+ path: `/scales/${axis}`,
204
+ message: `scales.${axis} is a ${domain.label} scale, but field "${use.field}" has ${outOfDomain} value(s) outside its ${domain.rule} domain alongside ${inDomain} valid — those rows will be silently dropped before stats.`,
205
+ suggestion: {
206
+ description: `Filter the out-of-domain rows deliberately, or use an identity/linear scale if ${domain.rule} values are meaningful.`,
207
+ },
208
+ });
209
+ break; // one advisory per axis is enough
210
+ }
211
+ }
212
+ }
213
+
214
+ return advisories;
215
+ }
216
+
217
+ function collectFractionalYearAdvisories(input: {
218
+ layers: unknown[];
219
+ scales: Record<string, unknown> | undefined;
220
+ fieldOf: LintFieldOf;
221
+ }): SpecAdvisory[] {
222
+ const { layers, scales, fieldOf } = input;
223
+ const advisories: SpecAdvisory[] = [];
224
+
225
+ // --- fractional-calendar-years (x only, once) ------------------------------
226
+ // Monthly series re-encoded as year + month/12 and plotted on a default or
227
+ // explicit linear scale. Linear defaultTickFormat then prints 1855.9; Inspect
228
+ // pins echo the same. Time/band scales are out of scope (different contract).
229
+ // Only x: calendar-as-y is rare and ordinary 1000–9999 y measures would FP.
230
+ //
231
+ // Memoize the look-alike verdict per field so multi-layer charts scan once.
232
+ {
233
+ const axis = "x" as const;
234
+ const raw = scales?.[axis];
235
+ if (raw === undefined || isRecord(raw)) {
236
+ const config = raw as PositionScaleSpec | undefined;
237
+ if (!isNonLinearYearScale(config)) {
238
+ const fieldVerdict = new Map<string, boolean>();
239
+ let fired = false;
240
+ for (let i = 0; i < layers.length && !fired; i++) {
241
+ const layer = layers[i];
242
+ if (!isRecord(layer)) continue;
243
+ const layerAes = isRecord(layer["aes"]) ? (layer["aes"] as Aes) : undefined;
244
+ const use = fieldOf(layerAes, axis);
245
+ const values = use?.info.values;
246
+ if (use === null || values === null || values === undefined) continue;
247
+ // Only quantitative columns; temporal/string years are a different path.
248
+ if (use.info.type !== null && use.info.type !== "quantitative") continue;
249
+
250
+ let hit = fieldVerdict.get(use.field);
251
+ if (hit === undefined) {
252
+ hit = looksLikeFractionalCalendarYears(values);
253
+ fieldVerdict.set(use.field, hit);
254
+ }
255
+ if (!hit) continue;
256
+
257
+ const sample = sampleFractionalYear(values);
258
+ const sampleText =
259
+ sample === null ? "year + month/12" : String(Math.round(sample * 1000) / 1000);
260
+ advisories.push({
261
+ code: "fractional-calendar-years",
262
+ path: `/aes/${axis}`,
263
+ message: `Field "${use.field}" on ${axis} looks like calendar months encoded as fractional years (e.g. ${sampleText}) on a linear scale — tick labels and Inspect pins will show decimals like 1855.9 instead of months.`,
264
+ suggestion: {
265
+ description:
266
+ 'Store ISO year-month or date strings ("1854-04" / "1854-04-01") and use a time scale so labels read as calendar months.',
267
+ example: { scales: { [axis]: { type: "time", labels: "%b %Y" } } },
268
+ },
269
+ });
270
+ fired = true;
271
+ }
272
+ }
273
+ }
274
+ }
275
+
276
+ return advisories;
277
+ }
278
+
279
+ export function collectScaleLintAdvisories(input: {
280
+ layers: unknown[];
281
+ scales: Record<string, unknown> | undefined;
282
+ fieldOf: LintFieldOf;
283
+ }): SpecAdvisory[] {
284
+ return [...collectTransformAdvisories(input), ...collectFractionalYearAdvisories(input)];
285
+ }
package/src/lint.ts ADDED
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Spec lint — advisories for VALID-but-questionable specs (Hadley lesson 16:
3
+ * "we can produce many plots that don't make sense, yet are grammatically
4
+ * valid"). `lintSpec()` never errors and never blocks: it returns
5
+ * `SpecAdvisory[]` — high value for agent-generated charts, where a
6
+ * grammatically valid spec can still be a meaningless plot.
7
+ *
8
+ * Distinct from the two error tiers (validate()) and from the pipeline's
9
+ * heuristic advisories (@ggts-sh/core `RenderModel.advisories`): lint runs
10
+ * BEFORE any pipeline, on the spec alone (plus inline data or a DataProfile
11
+ * when available — data-dependent rules skip silently without evidence).
12
+ *
13
+ * Rules considered and deliberately ABSENT (documented for auditability):
14
+ * - dual-axis misuse: the grammar has no dual axes — unrepresentable.
15
+ * - pie-chart overuse: polar/radial coords exist; a dedicated "prefer bar
16
+ * over pie" advisory is deferred until usage evidence exists.
17
+ * - text layer without a label aes: that is a validation ERROR
18
+ * (`missing-required-channel`), not an advisory.
19
+ * - transform scale over entirely out-of-domain data: the pipeline handles
20
+ * it via the `scale-transform-domain` error (explicit limits) / warning
21
+ * (data), plus the applicable censor count; the lint advisory here fires on
22
+ * MIXED data (some in-domain, some out), where the silent row-drop is most
23
+ * surprising.
24
+ *
25
+ * Implementation:
26
+ * - lint-catalog.ts — pure advisory catalog (re-exported here)
27
+ * - lint-layer-rules.ts — layer-scoped rules
28
+ * - lint-scale-rules.ts — scale-level transform-domain-data and
29
+ * fractional-calendar-years (year+month/12 linear pitfall)
30
+ *
31
+ * Wired into `validate(spec, { lint: true })` (advisories ride the result)
32
+ * and into the `ggts render` CLI (stderr JSON lines, kind "advisory",
33
+ * source "spec-lint").
34
+ */
35
+ import type { JSONValue } from "./portability.js";
36
+ import type { Aes, ChannelName } from "./schema.js";
37
+ import type { FieldEvidenceEntry, FieldEvidenceMap, ValidateOptions } from "./validate-data.js";
38
+ import {
39
+ DEFAULT_VALIDATE_LIMITS,
40
+ effectiveChannel,
41
+ resolveFieldEvidence,
42
+ } from "./validate-data.js";
43
+
44
+ import type { LintAdvisoryCode } from "./lint-catalog.js";
45
+ import { collectLayerLintAdvisories } from "./lint-layer-rules.js";
46
+ import { collectScaleLintAdvisories } from "./lint-scale-rules.js";
47
+
48
+ export { LINT_CATALOG, type LintAdvisoryCode, type LintCatalogEntry } from "./lint-catalog.js";
49
+
50
+ /** One lint advisory: never an error, always actionable. */
51
+ export interface SpecAdvisory {
52
+ code: LintAdvisoryCode;
53
+ /** JSON pointer into the spec ("" = root). */
54
+ path: string;
55
+ message: string;
56
+ suggestion?: {
57
+ description: string;
58
+ example?: JSONValue;
59
+ };
60
+ }
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Field-type evidence (inline data or DataProfile; absent = rules skip)
64
+ // ---------------------------------------------------------------------------
65
+
66
+ function isRecord(v: unknown): v is Record<string, unknown> {
67
+ return typeof v === "object" && v !== null && !Array.isArray(v);
68
+ }
69
+
70
+ /**
71
+ * Standalone evidence build (lintSpec without a shared map from validate).
72
+ * Merges options.limits into DEFAULT_VALIDATE_LIMITS the same way validate()
73
+ * does, so callers can raise or lower maxRows/maxBytes and data-aware rules
74
+ * honor the override (skip over-limit; never fabricate a complaint).
75
+ */
76
+ function buildEvidence(
77
+ spec: Record<string, unknown>,
78
+ options: ValidateOptions | undefined,
79
+ ): FieldEvidenceMap | null {
80
+ const limits = { ...DEFAULT_VALIDATE_LIMITS, ...options?.limits };
81
+ const resolved = resolveFieldEvidence(spec, options ?? {}, limits);
82
+ return resolved.status === "ok" ? resolved.fields : null;
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // lintSpec
87
+ // ---------------------------------------------------------------------------
88
+
89
+ /**
90
+ * Lint a spec for valid-but-questionable constructs. `options` mirrors
91
+ * validate(): pass `{ profile }` to lint against out-of-band data, or let the
92
+ * spec's inline data provide the evidence; `options.limits` merges over
93
+ * DEFAULT_VALIDATE_LIMITS the same way validate() does. Rules whose evidence
94
+ * is missing (including over-limit inline data) skip silently — lint never
95
+ * fabricates a complaint.
96
+ *
97
+ * When called from validate({ lint: true }), pass `sharedEvidence` so field
98
+ * types/columns are not rebuilt after dataChecks already resolved them.
99
+ */
100
+ export function lintSpec(
101
+ spec: unknown,
102
+ options?: ValidateOptions,
103
+ sharedEvidence?: FieldEvidenceMap | null,
104
+ ): SpecAdvisory[] {
105
+ if (!isRecord(spec) || !Array.isArray(spec["layers"])) return [];
106
+ const evidence = sharedEvidence === undefined ? buildEvidence(spec, options) : sharedEvidence;
107
+ const plotAes = isRecord(spec["aes"]) ? (spec["aes"] as Aes) : undefined;
108
+ const layers = spec["layers"] as unknown[];
109
+ const scales = isRecord(spec["scales"]) ? spec["scales"] : undefined;
110
+
111
+ const fieldOf = (
112
+ layerAes: Aes | undefined,
113
+ channel: ChannelName,
114
+ ): { field: string; info: FieldEvidenceEntry } | null => {
115
+ const mapped = effectiveChannel(plotAes, layerAes, channel);
116
+ if (mapped === undefined || !("field" in mapped)) return null;
117
+ const info = evidence?.get(mapped.field);
118
+ if (info === undefined) return null;
119
+ return { field: mapped.field, info };
120
+ };
121
+
122
+ return [
123
+ ...collectLayerLintAdvisories({ layers, fieldOf }),
124
+ ...collectScaleLintAdvisories({ layers, scales, fieldOf }),
125
+ ];
126
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Coordinate-system canonicalization for normalize()
3
+ * (cartesian/flip/transform/fixed/sf/radial).
4
+ * Scales: normalize-scales.ts. Layer/aes orchestration: normalize.ts.
5
+ */
6
+
7
+ import type {
8
+ CoordRadialSpec,
9
+ CoordSpec,
10
+ CoordTransformAxisSpec,
11
+ CoordTransformSpec,
12
+ } from "./schema.js";
13
+
14
+ function normalizeCoordAxis(
15
+ axis: CoordTransformAxisSpec | undefined,
16
+ ): CoordTransformAxisSpec | undefined {
17
+ if (axis === undefined) return axis;
18
+ const runtimeAxis = axis as unknown;
19
+ if (runtimeAxis === null || typeof runtimeAxis !== "object" || Array.isArray(runtimeAxis))
20
+ return runtimeAxis as CoordTransformAxisSpec;
21
+ // Preserve unknown and malformed runtime values so strict schema validation
22
+ // can reject them; only copy valid tuple-shaped limits defensively.
23
+ const record = runtimeAxis as Record<string, unknown>;
24
+ const normalized = {
25
+ ...record,
26
+ ...(Array.isArray(record["limits"]) && {
27
+ limits: [...(record["limits"] as unknown[])],
28
+ }),
29
+ } as CoordTransformAxisSpec;
30
+ // Drop valid defaults (including explicit undefined from option spreads);
31
+ // preserve malformed non-booleans for schema reject.
32
+ if (record["reverse"] === false || record["reverse"] === undefined) delete normalized.reverse;
33
+ if (record["expand"] === true || record["expand"] === undefined) delete normalized.expand;
34
+ return normalized;
35
+ }
36
+
37
+ function effectiveCoordAxis(axis: ReturnType<typeof normalizeCoordAxis>): boolean {
38
+ if (axis === undefined) return false;
39
+ const runtimeAxis = axis as unknown;
40
+ if (runtimeAxis === null || typeof runtimeAxis !== "object" || Array.isArray(runtimeAxis))
41
+ return true;
42
+ const reverse = (axis as { reverse?: unknown }).reverse;
43
+ const expand = (axis as { expand?: unknown }).expand;
44
+ // Non-boolean reverse/expand must stay effective so schema can reject them.
45
+ const malformedReverse = reverse !== undefined && reverse !== true && reverse !== false;
46
+ const malformedExpand = expand !== undefined && expand !== true && expand !== false;
47
+ return (
48
+ axis.transform !== "identity" ||
49
+ axis.limits !== undefined ||
50
+ axis.reverse === true ||
51
+ axis.expand === false ||
52
+ malformedReverse ||
53
+ malformedExpand ||
54
+ Object.keys(axis).some(
55
+ (key) => key !== "transform" && key !== "limits" && key !== "reverse" && key !== "expand",
56
+ )
57
+ );
58
+ }
59
+
60
+ export function normalizeCoord(coord: CoordSpec | undefined): CoordSpec | undefined {
61
+ if (coord === undefined) return undefined;
62
+ const runtimeCoord = coord as unknown;
63
+ if (runtimeCoord === null || typeof runtimeCoord !== "object" || Array.isArray(runtimeCoord))
64
+ return runtimeCoord as CoordSpec;
65
+ const record = runtimeCoord as Record<string, unknown>;
66
+ if (record["type"] === "cartesian")
67
+ return Object.keys(record).some((key) => key !== "type")
68
+ ? ({ ...record } as CoordSpec)
69
+ : undefined;
70
+ if (record["type"] === "flip") return { ...record } as CoordSpec;
71
+ if (record["type"] === "fixed" || record["type"] === "sf") {
72
+ return normalizeFixedCoord(record);
73
+ }
74
+ if (record["type"] === "radial") {
75
+ return normalizeRadialCoord(record);
76
+ }
77
+ if (record["type"] !== "transform") return { ...record } as CoordSpec;
78
+ return normalizeTransformCoord(coord as CoordTransformSpec);
79
+ }
80
+
81
+ function normalizeFixedCoord(record: Record<string, unknown>): CoordSpec {
82
+ const fixed = { ...record } as unknown as { type: "fixed" | "sf"; ratio?: unknown };
83
+ if (fixed.ratio === 1 || fixed.ratio === undefined) delete fixed.ratio;
84
+ return fixed as CoordSpec;
85
+ }
86
+
87
+ function normalizeRadialCoord(record: Record<string, unknown>): CoordSpec {
88
+ const radial = { ...record } as unknown as CoordRadialSpec & Record<string, unknown>;
89
+ if (radial.theta === "x" || radial.theta === undefined) delete radial.theta;
90
+ if (radial.start === 0 || radial.start === undefined) delete radial.start;
91
+ if (radial.innerRadius === 0 || radial.innerRadius === undefined) delete radial.innerRadius;
92
+ if (radial.expand === true || radial.expand === undefined) delete radial.expand;
93
+ if (radial.clip === false || radial.clip === undefined) delete radial.clip;
94
+ if (radial.reverse === "none" || radial.reverse === undefined) delete radial.reverse;
95
+ if (Array.isArray(radial.thetaLimits) && radial.thetaLimits.length === 2) {
96
+ radial.thetaLimits = [radial.thetaLimits[0]!, radial.thetaLimits[1]!];
97
+ }
98
+ if (Array.isArray(radial.rLimits) && radial.rLimits.length === 2) {
99
+ radial.rLimits = [radial.rLimits[0]!, radial.rLimits[1]!];
100
+ }
101
+ return radial;
102
+ }
103
+
104
+ function normalizeTransformCoord(transformed: CoordTransformSpec): CoordSpec | undefined {
105
+ const x = normalizeCoordAxis(transformed.x);
106
+ const y = normalizeCoordAxis(transformed.y);
107
+ const hasUnknownKey = Object.keys(transformed).some(
108
+ (key) => key !== "type" && key !== "x" && key !== "y" && key !== "clip",
109
+ );
110
+ // Default clip is true/absent; only collapse when clip is a valid default.
111
+ const clipIsDefault = transformed.clip === true || transformed.clip === undefined;
112
+ if (!effectiveCoordAxis(x) && !effectiveCoordAxis(y) && clipIsDefault && !hasUnknownKey) {
113
+ return undefined;
114
+ }
115
+ const normalized: CoordSpec = {
116
+ ...transformed,
117
+ type: "transform",
118
+ ...(effectiveCoordAxis(x) && { x: x! }),
119
+ ...(effectiveCoordAxis(y) && { y: y! }),
120
+ };
121
+ if (!effectiveCoordAxis(x)) delete normalized.x;
122
+ if (!effectiveCoordAxis(y)) delete normalized.y;
123
+ // Keep false and non-boolean clip (malformed) for validation.
124
+ if (transformed.clip === true || transformed.clip === undefined) delete normalized.clip;
125
+ return normalized;
126
+ }