ggaction 0.0.12 → 0.0.15

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 (378) hide show
  1. package/CHANGELOG.md +127 -0
  2. package/README.md +20 -11
  3. package/knowledge/action-card.schema.json +1 -1
  4. package/knowledge/action-cards.json +1 -1
  5. package/knowledge/action-cards.schema.json +1 -1
  6. package/knowledge/extension-authoring.md +2 -1
  7. package/knowledge/intent-taxonomy.json +1 -1
  8. package/knowledge/mcp-resources.json +1 -1
  9. package/knowledge/task-resolver.js +1 -1
  10. package/package.json +10 -3
  11. package/src/actions/basic.js +4 -2
  12. package/src/actions/boxPlots/components.js +14 -4
  13. package/src/actions/boxPlots/edit.js +31 -5
  14. package/src/actions/boxPlots/materialize.js +13 -4
  15. package/src/actions/boxPlots/options.js +48 -16
  16. package/src/actions/canvas/actions.js +3 -1
  17. package/src/actions/canvas/fitting.js +211 -0
  18. package/src/actions/canvas/index.js +2 -0
  19. package/src/actions/categoryOrder/index.js +18 -6
  20. package/src/actions/charts/area.js +51 -0
  21. package/src/actions/charts/bar.js +13 -6
  22. package/src/actions/charts/beeswarm.js +70 -0
  23. package/src/actions/charts/density.js +48 -0
  24. package/src/actions/charts/ecdf.js +186 -0
  25. package/src/actions/charts/endpoints.js +473 -0
  26. package/src/actions/charts/heatmap.js +4 -3
  27. package/src/actions/charts/histogram.js +5 -4
  28. package/src/actions/charts/horizon.js +73 -0
  29. package/src/actions/charts/index.js +37 -0
  30. package/src/actions/charts/interval-regression.js +172 -0
  31. package/src/actions/charts/line.js +8 -6
  32. package/src/actions/charts/parallel.js +8 -2
  33. package/src/actions/charts/pie.js +52 -0
  34. package/src/actions/charts/polar.js +134 -0
  35. package/src/actions/charts/radar.js +305 -0
  36. package/src/actions/charts/radial.js +50 -0
  37. package/src/actions/charts/raincloud.js +422 -0
  38. package/src/actions/charts/rug-strip.js +283 -0
  39. package/src/actions/charts/scatter.js +11 -3
  40. package/src/actions/charts/shared.js +69 -10
  41. package/src/actions/composition/actions.js +129 -3
  42. package/src/actions/coordinates/actions.js +1 -1
  43. package/src/actions/coordinates/edit.js +76 -0
  44. package/src/actions/data/bin.js +29 -0
  45. package/src/actions/data/bin2d.js +57 -185
  46. package/src/actions/data/complete.js +26 -0
  47. package/src/actions/data/computed.js +24 -0
  48. package/src/actions/data/create.js +2 -2
  49. package/src/actions/data/density.js +12 -20
  50. package/src/actions/data/derived.js +111 -15
  51. package/src/actions/data/ecdf.js +38 -0
  52. package/src/actions/data/edit.js +539 -0
  53. package/src/actions/data/filter.js +226 -30
  54. package/src/actions/data/fold.js +24 -0
  55. package/src/actions/data/impute.js +26 -0
  56. package/src/actions/data/index.js +38 -0
  57. package/src/actions/data/interval.js +2 -0
  58. package/src/actions/data/intervalEdit.js +86 -4
  59. package/src/actions/data/normalize.js +27 -0
  60. package/src/actions/data/regression.js +19 -21
  61. package/src/actions/data/revisionBindings.js +145 -0
  62. package/src/actions/data/shared.js +20 -4
  63. package/src/actions/data/stack.js +26 -0
  64. package/src/actions/data/summary.js +26 -0
  65. package/src/actions/data/timeUnit.js +4 -1
  66. package/src/actions/data/window.js +1 -1
  67. package/src/actions/encodings/appearance.js +32 -13
  68. package/src/actions/encodings/barWidth.js +8 -0
  69. package/src/actions/encodings/basic.js +2 -0
  70. package/src/actions/encodings/channels.js +415 -0
  71. package/src/actions/encodings/color/continuous.js +19 -59
  72. package/src/actions/encodings/color/index.js +30 -23
  73. package/src/actions/encodings/color/layout.js +3 -97
  74. package/src/actions/encodings/color/policy.js +5 -8
  75. package/src/actions/encodings/density/resolve.js +37 -5
  76. package/src/actions/encodings/density.js +124 -34
  77. package/src/actions/encodings/histogram.js +3 -2
  78. package/src/actions/encodings/horizon/resolve.js +8 -4
  79. package/src/actions/encodings/horizon.js +18 -8
  80. package/src/actions/encodings/index.js +6 -0
  81. package/src/actions/encodings/offset.js +30 -29
  82. package/src/actions/encodings/parallel.js +2 -8
  83. package/src/actions/encodings/position/apply.js +63 -11
  84. package/src/actions/encodings/position/index.js +52 -6
  85. package/src/actions/encodings/position/policies/arc.js +14 -0
  86. package/src/actions/encodings/position/policies/area.js +4 -42
  87. package/src/actions/encodings/position/policies/bar.js +57 -13
  88. package/src/actions/encodings/position/resolve.js +88 -26
  89. package/src/actions/encodings/ranged.js +123 -97
  90. package/src/actions/encodings/remove.js +44 -34
  91. package/src/actions/encodings/ruleAppearance.js +24 -32
  92. package/src/actions/encodings/seriesLayout.js +105 -0
  93. package/src/actions/encodings/shared.js +107 -37
  94. package/src/actions/encodings/stroke.js +226 -0
  95. package/src/actions/encodings/strokeDash.js +9 -6
  96. package/src/actions/encodings/temporal.js +7 -0
  97. package/src/actions/encodings/text.js +30 -15
  98. package/src/actions/errorBands/create.js +41 -8
  99. package/src/actions/errorBands/edit.js +360 -25
  100. package/src/actions/errorBars/create.js +56 -17
  101. package/src/actions/errorBars/edit.js +372 -21
  102. package/src/actions/facets/actions.js +619 -41
  103. package/src/actions/facets/derive.js +139 -25
  104. package/src/actions/gradientPlots/components.js +43 -13
  105. package/src/actions/gradientPlots/edit.js +14 -4
  106. package/src/actions/gradientPlots/materialize.js +8 -8
  107. package/src/actions/gradientPlots/options.js +10 -2
  108. package/src/actions/guides/applicability.js +28 -19
  109. package/src/actions/guides/axes/axes.js +4 -3
  110. package/src/actions/guides/axes/axis.js +18 -18
  111. package/src/actions/guides/axes/components.js +43 -0
  112. package/src/actions/guides/axes/edit.js +16 -51
  113. package/src/actions/guides/axes/labels.js +184 -28
  114. package/src/actions/guides/axes/lines.js +13 -7
  115. package/src/actions/guides/axes/parallel/lifecycle.js +117 -0
  116. package/src/actions/guides/axes/parallel/policy.js +137 -0
  117. package/src/actions/guides/axes/parallel/resolve.js +50 -25
  118. package/src/actions/guides/axes/parallel.js +93 -217
  119. package/src/actions/guides/axes/policy.js +21 -67
  120. package/src/actions/guides/axes/tickGroups.js +15 -8
  121. package/src/actions/guides/axes/ticks.js +11 -6
  122. package/src/actions/guides/axes/titles.js +32 -11
  123. package/src/actions/guides/basic.js +2 -0
  124. package/src/actions/guides/facade.js +106 -0
  125. package/src/actions/guides/facadeAxes.js +200 -0
  126. package/src/actions/guides/facadeLegend.js +63 -0
  127. package/src/actions/guides/grids/grid.js +37 -33
  128. package/src/actions/guides/grids/resolve.js +17 -4
  129. package/src/actions/guides/guides.js +54 -31
  130. package/src/actions/guides/legends/blocks.js +426 -0
  131. package/src/actions/guides/legends/categorical/actions.js +309 -279
  132. package/src/actions/guides/legends/categorical/components.js +21 -17
  133. package/src/actions/guides/legends/categorical/index.js +37 -33
  134. package/src/actions/guides/legends/categorical/layout.js +123 -354
  135. package/src/actions/guides/legends/categorical/options.js +32 -16
  136. package/src/actions/guides/legends/categorical/recipes.js +114 -9
  137. package/src/actions/guides/legends/categorical/resolve.js +30 -15
  138. package/src/actions/guides/legends/categorical/symbols.js +87 -33
  139. package/src/actions/guides/legends/continuous/common.js +99 -26
  140. package/src/actions/guides/legends/continuous/gradient.js +120 -89
  141. package/src/actions/guides/legends/continuous/index.js +8 -2
  142. package/src/actions/guides/legends/continuous/interval.js +79 -95
  143. package/src/actions/guides/legends/continuous/opacity.js +113 -89
  144. package/src/actions/guides/legends/continuous/stroke.js +525 -0
  145. package/src/actions/guides/legends/creation.js +40 -0
  146. package/src/actions/guides/legends/edit.js +321 -90
  147. package/src/actions/guides/legends/focused.js +2 -2
  148. package/src/actions/guides/legends/index.js +2 -0
  149. package/src/actions/guides/legends/lane.js +149 -102
  150. package/src/actions/guides/legends/lifecycle.js +143 -0
  151. package/src/actions/guides/legends/remove.js +43 -78
  152. package/src/actions/guides/legends/sampling.js +140 -0
  153. package/src/actions/guides/legends/size.js +213 -116
  154. package/src/actions/guides/legends/strokeWidth.js +121 -73
  155. package/src/actions/guides/legends/target.js +123 -0
  156. package/src/actions/guides/legends/transition.js +192 -0
  157. package/src/actions/guides/polar/axes/facade.js +81 -66
  158. package/src/actions/guides/polar/axes/labels.js +28 -8
  159. package/src/actions/guides/polar/axes/lines.js +12 -5
  160. package/src/actions/guides/polar/axes/shared.js +22 -0
  161. package/src/actions/guides/polar/axes/ticks.js +6 -5
  162. package/src/actions/guides/polar/axes/titles.js +6 -5
  163. package/src/actions/guides/polar/grids.js +8 -8
  164. package/src/actions/guides/polar/resolve.js +12 -8
  165. package/src/actions/guides/reuse.js +44 -0
  166. package/src/actions/guides/tickValues.js +19 -21
  167. package/src/actions/index.js +15 -2
  168. package/src/actions/intervals/resolve.js +35 -15
  169. package/src/actions/marks/arc/actions.js +78 -20
  170. package/src/actions/marks/area/actions.js +51 -12
  171. package/src/actions/marks/area/materialize.js +22 -3
  172. package/src/actions/marks/bar/create.js +11 -2
  173. package/src/actions/marks/bar/edit.js +20 -3
  174. package/src/actions/marks/bar/materialize.js +33 -9
  175. package/src/actions/marks/index.js +14 -0
  176. package/src/actions/marks/line/actions.js +71 -113
  177. package/src/actions/marks/line/materialize.js +57 -56
  178. package/src/actions/marks/point/create.js +8 -2
  179. package/src/actions/marks/point/edit.js +29 -21
  180. package/src/actions/marks/point/index.js +1 -0
  181. package/src/actions/marks/point/jitter.js +3 -0
  182. package/src/actions/marks/point/materialize.js +154 -16
  183. package/src/actions/marks/point/packing.js +68 -0
  184. package/src/actions/marks/rect/actions.js +33 -12
  185. package/src/actions/marks/references.js +658 -0
  186. package/src/actions/marks/remove.js +87 -4
  187. package/src/actions/marks/rule/actions.js +77 -9
  188. package/src/actions/marks/rule/appearance.js +26 -0
  189. package/src/actions/marks/shared.js +13 -13
  190. package/src/actions/marks/text/actions.js +296 -13
  191. package/src/actions/marks/text/index.js +4 -0
  192. package/src/actions/marks/text/layout.js +156 -8
  193. package/src/actions/marks/text/removal.js +173 -0
  194. package/src/actions/marks/text/selection.js +73 -0
  195. package/src/actions/marks/tick/actions.js +22 -5
  196. package/src/actions/primitives/createGraphics.js +3 -1
  197. package/src/actions/primitives/editGraphics.js +26 -0
  198. package/src/actions/primitives/semantic.js +3 -2
  199. package/src/actions/primitives/semanticAction.js +120 -10
  200. package/src/actions/primitives/semanticValidation/guide.js +10 -0
  201. package/src/actions/primitives/semanticValidation/index.js +9 -1
  202. package/src/actions/primitives/semanticValidation/layer.js +23 -1
  203. package/src/actions/primitives/semanticValidation/scale.js +38 -8
  204. package/src/actions/regression/components.js +26 -11
  205. package/src/actions/regression/create.js +11 -3
  206. package/src/actions/regression/edit.js +40 -13
  207. package/src/actions/regression/resolve.js +1 -1
  208. package/src/actions/resources/index.js +7 -0
  209. package/src/actions/resources/remove.js +172 -0
  210. package/src/actions/scales/channels.js +212 -0
  211. package/src/actions/scales/consumers/common.js +21 -54
  212. package/src/actions/scales/consumers/families.js +28 -14
  213. package/src/actions/scales/consumers/index.js +42 -4
  214. package/src/actions/scales/consumers/seriesLayout.js +57 -12
  215. package/src/actions/scales/create.js +58 -26
  216. package/src/actions/scales/definitions.js +109 -39
  217. package/src/actions/scales/edit.js +101 -34
  218. package/src/actions/scales/editPolicy.js +95 -35
  219. package/src/actions/scales/index.js +6 -0
  220. package/src/actions/scales/materialize.js +14 -49
  221. package/src/actions/scales/offset.js +48 -0
  222. package/src/actions/scales/parallel.js +71 -0
  223. package/src/actions/scales/patch.js +1 -1
  224. package/src/actions/scales/preview.js +140 -0
  225. package/src/actions/scales/quantitativeColor.js +9 -5
  226. package/src/actions/selection/actions.js +154 -30
  227. package/src/actions/selection/index.js +3 -0
  228. package/src/actions/theme/actions.js +98 -0
  229. package/src/actions/theme/composition.js +238 -0
  230. package/src/actions/theme/index.js +16 -0
  231. package/src/actions/theme/reconcile.js +1130 -0
  232. package/src/actions/theme/state.js +160 -0
  233. package/src/actions/titles/actions.js +11 -10
  234. package/src/actions/titles/resolve.js +1 -40
  235. package/src/actions/violinPlots/create.js +90 -43
  236. package/src/actions/violinPlots/edit.js +192 -0
  237. package/src/actions/violinPlots/index.js +2 -0
  238. package/src/core/ChartProgram.js +7 -0
  239. package/src/core/action.js +32 -2
  240. package/src/core/compositionState.js +141 -8
  241. package/src/core/programState.js +20 -0
  242. package/src/core/resourceReferences.js +332 -0
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/grammar/aggregate.js +66 -23
  245. package/src/grammar/arcs.js +58 -1
  246. package/src/grammar/areaEndpoints.js +46 -0
  247. package/src/grammar/areaSeries.js +87 -41
  248. package/src/grammar/bars/aggregate.js +37 -13
  249. package/src/grammar/bars/geometry.js +44 -0
  250. package/src/grammar/bars/policy.js +9 -7
  251. package/src/grammar/bin.js +226 -0
  252. package/src/grammar/categoryOrder.js +34 -0
  253. package/src/grammar/complete.js +279 -0
  254. package/src/grammar/computed.js +351 -0
  255. package/src/grammar/coordinates.js +37 -0
  256. package/src/grammar/density.js +239 -37
  257. package/src/grammar/displayLabels.js +57 -0
  258. package/src/grammar/ecdf.js +198 -0
  259. package/src/grammar/facets/dependencies.js +133 -1
  260. package/src/grammar/facets/guides.js +3 -0
  261. package/src/grammar/facets/index.js +123 -52
  262. package/src/grammar/facets/scales.js +15 -18
  263. package/src/grammar/fold.js +123 -0
  264. package/src/grammar/horizon.js +6 -4
  265. package/src/grammar/impute.js +340 -0
  266. package/src/grammar/interval.js +37 -20
  267. package/src/grammar/lineSeries.js +20 -63
  268. package/src/grammar/markFilter.js +22 -4
  269. package/src/grammar/markLabelSelection.js +57 -0
  270. package/src/grammar/markLabels.js +110 -0
  271. package/src/grammar/normalize.js +366 -0
  272. package/src/grammar/parallelAxisTitles.js +16 -0
  273. package/src/grammar/pathSeries.js +109 -0
  274. package/src/grammar/pointPacking.js +183 -0
  275. package/src/grammar/pointShapes.js +13 -4
  276. package/src/grammar/polar.js +90 -8
  277. package/src/grammar/positionDatum.js +12 -0
  278. package/src/grammar/rects.js +9 -1
  279. package/src/grammar/regression/derive.js +4 -0
  280. package/src/grammar/regression/index.js +8 -2
  281. package/src/grammar/regression/models.js +22 -7
  282. package/src/grammar/regression/parameters.js +63 -13
  283. package/src/grammar/rotation.js +29 -0
  284. package/src/grammar/roundedRect.js +180 -0
  285. package/src/grammar/rules.js +8 -21
  286. package/src/grammar/scales/color.js +23 -1
  287. package/src/grammar/scales/colorConsumers.js +57 -0
  288. package/src/grammar/scales/continuous.js +2 -2
  289. package/src/grammar/scales/definition.js +42 -5
  290. package/src/grammar/scales/discretized.js +11 -5
  291. package/src/grammar/scales/fields.js +38 -8
  292. package/src/grammar/scales/index.js +1 -0
  293. package/src/grammar/scales/mapping.js +2 -0
  294. package/src/grammar/scales/ordinal.js +12 -3
  295. package/src/grammar/scales/policies.js +2 -2
  296. package/src/grammar/scales/radial.js +55 -0
  297. package/src/grammar/scales/size.js +456 -0
  298. package/src/grammar/scales/types.js +1 -0
  299. package/src/grammar/schemas/concreteGraphic.js +14 -2
  300. package/src/grammar/schemas/graphic.js +15 -0
  301. package/src/grammar/schemas/graphicBounds.js +179 -30
  302. package/src/grammar/schemas/semanticPath.js +23 -4
  303. package/src/grammar/stack.js +170 -0
  304. package/src/grammar/statisticalReference.js +103 -0
  305. package/src/grammar/statistics/confidenceInterval.js +101 -0
  306. package/src/grammar/strokeStyle.js +77 -0
  307. package/src/grammar/summary.js +245 -0
  308. package/src/grammar/text.js +22 -16
  309. package/src/grammar/timeUnit.js +234 -13
  310. package/src/grammar/transformTopology.js +10 -0
  311. package/src/grammar/transforms.js +418 -16
  312. package/src/grammar/valueFormat.js +112 -0
  313. package/src/grammar/weightedStatistics.js +374 -0
  314. package/src/grammar/window.js +232 -21
  315. package/src/layout/aspect.js +80 -0
  316. package/src/layout/facets.js +100 -12
  317. package/src/layout/guideCollisions.js +12 -0
  318. package/src/layout/labels.js +343 -0
  319. package/src/layout/legend.js +2 -10
  320. package/src/layout/legendItems.js +102 -0
  321. package/src/layout/legendLane.js +100 -11
  322. package/src/materialization/bars/aggregate.js +56 -147
  323. package/src/materialization/bars/histogram.js +104 -20
  324. package/src/materialization/bars/ranged.js +19 -3
  325. package/src/materialization/bars/resolve.js +7 -4
  326. package/src/materialization/categorySlotOffset.js +40 -0
  327. package/src/materialization/composition.js +3 -1
  328. package/src/materialization/compositionSnapshot.js +1 -1
  329. package/src/materialization/coordinateBounds.js +204 -0
  330. package/src/materialization/dependencies.js +93 -16
  331. package/src/materialization/encodings.js +1 -1
  332. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  333. package/src/materialization/facetGuides/placement.js +8 -2
  334. package/src/materialization/facetGuides/preparation.js +12 -7
  335. package/src/materialization/facetHeaders.js +346 -0
  336. package/src/materialization/facets.js +23 -66
  337. package/src/materialization/guides/layout.js +57 -0
  338. package/src/materialization/guides/resources.js +28 -2
  339. package/src/materialization/legends.js +11 -2
  340. package/src/materialization/marks/capabilities.js +20 -5
  341. package/src/materialization/marks/grouping.js +52 -0
  342. package/src/materialization/marks/index.js +68 -27
  343. package/src/materialization/marks/policies.js +4 -1
  344. package/src/materialization/rect.js +55 -42
  345. package/src/materialization/rectConfig.js +5 -1
  346. package/src/materialization/rowEncoding.js +13 -13
  347. package/src/materialization/scaleGuideDependencies.js +8 -5
  348. package/src/materialization/scales/map.js +6 -3
  349. package/src/materialization/scales/policies/arc.js +42 -0
  350. package/src/materialization/scales/policies/binnedPosition.js +7 -4
  351. package/src/materialization/scales/policies/offset.js +50 -8
  352. package/src/materialization/scales/policies/series.js +5 -1
  353. package/src/materialization/scales/resolve.js +65 -17
  354. package/src/materialization/selection/compatibility.js +11 -0
  355. package/src/materialization/selection/items/arc.js +28 -4
  356. package/src/materialization/selection/items/bar.js +42 -71
  357. package/src/materialization/selection/items/common.js +27 -6
  358. package/src/materialization/selection/items/path.js +3 -13
  359. package/src/materialization/selection/items/rect.js +35 -8
  360. package/src/materialization/selection/path.js +1 -1
  361. package/src/materialization/selection/point.js +3 -1
  362. package/src/materialization/text.js +324 -14
  363. package/src/mcp/adapter.js +2 -2
  364. package/src/renderers/canvas/circle.js +2 -3
  365. package/src/renderers/canvas/line.js +2 -3
  366. package/src/renderers/canvas/native.js +4 -4
  367. package/src/renderers/canvas/path.js +2 -3
  368. package/src/renderers/canvas/rect.js +2 -3
  369. package/src/renderers/canvas/stroke.js +11 -0
  370. package/src/renderers/svg.js +19 -0
  371. package/src/selectors/datasets.js +44 -0
  372. package/src/selectors/layers.js +7 -2
  373. package/src/selectors/scales.js +6 -0
  374. package/src/theme/defaults.js +140 -0
  375. package/types/basic.d.ts +17 -2
  376. package/types/index.d.ts +211 -0
  377. package/types/program.d.ts +2182 -204
  378. package/src/materialization/bars/grouped.js +0 -144
