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,101 @@
1
+ import { cloneAndFreeze } from "../../core/immutable.js";
2
+ import { studentTCriticalValue } from "./studentT.js";
3
+
4
+ export const CONFIDENCE_INTERVAL_METHODS = Object.freeze([
5
+ "normal",
6
+ "student-t"
7
+ ]);
8
+
9
+ function validateMethod(method, label) {
10
+ if (!CONFIDENCE_INTERVAL_METHODS.includes(method)) {
11
+ throw new Error(`Unsupported ${label} method "${method}".`);
12
+ }
13
+ }
14
+
15
+ function validateLevel(level, label) {
16
+ if (!Number.isFinite(level) || level <= 0 || level >= 1) {
17
+ throw new RangeError(`${label} level must be between 0 and 1.`);
18
+ }
19
+ }
20
+
21
+ // Peter J. Acklam's inverse-normal approximation. The exact 95% compatibility
22
+ // value is handled by normalCriticalValue before this approximation is used.
23
+ function inverseNormal(probability) {
24
+ const a = [
25
+ -3.969683028665376e1, 2.209460984245205e2,
26
+ -2.759285104469687e2, 1.38357751867269e2,
27
+ -3.066479806614716e1, 2.506628277459239
28
+ ];
29
+ const b = [
30
+ -5.447609879822406e1, 1.615858368580409e2,
31
+ -1.556989798598866e2, 6.680131188771972e1,
32
+ -1.328068155288572e1
33
+ ];
34
+ const c = [
35
+ -7.784894002430293e-3, -3.223964580411365e-1,
36
+ -2.400758277161838, -2.549732539343734,
37
+ 4.374664141464968, 2.938163982698783
38
+ ];
39
+ const d = [
40
+ 7.784695709041462e-3, 3.224671290700398e-1,
41
+ 2.445134137142996, 3.754408661907416
42
+ ];
43
+ const low = 0.02425;
44
+ const high = 1 - low;
45
+ if (probability < low) {
46
+ const q = Math.sqrt(-2 * Math.log(probability));
47
+ return (((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
48
+ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
49
+ }
50
+ if (probability > high) {
51
+ const q = Math.sqrt(-2 * Math.log(1 - probability));
52
+ return -(((((c[0] * q + c[1]) * q + c[2]) * q + c[3]) * q + c[4]) * q + c[5]) /
53
+ ((((d[0] * q + d[1]) * q + d[2]) * q + d[3]) * q + 1);
54
+ }
55
+ const q = probability - 0.5;
56
+ const r = q * q;
57
+ return (((((a[0] * r + a[1]) * r + a[2]) * r + a[3]) * r + a[4]) * r + a[5]) * q /
58
+ (((((b[0] * r + b[1]) * r + b[2]) * r + b[3]) * r + b[4]) * r + 1);
59
+ }
60
+
61
+ export function normalizeConfidenceInterval({
62
+ method,
63
+ level
64
+ } = {}, {
65
+ defaultMethod,
66
+ defaultLevel = 0.95,
67
+ label = "Confidence interval"
68
+ } = {}) {
69
+ validateMethod(defaultMethod, label);
70
+ const resolvedMethod = method ?? defaultMethod;
71
+ const resolvedLevel = level ?? defaultLevel;
72
+ validateMethod(resolvedMethod, label);
73
+ validateLevel(resolvedLevel, label);
74
+ return cloneAndFreeze({ method: resolvedMethod, level: resolvedLevel });
75
+ }
76
+
77
+ export function normalCriticalValue(level) {
78
+ validateLevel(level, "Normal confidence interval");
79
+ if (level === 0.95) return 1.96;
80
+ return inverseNormal((1 + level) / 2);
81
+ }
82
+
83
+ export function confidenceCriticalValue({
84
+ method,
85
+ level,
86
+ degreesOfFreedom
87
+ }) {
88
+ const normalized = normalizeConfidenceInterval(
89
+ { method, level },
90
+ { defaultMethod: method, label: "Confidence interval" }
91
+ );
92
+ if (normalized.method === "normal") {
93
+ return normalCriticalValue(normalized.level);
94
+ }
95
+ if (!Number.isInteger(degreesOfFreedom) || degreesOfFreedom < 1) {
96
+ throw new RangeError(
97
+ "Student-t confidence interval degreesOfFreedom must be a positive integer."
98
+ );
99
+ }
100
+ return studentTCriticalValue(normalized.level, degreesOfFreedom);
101
+ }
@@ -0,0 +1,77 @@
1
+ import { freezeOwned, isPlainObject } from "../core/immutable.js";
2
+
3
+ export const DEFAULT_LINE_CAP = "butt";
4
+ export const DEFAULT_LINE_JOIN = "miter";
5
+ export const DEFAULT_MITER_LIMIT = 10;
6
+
7
+ export const LINE_CAPS = Object.freeze(["butt", "round", "square"]);
8
+ export const LINE_JOINS = Object.freeze(["miter", "round", "bevel"]);
9
+ export const STROKE_STYLE_PROPERTIES = Object.freeze([
10
+ "lineCap",
11
+ "lineJoin",
12
+ "miterLimit"
13
+ ]);
14
+
15
+ function validateEnum(value, values, property, label) {
16
+ if (typeof value !== "string") {
17
+ throw new TypeError(`${label} ${property} must be a string.`);
18
+ }
19
+ if (!values.includes(value)) {
20
+ throw new Error(
21
+ `${label} ${property} must be one of ${values.join(", ")}.`
22
+ );
23
+ }
24
+ return value;
25
+ }
26
+
27
+ export function validateLineCap(value, label = "Stroke style") {
28
+ return validateEnum(value, LINE_CAPS, "lineCap", label);
29
+ }
30
+
31
+ export function validateLineJoin(value, label = "Stroke style") {
32
+ return validateEnum(value, LINE_JOINS, "lineJoin", label);
33
+ }
34
+
35
+ export function validateMiterLimit(value, label = "Stroke style") {
36
+ if (!Number.isFinite(value)) {
37
+ throw new TypeError(`${label} miterLimit must be a finite number.`);
38
+ }
39
+ if (!(value > 0)) {
40
+ throw new RangeError(`${label} miterLimit must be positive.`);
41
+ }
42
+ return value;
43
+ }
44
+
45
+ export function resolveStrokeDetails(request = {}) {
46
+ if (!isPlainObject(request)) {
47
+ throw new TypeError("Stroke style details must be a plain object.");
48
+ }
49
+ return freezeOwned({
50
+ lineCap: request.lineCap === undefined
51
+ ? DEFAULT_LINE_CAP
52
+ : validateLineCap(request.lineCap),
53
+ lineJoin: request.lineJoin === undefined
54
+ ? DEFAULT_LINE_JOIN
55
+ : validateLineJoin(request.lineJoin),
56
+ miterLimit: request.miterLimit === undefined
57
+ ? DEFAULT_MITER_LIMIT
58
+ : validateMiterLimit(request.miterLimit)
59
+ });
60
+ }
61
+
62
+ export function requestedStrokeDetails(request = {}, label = "Stroke style") {
63
+ if (!isPlainObject(request)) {
64
+ throw new TypeError(`${label} details must be a plain object.`);
65
+ }
66
+ const result = {};
67
+ if (Object.hasOwn(request, "lineCap")) {
68
+ result.lineCap = validateLineCap(request.lineCap, label);
69
+ }
70
+ if (Object.hasOwn(request, "lineJoin")) {
71
+ result.lineJoin = validateLineJoin(request.lineJoin, label);
72
+ }
73
+ if (Object.hasOwn(request, "miterLimit")) {
74
+ result.miterLimit = validateMiterLimit(request.miterLimit, label);
75
+ }
76
+ return freezeOwned(result);
77
+ }
@@ -0,0 +1,245 @@
1
+ import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
2
+ import {
3
+ aggregateRows,
4
+ isScalarAggregate,
5
+ validateAggregate,
6
+ validateAggregateFieldValues
7
+ } from "./aggregate.js";
8
+ import {
9
+ calculateWeightedAggregate,
10
+ normalizeStatisticalWeight,
11
+ readStatisticalWeights,
12
+ summarizeStatisticalWeights,
13
+ validateWeightedAggregate,
14
+ validateWeightedNumericFields,
15
+ weightedRows
16
+ } from "./weightedStatistics.js";
17
+
18
+ const TRANSFORM_KEYS = Object.freeze([
19
+ "type", "groupBy", "aggregates", "members", "weight"
20
+ ]);
21
+ const AGGREGATE_KEYS = Object.freeze(["op", "field", "as"]);
22
+ const NOMINAL_OPERATIONS = new Set(["distinct", "valid", "missing"]);
23
+ const MAX_OUTPUT_ROWS = 10_000;
24
+
25
+ function rejectUnknownKeys(value, supported, label) {
26
+ const unknown = Object.keys(value).find(key => !supported.includes(key));
27
+ if (unknown !== undefined) {
28
+ throw new Error(`Unknown ${label} property "${unknown}".`);
29
+ }
30
+ }
31
+
32
+ function requireField(value, label) {
33
+ if (typeof value !== "string" || value.length === 0) {
34
+ throw new TypeError(`${label} must be a non-empty string.`);
35
+ }
36
+ return value;
37
+ }
38
+
39
+ function normalizeGroupBy(value) {
40
+ if (value === undefined) return [];
41
+ return Array.isArray(value) ? [...value] : [value];
42
+ }
43
+
44
+ function normalizeAggregate(value) {
45
+ if (!isPlainObject(value)) return value;
46
+ return {
47
+ ...value,
48
+ op: validateAggregate(value.op),
49
+ ...(Object.hasOwn(value, "field") ? { field: value.field } : {}),
50
+ as: value.as
51
+ };
52
+ }
53
+
54
+ export function normalizeSummaryTransform({ groupBy, aggregates, members, weight } = {}) {
55
+ const transform = {
56
+ type: "summary",
57
+ groupBy: normalizeGroupBy(groupBy),
58
+ aggregates: Array.isArray(aggregates)
59
+ ? aggregates.map(normalizeAggregate)
60
+ : aggregates,
61
+ ...(members === undefined ? {} : { members }),
62
+ ...(weight === undefined
63
+ ? {}
64
+ : { weight: normalizeStatisticalWeight(weight, "Summary weight") })
65
+ };
66
+ validateSummaryTransform(transform);
67
+ return cloneAndFreeze(transform);
68
+ }
69
+
70
+ export function validateSummaryTransform(transform) {
71
+ if (!isPlainObject(transform)) {
72
+ throw new TypeError("Summary transform must be a plain object.");
73
+ }
74
+ rejectUnknownKeys(transform, TRANSFORM_KEYS, "summary transform");
75
+ if (transform.type !== "summary") {
76
+ throw new Error(`Unsupported summary transform "${transform.type}".`);
77
+ }
78
+ if (
79
+ !Array.isArray(transform.groupBy) ||
80
+ transform.groupBy.some(field => typeof field !== "string" || field.length === 0)
81
+ ) {
82
+ throw new TypeError("Summary groupBy must contain field names.");
83
+ }
84
+ if (new Set(transform.groupBy).size !== transform.groupBy.length) {
85
+ throw new Error("Summary groupBy fields must be unique.");
86
+ }
87
+ if (!Array.isArray(transform.aggregates) || transform.aggregates.length === 0) {
88
+ throw new TypeError("Summary aggregates must be a non-empty array.");
89
+ }
90
+ if (transform.aggregates.length > 64) {
91
+ throw new RangeError("Summary aggregates cannot contain more than 64 outputs.");
92
+ }
93
+ const outputs = new Set(transform.groupBy);
94
+ transform.aggregates.forEach((aggregate, index) => {
95
+ if (!isPlainObject(aggregate)) {
96
+ throw new TypeError(`Summary aggregate ${index} must be a plain object.`);
97
+ }
98
+ rejectUnknownKeys(aggregate, AGGREGATE_KEYS, `summary aggregate ${index}`);
99
+ const operation = validateAggregate(aggregate.op);
100
+ if (transform.weight !== undefined) validateWeightedAggregate(operation);
101
+ if (operation === "count") {
102
+ if (Object.hasOwn(aggregate, "field")) {
103
+ throw new Error("Summary count does not accept a field.");
104
+ }
105
+ } else {
106
+ requireField(aggregate.field, `Summary aggregate ${index} field`);
107
+ }
108
+ const output = requireField(aggregate.as, `Summary aggregate ${index} as`);
109
+ if (outputs.has(output)) {
110
+ throw new Error(`Summary output field "${output}" collides with another output.`);
111
+ }
112
+ outputs.add(output);
113
+ });
114
+ if (transform.members !== undefined) {
115
+ const members = requireField(transform.members, "Summary members");
116
+ if (outputs.has(members)) {
117
+ throw new Error(`Summary members field "${members}" collides with another output.`);
118
+ }
119
+ }
120
+ if (transform.weight !== undefined) {
121
+ normalizeStatisticalWeight(transform.weight, "Summary weight");
122
+ }
123
+ return transform;
124
+ }
125
+
126
+ function scalarKey(value, label) {
127
+ if (value === null) return "null";
128
+ if (typeof value === "string") return `string:${value.length}:${value}`;
129
+ if (typeof value === "boolean") return `boolean:${value}`;
130
+ if (typeof value === "number" && Number.isFinite(value)) {
131
+ return `number:${Object.is(value, -0) ? 0 : value}`;
132
+ }
133
+ throw new TypeError(
134
+ `${label} must contain null, strings, booleans, or finite numbers.`
135
+ );
136
+ }
137
+
138
+ function requireSourceFields(rows, transform) {
139
+ if (rows.length === 0) return;
140
+ const fields = new Set(rows.flatMap(row => Object.keys(row)));
141
+ for (const field of transform.groupBy) {
142
+ if (!fields.has(field)) {
143
+ throw new Error(`Summary source does not contain group field "${field}".`);
144
+ }
145
+ }
146
+ for (const aggregate of transform.aggregates) {
147
+ if (aggregate.field !== undefined && !fields.has(aggregate.field)) {
148
+ throw new Error(`Summary source does not contain aggregate field "${aggregate.field}".`);
149
+ }
150
+ const orderBy = isPlainObject(aggregate.op) ? aggregate.op.orderBy : undefined;
151
+ if (orderBy !== undefined && !fields.has(orderBy)) {
152
+ throw new Error(`Summary source does not contain order field "${orderBy}".`);
153
+ }
154
+ }
155
+ if (transform.weight !== undefined && !fields.has(transform.weight.field)) {
156
+ throw new Error(`Summary source does not contain weight field "${transform.weight.field}".`);
157
+ }
158
+ }
159
+
160
+ function validateAggregateValues(rows, aggregate) {
161
+ if (aggregate.op === "count") return;
162
+ if (isScalarAggregate(aggregate.op) && NOMINAL_OPERATIONS.has(aggregate.op)) {
163
+ validateAggregateFieldValues(rows, aggregate.field, "nominal");
164
+ return;
165
+ }
166
+ validateAggregateFieldValues(rows, aggregate.field, "quantitative");
167
+ }
168
+
169
+ export function deriveSummaryRows(rows, transform) {
170
+ validateSummaryTransform(transform);
171
+ requireSourceFields(rows, transform);
172
+ let weightEntries;
173
+ if (transform.weight === undefined) {
174
+ for (const aggregate of transform.aggregates) {
175
+ validateAggregateValues(rows, aggregate);
176
+ }
177
+ } else {
178
+ weightEntries = readStatisticalWeights(rows, transform.weight, "Summary").entries;
179
+ validateWeightedNumericFields(
180
+ weightEntries,
181
+ [...new Set(transform.aggregates.flatMap(aggregate =>
182
+ aggregate.field === undefined ? [] : [aggregate.field]
183
+ ))],
184
+ "Summary"
185
+ );
186
+ }
187
+
188
+ const groups = new Map();
189
+ if (transform.groupBy.length === 0) {
190
+ groups.set("all", {
191
+ values: {},
192
+ rows,
193
+ ...(weightEntries === undefined ? {} : { entries: weightEntries })
194
+ });
195
+ } else {
196
+ rows.forEach((row, index) => {
197
+ const values = Object.fromEntries(transform.groupBy.map(field => [field, row[field]]));
198
+ const key = transform.groupBy.map(field =>
199
+ scalarKey(row[field], `Summary group field "${field}"`)
200
+ ).join("|");
201
+ const group = groups.get(key) ?? {
202
+ values,
203
+ rows: [],
204
+ ...(weightEntries === undefined ? {} : { entries: [] })
205
+ };
206
+ group.rows.push(row);
207
+ if (weightEntries !== undefined) group.entries.push(weightEntries[index]);
208
+ groups.set(key, group);
209
+ });
210
+ }
211
+ if (groups.size > MAX_OUTPUT_ROWS) {
212
+ throw new RangeError(`Summary output cannot exceed ${MAX_OUTPUT_ROWS} groups.`);
213
+ }
214
+
215
+ return [...groups.values()].map(group => {
216
+ const weightSummary = transform.weight === undefined
217
+ ? undefined
218
+ : summarizeStatisticalWeights(
219
+ group.entries,
220
+ transform.weight.kind,
221
+ "Summary group"
222
+ );
223
+ return {
224
+ ...group.values,
225
+ ...Object.fromEntries(transform.aggregates.map(aggregate => [
226
+ aggregate.as,
227
+ weightSummary === undefined
228
+ ? aggregateRows(group.rows, aggregate.field ?? "__row", aggregate.op)
229
+ : calculateWeightedAggregate(
230
+ weightSummary,
231
+ aggregate.field,
232
+ aggregate.op,
233
+ `Summary aggregate "${aggregate.as}"`
234
+ )
235
+ ])),
236
+ ...(transform.members === undefined
237
+ ? {}
238
+ : {
239
+ [transform.members]: weightSummary === undefined
240
+ ? group.rows
241
+ : weightedRows(weightSummary)
242
+ })
243
+ };
244
+ });
245
+ }
@@ -1,5 +1,15 @@
1
1
  import { cloneAndFreeze } from "../core/immutable.js";
2
2
  import { validateConcreteGraphicValue } from "./schemas/concreteGraphic.js";
3
+ import {
4
+ formatValue,
5
+ isUtcValueFormat,
6
+ validateValueFormat
7
+ } from "./valueFormat.js";
8
+ import { resolveRotation } from "./rotation.js";
9
+
10
+ export function isSourceOwnedText(layer) {
11
+ return layer?.mark?.type === "text" && layer.source !== undefined;
12
+ }
3
13
 
4
14
  export const DEFAULT_TEXT_MARK = cloneAndFreeze({
5
15
  fill: "#334155",
@@ -14,20 +24,13 @@ export const DEFAULT_TEXT_MARK = cloneAndFreeze({
14
24
  dy: 0
15
25
  });
16
26
 
17
- const FIXED_FORMAT = /^\.(\d{1,2})f$/;
18
-
19
27
  export function validateTextFormat(format) {
20
- if (format === undefined || format === "auto") return format ?? "auto";
21
- if (typeof format !== "string" || !FIXED_FORMAT.test(format)) {
28
+ if (format !== undefined && typeof format !== "string") {
22
29
  throw new Error(
23
- 'Text format must be "auto" or a fixed-decimal token such as ".1f".'
30
+ 'Text format must be "auto" or a supported numeric/UTC format string.'
24
31
  );
25
32
  }
26
- const decimals = Number(format.match(FIXED_FORMAT)[1]);
27
- if (decimals > 12) {
28
- throw new RangeError("Text fixed-decimal format supports at most 12 decimals.");
29
- }
30
- return format;
33
+ return validateValueFormat(format, "Text format");
31
34
  }
32
35
 
33
36
  export function formatTextValue(value, format = "auto") {
@@ -37,10 +40,11 @@ export function formatTextValue(value, format = "auto") {
37
40
  const text = String(value);
38
41
  return text.length === 0 ? undefined : text;
39
42
  }
40
- if (!Number.isFinite(value)) {
41
- throw new TypeError(`Text format "${resolved}" requires a finite number.`);
42
- }
43
- return value.toFixed(Number(resolved.match(FIXED_FORMAT)[1]));
43
+ return formatValue(value, {
44
+ format: resolved,
45
+ valueType: isUtcValueFormat(resolved) ? "temporal" : "quantitative",
46
+ label: "Text format"
47
+ });
44
48
  }
45
49
 
46
50
  export function normalizeTextMarkConfig(options, base = DEFAULT_TEXT_MARK) {
@@ -52,14 +56,16 @@ export function normalizeTextMarkConfig(options, base = DEFAULT_TEXT_MARK) {
52
56
  fontFamily: "fontFamily",
53
57
  fontWeight: "fontWeight",
54
58
  align: "textAlign",
55
- baseline: "textBaseline",
56
- rotation: "rotation"
59
+ baseline: "textBaseline"
57
60
  };
58
61
  for (const [option, property] of Object.entries(mapping)) {
59
62
  if (!Object.hasOwn(options, option)) continue;
60
63
  validateConcreteGraphicValue("text", property, options[option]);
61
64
  config[option] = options[option];
62
65
  }
66
+ if (Object.hasOwn(options, "rotation")) {
67
+ config.rotation = resolveRotation(options.rotation, "Text rotation");
68
+ }
63
69
  for (const property of ["dx", "dy"]) {
64
70
  if (!Object.hasOwn(options, property)) continue;
65
71
  if (!Number.isFinite(options[property])) {