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
@@ -0,0 +1,658 @@
1
+ import { isSourceOwnedText } from "../../grammar/text.js";
2
+ import { action } from "../../core/action.js";
3
+ import { isPlainObject } from "../../core/immutable.js";
4
+ import { validateUserId } from "../../core/identifiers.js";
5
+ import { validateKeys } from "../../core/validation.js";
6
+ import { findCoordinate } from "../../selectors/coordinates.js";
7
+ import {
8
+ findDataset,
9
+ hasDataset,
10
+ hasDatasetOwner
11
+ } from "../../selectors/datasets.js";
12
+ import { findLayer, resolveEligibleLayer } from "../../selectors/layers.js";
13
+ import { resolveConsumerValuesForDataset } from
14
+ "../scales/consumers/index.js";
15
+ import { resolveMarkItems } from
16
+ "../../materialization/selection/policies/index.js";
17
+ import { getMarkMaterializationPolicy } from
18
+ "../../materialization/marks/policies.js";
19
+ import { requireDerivedDataset } from "../data/shared.js";
20
+ import { withRematerializedDerivedDataset } from
21
+ "../primitives/semanticAction.js";
22
+ import {
23
+ deriveStatisticalReferenceValues,
24
+ normalizeReferenceField,
25
+ normalizeReferencePopulation,
26
+ normalizeReferenceStatistics,
27
+ normalizeStatisticalReferenceTransform,
28
+ validateStatisticalReferenceTransform
29
+ } from "../../grammar/statisticalReference.js";
30
+ import {
31
+ resolveFacadeData,
32
+ resolveFacadeId,
33
+ validateFacadeOptions
34
+ } from "../charts/shared.js";
35
+ import { RECT_STYLE_PROPERTIES } from "../../grammar/roundedRect.js";
36
+ import { STROKE_STYLE_PROPERTIES } from "../../grammar/strokeStyle.js";
37
+
38
+ const COMMON_OPTIONS = Object.freeze([
39
+ "id", "x", "y", "space", "source", "data", "coordinate", "temporalUnit"
40
+ ]);
41
+ const DYNAMIC_OPTIONS = Object.freeze([
42
+ "id", "source", "axis", "population", "field", "statistic", "statistics"
43
+ ]);
44
+ const LINE_STYLE = Object.freeze([
45
+ "stroke", "strokeWidth", "strokeDash", "opacity", ...STROKE_STYLE_PROPERTIES
46
+ ]);
47
+ const BAND_STYLE = Object.freeze([
48
+ "fill", "opacity", "stroke", "strokeWidth", ...RECT_STYLE_PROPERTIES
49
+ ]);
50
+ const MATERIALIZE_OPTIONS = Object.freeze(["id"]);
51
+ const SERIES_MARKS = new Set(["line", "area"]);
52
+
53
+ function resolveLiteralBinding(program, args, axis, operation, band) {
54
+ const space = args.space === undefined ? "data" : args.space;
55
+ if (space !== "data" && space !== "plot") {
56
+ throw new Error(`${operation} space must be data or plot.`);
57
+ }
58
+ if (space === "plot") {
59
+ if (Object.hasOwn(args, "source") || Object.hasOwn(args, "temporalUnit")) {
60
+ throw new Error(`${operation} plot space does not accept source or temporalUnit.`);
61
+ }
62
+ return {
63
+ data: resolveFacadeData(program, args.data, operation),
64
+ fieldType: "quantitative",
65
+ coordinate: args.coordinate,
66
+ plot: true
67
+ };
68
+ }
69
+ if (Object.hasOwn(args, "data") || Object.hasOwn(args, "coordinate")) {
70
+ throw new Error(`${operation} data space uses source data and coordinate.`);
71
+ }
72
+ const source = resolveEligibleLayer(program, {
73
+ target: args.source === undefined
74
+ ? undefined
75
+ : validateUserId(args.source, `${operation} source`),
76
+ predicate: layer =>
77
+ !isSourceOwnedText(layer) &&
78
+ layer.data !== undefined &&
79
+ layer.encoding?.[axis]?.scale !== undefined &&
80
+ findCoordinate(program, layer.coordinate)?.type === "cartesian" &&
81
+ (!band || ["quantitative", "temporal"].includes(
82
+ layer.encoding[axis].fieldType
83
+ )),
84
+ label: `${operation} Cartesian layer`,
85
+ targetOption: "source"
86
+ });
87
+ const encoding = source.encoding[axis];
88
+ return {
89
+ data: source.data,
90
+ coordinate: source.coordinate,
91
+ scale: encoding.scale,
92
+ fieldType: encoding.fieldType,
93
+ temporalUnit: args.temporalUnit === undefined
94
+ ? encoding.temporalUnit
95
+ : args.temporalUnit
96
+ };
97
+ }
98
+
99
+ function createLiteralReference(program, args, band) {
100
+ const operation = band ? "createReferenceBand" : "createReferenceLine";
101
+ const styleOptions = band ? BAND_STYLE : LINE_STYLE;
102
+ validateFacadeOptions(args, [...COMMON_OPTIONS, ...styleOptions], operation);
103
+ const axes = ["x", "y"].filter(axis => Object.hasOwn(args, axis));
104
+ if (axes.length !== 1) {
105
+ throw new Error(`${operation} requires exactly one of x or y.`);
106
+ }
107
+ const axis = axes[0];
108
+ const values = band ? args[axis] : [args[axis]];
109
+ if (!Array.isArray(values) || values.length !== (band ? 2 : 1)) {
110
+ throw new Error(`${operation} ${axis} requires a two-value interval.`);
111
+ }
112
+ const id = resolveFacadeId(program, args.id, {
113
+ defaultId: band ? "referenceBand" : "referenceLine",
114
+ operation
115
+ });
116
+ const binding = resolveLiteralBinding(program, args, axis, operation, band);
117
+ if (
118
+ binding.plot &&
119
+ !values.every(value =>
120
+ Number.isFinite(value) && value >= 0 && value <= 1
121
+ )
122
+ ) {
123
+ throw new Error(
124
+ `${operation} plot coordinates must be finite numbers in [0, 1].`
125
+ );
126
+ }
127
+ const scale = binding.plot ? `${id}-${axis}` : binding.scale;
128
+ const style = Object.fromEntries(
129
+ styleOptions
130
+ .filter(key => args[key] !== undefined)
131
+ .map(key => [key, args[key]])
132
+ );
133
+ const encode = axis === "x" ? "encodeX" : "encodeY";
134
+ const options = {
135
+ target: id,
136
+ fieldType: binding.fieldType,
137
+ scale: { id: scale },
138
+ ...(binding.coordinate === undefined
139
+ ? {}
140
+ : { coordinate: binding.coordinate }),
141
+ ...(binding.temporalUnit === undefined
142
+ ? {}
143
+ : { temporalUnit: binding.temporalUnit })
144
+ };
145
+ const apply = initial => {
146
+ let next = binding.plot
147
+ ? initial.createScale({ id: scale, type: "linear", domain: [0, 1] })
148
+ : initial;
149
+ next = band
150
+ ? next.createRectMark({
151
+ id,
152
+ data: binding.data,
153
+ fill: "#94a3b8",
154
+ opacity: 0.15,
155
+ stroke: false,
156
+ ...style
157
+ })
158
+ : next.createRuleMark({
159
+ id,
160
+ data: binding.data,
161
+ stroke: "#64748b",
162
+ strokeWidth: 1,
163
+ strokeDash: "dashed",
164
+ ...style
165
+ });
166
+ next = next[encode]({ ...options, datum: values[0] });
167
+ if (band) next = next[`${encode}2`]({ ...options, datum: values[1] });
168
+ return next._withMarkConfig(id, {
169
+ ...next.markConfigs[id],
170
+ referenceAuthoring: { kind: "literal" }
171
+ });
172
+ };
173
+ // Validate the complete lower-action chain on a discarded immutable branch.
174
+ apply(program);
175
+ return apply(program);
176
+ }
177
+
178
+ function requireStatisticalSource(program, config, referenceId) {
179
+ if (!isPlainObject(config)) {
180
+ throw new TypeError(
181
+ `Statistical reference "${referenceId}" requires materialization config.`
182
+ );
183
+ }
184
+ const sourceId = validateUserId(
185
+ config.source,
186
+ `Statistical reference "${referenceId}" source`
187
+ );
188
+ const source = findLayer(program, sourceId);
189
+ if (source === undefined) {
190
+ throw new Error(
191
+ `Statistical reference "${referenceId}" source "${sourceId}" does not exist.`
192
+ );
193
+ }
194
+ if (
195
+ sourceId === referenceId ||
196
+ program.markConfigs[sourceId]?.referenceAuthoring !== undefined ||
197
+ program.markConfigs[sourceId]?.statisticalReference !== undefined
198
+ ) {
199
+ throw new Error(
200
+ `Statistical reference "${referenceId}" source must be a non-reference mark.`
201
+ );
202
+ }
203
+ if (findCoordinate(program, source.coordinate)?.type !== "cartesian") {
204
+ throw new Error(
205
+ `Statistical reference "${referenceId}" source must use Cartesian coordinates.`
206
+ );
207
+ }
208
+ if (!["x", "y"].includes(config.axis)) {
209
+ throw new Error(
210
+ `Statistical reference "${referenceId}" axis must be x or y.`
211
+ );
212
+ }
213
+ const encoding = source.encoding?.[config.axis];
214
+ if (
215
+ encoding?.fieldType !== "quantitative" ||
216
+ typeof encoding.scale !== "string" ||
217
+ encoding.scale.length === 0
218
+ ) {
219
+ throw new Error(
220
+ `Statistical reference "${referenceId}" source axis must have a quantitative scaled encoding.`
221
+ );
222
+ }
223
+ if (
224
+ !isPlainObject(config.field) ||
225
+ !["axis", "explicit"].includes(config.field.kind) ||
226
+ (config.field.kind === "explicit" &&
227
+ (typeof config.field.field !== "string" || config.field.field.length === 0))
228
+ ) {
229
+ throw new Error(
230
+ `Statistical reference "${referenceId}" has an invalid field binding.`
231
+ );
232
+ }
233
+ return { source, encoding };
234
+ }
235
+
236
+ function resolvePopulationDataset(program, source, population, referenceId) {
237
+ let dataset = findDataset(program, source.data);
238
+ if (dataset === undefined || !Array.isArray(dataset.values)) {
239
+ throw new Error(
240
+ `Statistical reference "${referenceId}" source requires materialized data.`
241
+ );
242
+ }
243
+ if (population === "visibleItems") return dataset;
244
+ while (
245
+ dataset.transform?.length === 1 &&
246
+ dataset.transform[0].type === "markFilter" &&
247
+ dataset.transform[0].target === source.id
248
+ ) {
249
+ const parent = findDataset(program, dataset.source);
250
+ if (parent === undefined || !Array.isArray(parent.values)) {
251
+ throw new Error(
252
+ `Statistical reference "${referenceId}" bound data is unavailable.`
253
+ );
254
+ }
255
+ dataset = parent;
256
+ }
257
+ return dataset;
258
+ }
259
+
260
+ function strictFieldValues(rows, field, label) {
261
+ return rows.map((row, index) => {
262
+ if (!isPlainObject(row) || !Object.hasOwn(row, field)) {
263
+ throw new Error(`${label} field "${field}" is missing at item ${index}.`);
264
+ }
265
+ const value = row[field];
266
+ if (!Number.isFinite(value)) {
267
+ throw new TypeError(
268
+ `${label} field "${field}" must contain finite numbers; item ${index} is invalid.`
269
+ );
270
+ }
271
+ return value;
272
+ });
273
+ }
274
+
275
+ function visibleExplicitValues(program, source, dataset, field, referenceId) {
276
+ if (SERIES_MARKS.has(source.mark?.type) || source.encoding?.parallel !== undefined) {
277
+ throw new Error(
278
+ `Statistical reference "${referenceId}" visibleItems population is ambiguous for series marks.`
279
+ );
280
+ }
281
+ let resolvedProgram = program;
282
+ if (source.data !== dataset.id) {
283
+ const policy = getMarkMaterializationPolicy(source);
284
+ if (typeof policy?.op !== "string") {
285
+ throw new Error(
286
+ `Statistical reference "${referenceId}" cannot resolve visible items for its source mark.`
287
+ );
288
+ }
289
+ resolvedProgram = program
290
+ .editSemantic({
291
+ property: `layer[${source.id}].data`,
292
+ value: dataset.id
293
+ })
294
+ [policy.op]({ id: source.id });
295
+ }
296
+ const items = resolveMarkItems(resolvedProgram, source.id);
297
+ return items.map((item, index) => {
298
+ if (!Object.hasOwn(item.fields, field)) {
299
+ throw new Error(
300
+ `Statistical reference "${referenceId}" field "${field}" is not scalar at visible item ${index}.`
301
+ );
302
+ }
303
+ const value = item.fields[field];
304
+ if (!Number.isFinite(value)) {
305
+ throw new TypeError(
306
+ `Statistical reference "${referenceId}" field "${field}" must be finite at visible item ${index}.`
307
+ );
308
+ }
309
+ return value;
310
+ });
311
+ }
312
+
313
+ function resolvePopulationValues(
314
+ program,
315
+ source,
316
+ encoding,
317
+ config,
318
+ dataset,
319
+ referenceId
320
+ ) {
321
+ if (
322
+ config.population === "visibleItems" &&
323
+ (SERIES_MARKS.has(source.mark?.type) || source.encoding?.parallel !== undefined)
324
+ ) {
325
+ throw new Error(
326
+ `Statistical reference "${referenceId}" visibleItems population is ambiguous for series marks.`
327
+ );
328
+ }
329
+ if (config.field.kind === "explicit") {
330
+ return config.population === "visibleItems"
331
+ ? visibleExplicitValues(
332
+ program,
333
+ source,
334
+ dataset,
335
+ config.field.field,
336
+ referenceId
337
+ )
338
+ : strictFieldValues(
339
+ dataset.values,
340
+ config.field.field,
341
+ `Statistical reference "${referenceId}" boundData`
342
+ );
343
+ }
344
+ return resolveConsumerValuesForDataset(program, {
345
+ layer: source,
346
+ channel: config.axis,
347
+ encoding
348
+ }, dataset);
349
+ }
350
+
351
+ function statisticalRows(program, referenceId, dataset) {
352
+ const config = program.markConfigs[referenceId]?.statisticalReference;
353
+ const { source, encoding } = requireStatisticalSource(
354
+ program,
355
+ config,
356
+ referenceId
357
+ );
358
+ if (!Array.isArray(config.statistics) || ![1, 2].includes(config.statistics.length)) {
359
+ throw new Error(
360
+ `Statistical reference "${referenceId}" requires one or two statistics.`
361
+ );
362
+ }
363
+ const statistics = normalizeReferenceStatistics(
364
+ config.statistics,
365
+ config.statistics.length,
366
+ `Statistical reference "${referenceId}"`
367
+ );
368
+ const population = normalizeReferencePopulation(
369
+ config.population,
370
+ `Statistical reference "${referenceId}" population`
371
+ );
372
+ const values = resolvePopulationValues(
373
+ program,
374
+ source,
375
+ encoding,
376
+ { ...config, population, statistics },
377
+ dataset,
378
+ referenceId
379
+ );
380
+ const resolved = deriveStatisticalReferenceValues(
381
+ values,
382
+ statistics,
383
+ `Statistical reference "${referenceId}"`
384
+ );
385
+ if (resolved.length === 2 && resolved[0] > resolved[1]) {
386
+ throw new Error(
387
+ `Statistical reference band "${referenceId}" lower statistic exceeds its upper statistic.`
388
+ );
389
+ }
390
+ return resolved.length === 1
391
+ ? [{ value: resolved[0] }]
392
+ : [{ lower: resolved[0], upper: resolved[1] }];
393
+ }
394
+
395
+ function replaceStatisticalDataset(program, id, source, transform, values) {
396
+ const dataset = findDataset(program, id);
397
+ if (dataset === undefined || dataset.source === undefined) {
398
+ throw new Error(`Unknown statistical reference dataset "${id}".`);
399
+ }
400
+ validateStatisticalReferenceTransform(transform);
401
+ return withRematerializedDerivedDataset(program, {
402
+ id,
403
+ source,
404
+ transform,
405
+ values
406
+ });
407
+ }
408
+
409
+ function syncReferenceEncoding(program, id, source, config) {
410
+ const layer = findLayer(program, id);
411
+ if (layer === undefined) {
412
+ throw new Error(`Unknown statistical reference mark "${id}".`);
413
+ }
414
+ const axis = config.axis;
415
+ const secondary = `${axis}2`;
416
+ const retained = new Set(
417
+ config.statistics.length === 1 ? [axis] : [axis, secondary]
418
+ );
419
+ let next = program;
420
+ for (const channel of ["x", "y", "x2", "y2"]) {
421
+ if (!retained.has(channel) && layer.encoding?.[channel] !== undefined) {
422
+ next = next.editSemantic({
423
+ property: `layer[${id}].encoding.${channel}`,
424
+ remove: true
425
+ });
426
+ }
427
+ }
428
+ const scale = source.encoding[axis].scale;
429
+ const fields = config.statistics.length === 1
430
+ ? [[axis, "value"]]
431
+ : [[axis, "lower"], [secondary, "upper"]];
432
+ for (const [channel, field] of fields) {
433
+ const current = findLayer(next, id).encoding?.[channel];
434
+ if (current?.datum !== undefined) {
435
+ next = next.editSemantic({
436
+ property: `layer[${id}].encoding.${channel}.datum`,
437
+ remove: true
438
+ });
439
+ }
440
+ for (const [property, value] of Object.entries({
441
+ field,
442
+ fieldType: "quantitative",
443
+ scale
444
+ })) {
445
+ if (findLayer(next, id).encoding?.[channel]?.[property] !== value) {
446
+ next = next.editSemantic({
447
+ property: `layer[${id}].encoding.${channel}.${property}`,
448
+ value
449
+ });
450
+ }
451
+ }
452
+ }
453
+ const currentCoordinate = findLayer(next, id).coordinate;
454
+ if (source.coordinate === undefined && currentCoordinate !== undefined) {
455
+ next = next.editSemantic({
456
+ property: `layer[${id}].coordinate`,
457
+ remove: true
458
+ });
459
+ } else if (source.coordinate !== undefined && source.coordinate !== currentCoordinate) {
460
+ next = next.editSemantic({
461
+ property: `layer[${id}].coordinate`,
462
+ value: source.coordinate
463
+ });
464
+ }
465
+ return next;
466
+ }
467
+
468
+ export const materializeStatisticalReferenceData = action(
469
+ {
470
+ op: "materializeStatisticalReferenceData",
471
+ description: "Materialize one owned statistical reference value dataset."
472
+ },
473
+ function (args = {}) {
474
+ validateKeys(args, MATERIALIZE_OPTIONS, "materializeStatisticalReferenceData");
475
+ const { id, source, transform } = requireDerivedDataset(
476
+ this,
477
+ args.id,
478
+ "statisticalReference"
479
+ );
480
+ const values = statisticalRows(this, transform.target, source);
481
+ return this.editSemantic({
482
+ property: `dataset[${id}].values`,
483
+ value: values
484
+ });
485
+ }
486
+ );
487
+
488
+ export const rematerializeStatisticalReference = action(
489
+ {
490
+ op: "rematerializeStatisticalReference",
491
+ description: "Recompute one source-dependent statistical reference."
492
+ },
493
+ function (args = {}) {
494
+ validateKeys(args, MATERIALIZE_OPTIONS, "rematerializeStatisticalReference");
495
+ const id = validateUserId(args.id, "Statistical reference mark id");
496
+ const config = this.markConfigs[id]?.statisticalReference;
497
+ const { source } = requireStatisticalSource(this, config, id);
498
+ const population = normalizeReferencePopulation(config.population);
499
+ const dataset = resolvePopulationDataset(this, source, population, id);
500
+ const values = statisticalRows(this, id, dataset);
501
+ const transform = normalizeStatisticalReferenceTransform({ target: id });
502
+ let next = replaceStatisticalDataset(
503
+ this,
504
+ config.dataId,
505
+ dataset.id,
506
+ transform,
507
+ values
508
+ );
509
+ if (findLayer(next, id)?.data !== config.dataId) {
510
+ next = next.editSemantic({
511
+ property: `layer[${id}].data`,
512
+ value: config.dataId
513
+ });
514
+ }
515
+ next = syncReferenceEncoding(next, id, source, config);
516
+ next = config.statistics.length === 1
517
+ ? next.rematerializeRuleMark({ id })
518
+ : next.rematerializeRectMark({ id });
519
+ return next._withContext(this.context);
520
+ }
521
+ );
522
+
523
+ function isDynamicReference(args) {
524
+ return ["axis", "population", "field", "statistic", "statistics"]
525
+ .some(key => Object.hasOwn(args, key));
526
+ }
527
+
528
+ function normalizeDynamicReference(program, args, band) {
529
+ const operation = band ? "createReferenceBand" : "createReferenceLine";
530
+ const styleOptions = band ? BAND_STYLE : LINE_STYLE;
531
+ validateFacadeOptions(
532
+ args,
533
+ [...DYNAMIC_OPTIONS, ...styleOptions],
534
+ operation
535
+ );
536
+ if (typeof args.source !== "string") {
537
+ throw new TypeError(`${operation} statistical source must be a mark id.`);
538
+ }
539
+ const sourceId = validateUserId(args.source, `${operation} source`);
540
+ if (!["x", "y"].includes(args.axis)) {
541
+ throw new Error(`${operation} statistical axis must be x or y.`);
542
+ }
543
+ if (band && Object.hasOwn(args, "statistic")) {
544
+ throw new Error(`${operation} uses statistics, not statistic.`);
545
+ }
546
+ if (!band && Object.hasOwn(args, "statistics")) {
547
+ throw new Error(`${operation} uses statistic, not statistics.`);
548
+ }
549
+ const statistics = normalizeReferenceStatistics(
550
+ band ? args.statistics : [args.statistic],
551
+ band ? 2 : 1,
552
+ operation
553
+ );
554
+ const id = resolveFacadeId(program, args.id, {
555
+ defaultId: band ? "referenceBand" : "referenceLine",
556
+ operation
557
+ });
558
+ const config = {
559
+ source: sourceId,
560
+ axis: args.axis,
561
+ population: normalizeReferencePopulation(args.population, `${operation} population`),
562
+ field: normalizeReferenceField(args.field, `${operation} field`),
563
+ statistics,
564
+ dataId: `${id}-statistical-reference-data`
565
+ };
566
+ const preview = program._withMarkConfig(id, { statisticalReference: config });
567
+ const { source } = requireStatisticalSource(preview, config, id);
568
+ const dataset = resolvePopulationDataset(
569
+ program,
570
+ source,
571
+ config.population,
572
+ id
573
+ );
574
+ statisticalRows(preview, id, dataset);
575
+ if (hasDataset(program, config.dataId) || hasDatasetOwner(program, config.dataId)) {
576
+ throw new Error(`Dataset "${config.dataId}" already exists.`);
577
+ }
578
+ return { id, config, dataset, styleOptions };
579
+ }
580
+
581
+ function createDynamicReference(program, args, band) {
582
+ const { id, config, dataset, styleOptions } = normalizeDynamicReference(
583
+ program,
584
+ args,
585
+ band
586
+ );
587
+ const style = Object.fromEntries(
588
+ styleOptions
589
+ .filter(key => args[key] !== undefined)
590
+ .map(key => [key, args[key]])
591
+ );
592
+ const transform = normalizeStatisticalReferenceTransform({ target: id });
593
+ const apply = initial => {
594
+ const originalContext = initial.context;
595
+ let next = initial
596
+ .createDerivedData({
597
+ id: config.dataId,
598
+ source: dataset.id,
599
+ transform: [transform]
600
+ })
601
+ ._withMarkConfig(id, { statisticalReference: config })
602
+ .materializeStatisticalReferenceData({ id: config.dataId });
603
+ next = band
604
+ ? next.createRectMark({
605
+ id,
606
+ data: config.dataId,
607
+ fill: "#94a3b8",
608
+ opacity: 0.15,
609
+ stroke: false,
610
+ ...style
611
+ })
612
+ : next.createRuleMark({
613
+ id,
614
+ data: config.dataId,
615
+ stroke: "#64748b",
616
+ strokeWidth: 1,
617
+ strokeDash: "dashed",
618
+ ...style
619
+ });
620
+ next = next._withMarkConfig(id, {
621
+ ...next.markConfigs[id],
622
+ itemFilterable: false,
623
+ statisticalReference: config
624
+ });
625
+ return next
626
+ .rematerializeStatisticalReference({ id })
627
+ ._withContext({ ...originalContext, currentMark: id });
628
+ };
629
+ // Validate every owned data, semantic, scale, and renderer step atomically.
630
+ apply(program);
631
+ return apply(program);
632
+ }
633
+
634
+ function createReference(program, args, band) {
635
+ return isDynamicReference(args)
636
+ ? createDynamicReference(program, args, band)
637
+ : createLiteralReference(program, args, band);
638
+ }
639
+
640
+ export const createReferenceLine = action(
641
+ {
642
+ op: "createReferenceLine",
643
+ description: "Create a constant or statistical reference line."
644
+ },
645
+ function (args = {}) {
646
+ return createReference(this, args, false);
647
+ }
648
+ );
649
+
650
+ export const createReferenceBand = action(
651
+ {
652
+ op: "createReferenceBand",
653
+ description: "Create a constant or statistical reference interval."
654
+ },
655
+ function (args = {}) {
656
+ return createReference(this, args, true);
657
+ }
658
+ );