@@ -1,29 +1,274 @@
1
- import { isPlainObject } from "../core/immutable.js";
1
+ import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
2
2
  import { validateBoxTransform } from "./boxPlot.js";
3
+ import { normalizeBinTransform, validateBinTransform } from "./bin.js";
3
4
  import {
5
+ normalizeBin2DTransform,
4
6
  requestedBin2DTransform,
5
7
  validateBin2DTransform
6
8
  } from "./bin2d.js";
7
- import { validateDensityTransform } from "./density.js";
8
- import { validateFilterTransform } from "./filter.js";
9
+ import {
10
+ normalizeDensityTransform,
11
+ validateDensityTransform
12
+ } from "./density.js";
13
+ import {
14
+ normalizeECDFTransform,
15
+ validateECDFTransform
16
+ } from "./ecdf.js";
17
+ import {
18
+ normalizeComputedTransform,
19
+ validateComputedTransform
20
+ } from "./computed.js";
21
+ import {
22
+ normalizeCompleteTransform,
23
+ validateCompleteTransform
24
+ } from "./complete.js";
25
+ import {
26
+ normalizeFilterTransform,
27
+ validateFilterTransform
28
+ } from "./filter.js";
29
+ import { normalizeFoldTransform, validateFoldTransform } from "./fold.js";
9
30
  import {
10
31
  requestedGradientProfileTransform,
11
32
  validateGradientProfileTransform
12
33
  } from "./gradientProfile.js";
13
- import { validateIntervalTransform } from "./interval.js";
34
+ import {
35
+ normalizeIntervalTransform,
36
+ validateIntervalTransform
37
+ } from "./interval.js";
38
+ import {
39
+ normalizeImputeTransform,
40
+ validateImputeTransform
41
+ } from "./impute.js";
14
42
  import {
15
43
  requestedHorizonTransform,
16
44
  validateHorizonTransform
17
45
  } from "./horizon.js";
18
46
  import { validateMarkFilterTransform } from "./markFilter.js";
19
- import { validateRegressionTransform } from "./regression/index.js";
20
- import { validateWindowTransform } from "./window.js";
21
- import { validateTimeUnitTransform } from "./timeUnit.js";
47
+ import {
48
+ normalizeNormalizeTransform,
49
+ validateNormalizeTransform
50
+ } from "./normalize.js";
51
+ import {
52
+ normalizeRegressionTransform,
53
+ validateRegressionTransform
54
+ } from "./regression/index.js";
55
+ import { normalizeWindowTransform, validateWindowTransform } from "./window.js";
56
+ import {
57
+ normalizeTimeUnitTransform,
58
+ validateTimeUnitTransform
59
+ } from "./timeUnit.js";
60
+ import {
61
+ normalizeSummaryTransform,
62
+ validateSummaryTransform
63
+ } from "./summary.js";
64
+ import { normalizeStackTransform, validateStackTransform } from "./stack.js";
65
+ import { validateStatisticalReferenceTransform } from
66
+ "./statisticalReference.js";
22
67
  import { findTransformTopology } from "./transformTopology.js";
23
68
 
24
- function requestedDensityTransform(transform) {
69
+ function requestedTransform(transform) {
25
70
  const { resolved: _resolved, ...requested } = transform;
26
- return requested;
71
+ return cloneAndFreeze(requested);
72
+ }
73
+
74
+ const requestedDensityTransform = requestedTransform;
75
+
76
+ function requestedOptions(transform) {
77
+ const { type: _type, resolved: _resolved, ...options } = transform;
78
+ return options;
79
+ }
80
+
81
+ function mergedOptions(transform, patch) {
82
+ const options = { ...requestedOptions(transform) };
83
+ if (patch.weight === false) delete options.weight;
84
+ return {
85
+ ...options,
86
+ ...Object.fromEntries(
87
+ Object.entries(patch).filter(
88
+ ([key, value]) => !(key === "weight" && value === false)
89
+ )
90
+ )
91
+ };
92
+ }
93
+
94
+ function replaceExclusive(options, patch, keys) {
95
+ if (!keys.some(key => Object.hasOwn(patch, key))) return options;
96
+ const next = { ...options };
97
+ for (const key of keys) delete next[key];
98
+ return next;
99
+ }
100
+
101
+ function normalizeSimple(normalize) {
102
+ return (transform, patch) => normalize(mergedOptions(transform, patch));
103
+ }
104
+
105
+ function normalizeFilterEdit(transform, patch) {
106
+ const base = replaceExclusive(
107
+ requestedOptions(transform),
108
+ patch,
109
+ ["oneOf", "predicate", "range"]
110
+ );
111
+ return normalizeFilterTransform({ ...base, ...patch });
112
+ }
113
+
114
+ function normalizeBinEdit(transform, patch) {
115
+ const prior = requestedOptions(transform);
116
+ const base = replaceExclusive(
117
+ { ...prior, ...prior.bin },
118
+ patch,
119
+ ["maxBins", "step", "boundaries"]
120
+ );
121
+ delete base.bin;
122
+ if (patch.weight === false) delete base.weight;
123
+ return normalizeBinTransform({
124
+ ...base,
125
+ ...Object.fromEntries(
126
+ Object.entries(patch).filter(
127
+ ([key, value]) => !(key === "weight" && value === false)
128
+ )
129
+ )
130
+ });
131
+ }
132
+
133
+ function normalizeBin2DEdit(transform, patch, owner) {
134
+ const base = requestedOptions(transform);
135
+ const members = patch.members ?? base.members;
136
+ let as = patch.as ?? base.as;
137
+ if (!Object.hasOwn(patch, "as")) {
138
+ as = { ...as };
139
+ if (members && as.members === undefined) {
140
+ as.members = `__${owner}_members`;
141
+ } else if (!members) {
142
+ delete as.members;
143
+ }
144
+ }
145
+ return normalizeBin2DTransform({
146
+ id: owner,
147
+ ...base,
148
+ ...patch,
149
+ members,
150
+ as
151
+ });
152
+ }
153
+
154
+ function normalizeCompleteEdit(transform, patch) {
155
+ const base = replaceExclusive(
156
+ requestedOptions(transform),
157
+ patch,
158
+ ["values", "sequence"]
159
+ );
160
+ return normalizeCompleteTransform({ ...base, ...patch });
161
+ }
162
+
163
+ function normalizeImputeEdit(transform, patch) {
164
+ const base = { ...requestedOptions(transform) };
165
+ if (Object.hasOwn(patch, "method") && patch.method !== base.method) {
166
+ delete base.value;
167
+ if (patch.method === "constant") base.sortBy = [];
168
+ }
169
+ return normalizeImputeTransform({ ...base, ...patch });
170
+ }
171
+
172
+ function normalizeIntervalEdit(transform, patch) {
173
+ const base = { ...requestedOptions(transform) };
174
+ const extent = patch.extent ?? base.extent;
175
+ if (extent !== "ci") {
176
+ delete base.method;
177
+ delete base.level;
178
+ }
179
+ return normalizeIntervalTransform({ ...base, ...patch, extent });
180
+ }
181
+
182
+ function normalizeNormalizeEdit(transform, patch) {
183
+ const base = { ...requestedOptions(transform) };
184
+ const method = patch.method ?? base.method;
185
+ if (method !== base.method) {
186
+ if (method !== "zscore") delete base.variance;
187
+ if (method === "change") delete base.zeroDenominator;
188
+ if (!["index", "change", "percentChange"].includes(method)) {
189
+ delete base.baseline;
190
+ delete base.sortBy;
191
+ }
192
+ if (
193
+ !["index", "change", "percentChange"].includes(base.method) &&
194
+ ["index", "change", "percentChange"].includes(method)
195
+ ) {
196
+ delete base.baseline;
197
+ delete base.sortBy;
198
+ }
199
+ }
200
+ return normalizeNormalizeTransform({ ...base, ...patch, method });
201
+ }
202
+
203
+ function normalizeRegressionEdit(transform, patch) {
204
+ const base = { ...requestedOptions(transform) };
205
+ const method = patch.method ?? base.method;
206
+ if (method !== base.method) {
207
+ if (method !== "polynomial") delete base.degree;
208
+ if (method !== "loess") delete base.span;
209
+ if (method === "loess" || base.method === "loess") {
210
+ for (const key of [
211
+ "confidenceMethod", "level", "confidence", "interval"
212
+ ]) delete base[key];
213
+ }
214
+ }
215
+ return normalizeRegressionTransform({ ...base, ...patch, method });
216
+ }
217
+
218
+ function normalizeTimeUnitEdit(transform, patch) {
219
+ const base = { ...requestedOptions(transform) };
220
+ const unit = patch.unit ?? base.unit;
221
+ if (unit !== "week" && unit !== base.unit) {
222
+ delete base.weekStartsOn;
223
+ delete base.weekRule;
224
+ }
225
+ return normalizeTimeUnitTransform({ ...base, ...patch, unit });
226
+ }
227
+
228
+ function fixedRoles(as, roles) {
229
+ return Object.fromEntries(roles.flatMap(role =>
230
+ typeof as?.[role] === "string" ? [[role, as[role]]] : []
231
+ ));
232
+ }
233
+
234
+ function uniqueRoles(values, key, field = "as") {
235
+ const counts = new Map();
236
+ for (const value of values) {
237
+ const role = key(value);
238
+ counts.set(role, (counts.get(role) ?? 0) + 1);
239
+ }
240
+ return Object.fromEntries(values.flatMap(value => {
241
+ const role = key(value);
242
+ return counts.get(role) === 1 && typeof value[field] === "string"
243
+ ? [[role, value[field]]]
244
+ : [];
245
+ }));
246
+ }
247
+
248
+ function summaryOutputRoles(transform) {
249
+ return {
250
+ ...uniqueRoles(
251
+ transform.aggregates,
252
+ aggregate => `aggregate:${JSON.stringify(aggregate.op)}:${aggregate.field ?? ""}`
253
+ ),
254
+ ...(typeof transform.members === "string"
255
+ ? { members: transform.members }
256
+ : {})
257
+ };
258
+ }
259
+
260
+ function windowOutputRoles(transform) {
261
+ return uniqueRoles(
262
+ transform.operations,
263
+ operation => [
264
+ "operation",
265
+ operation.op,
266
+ operation.field ?? "",
267
+ operation.offset ?? "",
268
+ JSON.stringify(operation.frame ?? null)
269
+ ].join(":"),
270
+ "as"
271
+ );
27
272
  }
28
273
 
29
274
  function facetHorizonTransform(transform, { scales = {} } = {}) {
@@ -44,11 +289,49 @@ function facetHorizonTransform(transform, { scales = {} } = {}) {
44
289
  }
45
290
 
46
291
  const TRANSFORM_POLICIES = Object.freeze({
292
+ bin: Object.freeze({
293
+ ...findTransformTopology("bin"),
294
+ validate: validateBinTransform,
295
+ materializeOp: "materializeBinData",
296
+ replayTransform: requestedTransform,
297
+ editable: true,
298
+ normalizeEdit: normalizeBinEdit,
299
+ outputRoles: transform => fixedRoles(
300
+ transform.as,
301
+ ["lower", "upper", "count", "members"]
302
+ )
303
+ }),
47
304
  bin2d: Object.freeze({
48
305
  ...findTransformTopology("bin2d"),
49
306
  validate: validateBin2DTransform,
50
307
  materializeOp: "materializeBin2DData",
51
- replayTransform: requestedBin2DTransform
308
+ replayTransform: requestedBin2DTransform,
309
+ editable: true,
310
+ normalizeEdit: normalizeBin2DEdit,
311
+ outputRoles: transform => fixedRoles(
312
+ transform.as,
313
+ ["x0", "x1", "y0", "y1", "count", "members"]
314
+ )
315
+ }),
316
+ computed: Object.freeze({
317
+ ...findTransformTopology("computed"),
318
+ validate: validateComputedTransform,
319
+ materializeOp: "materializeComputedData",
320
+ replayTransform: requestedTransform,
321
+ editable: true,
322
+ normalizeEdit: normalizeSimple(normalizeComputedTransform),
323
+ outputRoles: transform => ({ value: transform.as })
324
+ }),
325
+ complete: Object.freeze({
326
+ ...findTransformTopology("complete"),
327
+ validate: validateCompleteTransform,
328
+ materializeOp: "materializeCompleteData",
329
+ replayTransform: requestedTransform,
330
+ editable: true,
331
+ normalizeEdit: normalizeCompleteEdit,
332
+ outputRoles: transform => typeof transform.members === "string"
333
+ ? { members: transform.members }
334
+ : {}
52
335
  }),
53
336
  boxOutlier: Object.freeze({
54
337
  ...findTransformTopology("boxOutlier"),
@@ -64,12 +347,43 @@ const TRANSFORM_POLICIES = Object.freeze({
64
347
  ...findTransformTopology("density"),
65
348
  validate: validateDensityTransform,
66
349
  materializeOp: "materializeDensityData",
67
- replayTransform: requestedDensityTransform
350
+ replayTransform: requestedDensityTransform,
351
+ editable: true,
352
+ normalizeEdit: normalizeSimple(normalizeDensityTransform),
353
+ outputRoles: transform => ({
354
+ value: transform.as[0],
355
+ density: transform.as[1]
356
+ })
357
+ }),
358
+ ecdf: Object.freeze({
359
+ ...findTransformTopology("ecdf"),
360
+ validate: validateECDFTransform,
361
+ materializeOp: "materializeECDFData",
362
+ replayTransform: requestedTransform,
363
+ editable: true,
364
+ normalizeEdit: normalizeSimple(normalizeECDFTransform),
365
+ outputRoles: transform => fixedRoles(
366
+ transform.as,
367
+ ["value", "cumulative", "probability"]
368
+ )
68
369
  }),
69
370
  filter: Object.freeze({
70
371
  ...findTransformTopology("filter"),
71
372
  validate: validateFilterTransform,
72
- materializeOp: "materializeFilteredData"
373
+ materializeOp: "materializeFilteredData",
374
+ replayTransform: requestedTransform,
375
+ editable: true,
376
+ normalizeEdit: normalizeFilterEdit,
377
+ outputRoles: () => ({})
378
+ }),
379
+ fold: Object.freeze({
380
+ ...findTransformTopology("fold"),
381
+ validate: validateFoldTransform,
382
+ materializeOp: "materializeFoldData",
383
+ replayTransform: requestedTransform,
384
+ editable: true,
385
+ normalizeEdit: normalizeSimple(normalizeFoldTransform),
386
+ outputRoles: transform => fixedRoles(transform.as, ["key", "value"])
73
387
  }),
74
388
  gradientProfile: Object.freeze({
75
389
  ...findTransformTopology("gradientProfile"),
@@ -87,27 +401,91 @@ const TRANSFORM_POLICIES = Object.freeze({
87
401
  interval: Object.freeze({
88
402
  ...findTransformTopology("interval"),
89
403
  validate: validateIntervalTransform,
90
- materializeOp: "materializeIntervalData"
404
+ materializeOp: "materializeIntervalData",
405
+ replayTransform: requestedTransform,
406
+ editable: true,
407
+ normalizeEdit: normalizeIntervalEdit,
408
+ outputRoles: transform => fixedRoles(
409
+ transform.as,
410
+ ["center", "lower", "upper"]
411
+ )
412
+ }),
413
+ impute: Object.freeze({
414
+ ...findTransformTopology("impute"),
415
+ validate: validateImputeTransform,
416
+ materializeOp: "materializeImputedData",
417
+ replayTransform: requestedTransform,
418
+ editable: true,
419
+ normalizeEdit: normalizeImputeEdit,
420
+ outputRoles: () => ({})
91
421
  }),
92
422
  markFilter: Object.freeze({
93
423
  ...findTransformTopology("markFilter"),
94
424
  validate: validateMarkFilterTransform,
95
425
  materializeOp: "materializeMarkFilteredData"
96
426
  }),
427
+ normalize: Object.freeze({
428
+ ...findTransformTopology("normalize"),
429
+ validate: validateNormalizeTransform,
430
+ materializeOp: "materializeNormalizedData",
431
+ replayTransform: requestedTransform,
432
+ editable: true,
433
+ normalizeEdit: normalizeNormalizeEdit,
434
+ outputRoles: transform => ({ value: transform.as })
435
+ }),
97
436
  regression: Object.freeze({
98
437
  ...findTransformTopology("regression"),
99
438
  validate: validateRegressionTransform,
100
- materializeOp: "materializeRegressionData"
439
+ materializeOp: "materializeRegressionData",
440
+ replayTransform: requestedTransform,
441
+ editable: true,
442
+ normalizeEdit: normalizeRegressionEdit,
443
+ outputRoles: () => ({})
444
+ }),
445
+ summary: Object.freeze({
446
+ ...findTransformTopology("summary"),
447
+ validate: validateSummaryTransform,
448
+ materializeOp: "materializeSummaryData",
449
+ replayTransform: requestedTransform,
450
+ editable: true,
451
+ normalizeEdit: normalizeSimple(normalizeSummaryTransform),
452
+ outputRoles: summaryOutputRoles
453
+ }),
454
+ stack: Object.freeze({
455
+ ...findTransformTopology("stack"),
456
+ validate: validateStackTransform,
457
+ materializeOp: "materializeStackData",
458
+ replayTransform: requestedTransform,
459
+ editable: true,
460
+ normalizeEdit: normalizeSimple(normalizeStackTransform),
461
+ outputRoles: transform => fixedRoles(
462
+ transform.as,
463
+ ["start", "end", "value", "share"]
464
+ )
465
+ }),
466
+ statisticalReference: Object.freeze({
467
+ ...findTransformTopology("statisticalReference"),
468
+ validate: validateStatisticalReferenceTransform,
469
+ materializeOp: "materializeStatisticalReferenceData",
470
+ replayTransform: requestedTransform
101
471
  }),
102
472
  timeUnit: Object.freeze({
103
473
  ...findTransformTopology("timeUnit"),
104
474
  validate: validateTimeUnitTransform,
105
- materializeOp: "materializeTimeUnitData"
475
+ materializeOp: "materializeTimeUnitData",
476
+ replayTransform: requestedTransform,
477
+ editable: true,
478
+ normalizeEdit: normalizeTimeUnitEdit,
479
+ outputRoles: transform => ({ value: transform.as })
106
480
  }),
107
481
  window: Object.freeze({
108
482
  ...findTransformTopology("window"),
109
483
  validate: validateWindowTransform,
110
- materializeOp: "materializeWindowData"
484
+ materializeOp: "materializeWindowData",
485
+ replayTransform: requestedTransform,
486
+ editable: true,
487
+ normalizeEdit: normalizeSimple(normalizeWindowTransform),
488
+ outputRoles: windowOutputRoles
111
489
  })
112
490
  });
113
491
 
@@ -119,6 +497,30 @@ export function findTransformValidator(type) {
119
497
  return findTransformPolicy(type)?.validate;
120
498
  }
121
499
 
500
+ export function requestedDatasetTransform(transform) {
501
+ if (!isPlainObject(transform)) {
502
+ throw new TypeError("Requested dataset transform must be a plain object.");
503
+ }
504
+ const policy = findTransformPolicy(transform.type);
505
+ if (policy?.editable !== true) {
506
+ throw new Error(`Dataset transform "${transform.type ?? "unknown"}" is not editable.`);
507
+ }
508
+ return policy.replayTransform?.(transform) ?? requestedTransform(transform);
509
+ }
510
+
511
+ export function normalizeDatasetTransformEdit(transform, patch, owner) {
512
+ const policy = findTransformPolicy(transform?.type);
513
+ if (policy?.editable !== true || typeof policy.normalizeEdit !== "function") {
514
+ throw new Error(`Dataset transform "${transform?.type ?? "unknown"}" is not editable.`);
515
+ }
516
+ return policy.normalizeEdit(transform, patch, owner);
517
+ }
518
+
519
+ export function datasetTransformOutputRoles(transform) {
520
+ const policy = findTransformPolicy(transform?.type);
521
+ return policy?.outputRoles?.(transform) ?? {};
522
+ }
523
+
122
524
  export function validateDatasetTransforms(value) {
123
525
  if (!Array.isArray(value) || value.length !== 1 || !isPlainObject(value[0])) {
124
526
  throw new TypeError(
@@ -0,0 +1,112 @@
1
+ const NUMERIC_FORMAT = /^\.(0?\d|1[0-2])(f|%|e)$/;
2
+ const TIME_DIRECTIVES = new Set(["Y", "m", "d", "b", "%"]);
3
+ const MONTH_NAMES = Object.freeze([
4
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
5
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
6
+ ]);
7
+
8
+ export function isUtcValueFormat(format) {
9
+ if (typeof format !== "string") return false;
10
+ let directives = 0;
11
+ for (let index = 0; index < format.length; index += 1) {
12
+ if (format[index] !== "%") continue;
13
+ const directive = format[index + 1];
14
+ if (!TIME_DIRECTIVES.has(directive)) return false;
15
+ if (directive !== "%") directives += 1;
16
+ index += 1;
17
+ }
18
+ return directives > 0;
19
+ }
20
+
21
+ export function validateValueFormat(format, label = "Label format", {
22
+ allowDecimalsObject = false
23
+ } = {}) {
24
+ if (format === undefined || format === "auto") return format ?? "auto";
25
+ if (
26
+ format !== null &&
27
+ typeof format === "object" &&
28
+ Number.isInteger(format.decimals) &&
29
+ format.decimals >= 0 &&
30
+ format.decimals <= 100 &&
31
+ Object.keys(format).length === 1
32
+ ) {
33
+ if (allowDecimalsObject) return format;
34
+ }
35
+ if (typeof format === "string" && (
36
+ NUMERIC_FORMAT.test(format) || isUtcValueFormat(format)
37
+ )) {
38
+ return format;
39
+ }
40
+ const numericCandidate = typeof format === "string"
41
+ ? format.match(/^\.(\d+)(f|%|e)$/)
42
+ : undefined;
43
+ if (numericCandidate && Number(numericCandidate[1]) > 12) {
44
+ throw new RangeError(`${label} numeric format supports at most 12 decimals.`);
45
+ }
46
+ throw new TypeError(
47
+ `${label} must be auto${allowDecimalsObject ? ", { decimals }," : ","} or a supported format string (numeric or UTC).`
48
+ );
49
+ }
50
+
51
+ function pad(value, length = 2) {
52
+ return String(value).padStart(length, "0");
53
+ }
54
+
55
+ function formatUtcValue(value, format, label) {
56
+ const date = new Date(value);
57
+ if (!Number.isFinite(date.getTime())) {
58
+ throw new TypeError(`${label} requires a valid date or timestamp.`);
59
+ }
60
+ const values = {
61
+ Y: pad(date.getUTCFullYear(), 4),
62
+ m: pad(date.getUTCMonth() + 1),
63
+ d: pad(date.getUTCDate()),
64
+ b: MONTH_NAMES[date.getUTCMonth()]
65
+ };
66
+ return format.replace(/%([Ymdb%])/g, (_, directive) =>
67
+ directive === "%" ? "%" : values[directive]
68
+ );
69
+ }
70
+
71
+ export function formatValue(value, {
72
+ format = "auto",
73
+ valueType,
74
+ autoFormatter = current => String(current),
75
+ label = "Label format",
76
+ allowDecimalsObject = false
77
+ } = {}) {
78
+ const resolved = validateValueFormat(format, label, {
79
+ allowDecimalsObject
80
+ });
81
+ if (value === undefined || value === null) return undefined;
82
+ if (resolved === "auto") return autoFormatter(value);
83
+ if (["nominal", "ordinal", "discrete"].includes(valueType)) {
84
+ throw new Error('Discrete labels require format "auto".');
85
+ }
86
+ const utc = isUtcValueFormat(resolved);
87
+ if (valueType === "temporal") {
88
+ if (!utc) throw new Error("Temporal labels require a supported UTC format string.");
89
+ return formatUtcValue(value, resolved, label);
90
+ }
91
+ if (utc) {
92
+ if (valueType === "quantitative") {
93
+ throw new Error("Quantitative labels cannot use a UTC format string.");
94
+ }
95
+ return formatUtcValue(value, resolved, label);
96
+ }
97
+ if (!Number.isFinite(value)) {
98
+ throw new TypeError(`${label} "${typeof resolved === "string" ? resolved : "decimals"}" requires a finite number.`);
99
+ }
100
+ if (typeof resolved === "object") return value.toFixed(resolved.decimals);
101
+ const match = resolved.match(NUMERIC_FORMAT);
102
+ const precision = Number(match[1]);
103
+ if (match[2] === "f") return value.toFixed(precision);
104
+ if (match[2] === "%") {
105
+ const percentage = value * 100;
106
+ if (!Number.isFinite(percentage)) {
107
+ throw new RangeError(`${label} percent format overflow.`);
108
+ }
109
+ return `${percentage.toFixed(precision)}%`;
110
+ }
111
+ return value.toExponential(precision);
112
+ }