ggaction 0.0.13 → 0.0.16

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 (359) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/README.md +41 -12
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/knowledge/task-packet.schema.json +1 -1
  7. package/knowledge/task-resolver.js +1 -1
  8. package/package.json +39 -7
  9. package/src/BasicChartProgram.js +3 -1
  10. package/src/ChartProgram.js +2 -0
  11. package/src/accessibility.js +157 -0
  12. package/src/actions/basic.js +2 -2
  13. package/src/actions/boxPlots/components.js +20 -12
  14. package/src/actions/boxPlots/create.js +4 -5
  15. package/src/actions/boxPlots/edit.js +34 -9
  16. package/src/actions/boxPlots/materialize.js +12 -4
  17. package/src/actions/boxPlots/options.js +48 -16
  18. package/src/actions/canvas/actions.js +3 -3
  19. package/src/actions/canvas/fitting.js +1 -1
  20. package/src/actions/categoryOrder/index.js +21 -12
  21. package/src/actions/charts/area.js +6 -2
  22. package/src/actions/charts/bar.js +3 -2
  23. package/src/actions/charts/beeswarm.js +1 -1
  24. package/src/actions/charts/density.js +7 -3
  25. package/src/actions/charts/ecdf.js +6 -3
  26. package/src/actions/charts/endpoints.js +29 -7
  27. package/src/actions/charts/heatmap.js +3 -2
  28. package/src/actions/charts/histogram.js +5 -4
  29. package/src/actions/charts/horizon.js +4 -2
  30. package/src/actions/charts/interval-regression.js +7 -4
  31. package/src/actions/charts/line.js +4 -2
  32. package/src/actions/charts/parallel.js +9 -3
  33. package/src/actions/charts/pie.js +11 -3
  34. package/src/actions/charts/polar.js +17 -6
  35. package/src/actions/charts/radar.js +9 -3
  36. package/src/actions/charts/radial.js +12 -4
  37. package/src/actions/charts/raincloud.js +2 -2
  38. package/src/actions/charts/rug-strip.js +8 -4
  39. package/src/actions/charts/scatter.js +4 -2
  40. package/src/actions/composition/actions.js +29 -24
  41. package/src/actions/coordinates/actions.js +2 -2
  42. package/src/actions/coordinates/edit.js +76 -0
  43. package/src/actions/data/basicBin2d.js +4 -5
  44. package/src/actions/data/bin.js +3 -3
  45. package/src/actions/data/bin2d.js +63 -193
  46. package/src/actions/data/bin2dMaterialize.js +4 -5
  47. package/src/actions/data/box.js +4 -4
  48. package/src/actions/data/complete.js +26 -0
  49. package/src/actions/data/computed.js +4 -4
  50. package/src/actions/data/create.js +7 -14
  51. package/src/actions/data/density.js +15 -23
  52. package/src/actions/data/derived.js +57 -31
  53. package/src/actions/data/ecdf.js +2 -2
  54. package/src/actions/data/edit.js +551 -0
  55. package/src/actions/data/filter.js +28 -22
  56. package/src/actions/data/fold.js +2 -2
  57. package/src/actions/data/gradientProfile.js +2 -2
  58. package/src/actions/data/horizon.js +2 -2
  59. package/src/actions/data/impute.js +26 -0
  60. package/src/actions/data/index.js +62 -47
  61. package/src/actions/data/interval.js +2 -2
  62. package/src/actions/data/intervalEdit.js +26 -18
  63. package/src/actions/data/normalize.js +27 -0
  64. package/src/actions/data/regression.js +21 -27
  65. package/src/actions/data/revise.js +48 -0
  66. package/src/actions/data/revisionBindings.js +125 -0
  67. package/src/actions/data/shared.js +20 -4
  68. package/src/actions/data/stack.js +2 -2
  69. package/src/actions/data/summary.js +3 -3
  70. package/src/actions/data/timeUnit.js +6 -3
  71. package/src/actions/data/window.js +3 -3
  72. package/src/actions/encodings/angle.js +1 -1
  73. package/src/actions/encodings/appearance.js +4 -4
  74. package/src/actions/encodings/barWidth.js +1 -1
  75. package/src/actions/encodings/channels.js +415 -0
  76. package/src/actions/encodings/color/index.js +3 -2
  77. package/src/actions/encodings/density.js +7 -4
  78. package/src/actions/encodings/histogram.js +4 -3
  79. package/src/actions/encodings/horizon.js +6 -8
  80. package/src/actions/encodings/index.js +4 -0
  81. package/src/actions/encodings/offset.js +22 -15
  82. package/src/actions/encodings/parallel.js +4 -5
  83. package/src/actions/encodings/pathOrder.js +6 -11
  84. package/src/actions/encodings/position/apply.js +53 -7
  85. package/src/actions/encodings/position/index.js +14 -6
  86. package/src/actions/encodings/position/policies/bar.js +9 -0
  87. package/src/actions/encodings/position/resolve.js +24 -7
  88. package/src/actions/encodings/ranged.js +5 -5
  89. package/src/actions/encodings/remove.js +13 -9
  90. package/src/actions/encodings/ruleAppearance.js +2 -24
  91. package/src/actions/encodings/seriesLayout.js +2 -1
  92. package/src/actions/encodings/shared.js +82 -14
  93. package/src/actions/encodings/stroke.js +226 -0
  94. package/src/actions/encodings/strokeDash.js +2 -2
  95. package/src/actions/encodings/text.js +1 -1
  96. package/src/actions/errorBands/create.js +34 -20
  97. package/src/actions/errorBands/edit.js +34 -137
  98. package/src/actions/errorBars/create.js +43 -20
  99. package/src/actions/errorBars/edit.js +29 -125
  100. package/src/actions/facets/actions.js +267 -73
  101. package/src/actions/facets/derive.js +99 -28
  102. package/src/actions/facets/guides.js +4 -5
  103. package/src/actions/facets/replay.js +4 -5
  104. package/src/actions/gradientPlots/components.js +15 -8
  105. package/src/actions/gradientPlots/create.js +4 -5
  106. package/src/actions/gradientPlots/edit.js +18 -9
  107. package/src/actions/gradientPlots/materialize.js +5 -3
  108. package/src/actions/gradientPlots/options.js +10 -2
  109. package/src/actions/gradientPlots/rebind.js +4 -5
  110. package/src/actions/guides/applicability.js +8 -2
  111. package/src/actions/guides/axes/axes.js +23 -4
  112. package/src/actions/guides/axes/axis.js +2 -2
  113. package/src/actions/guides/axes/edit.js +2 -2
  114. package/src/actions/guides/axes/labels.js +67 -21
  115. package/src/actions/guides/axes/lines.js +15 -9
  116. package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
  117. package/src/actions/guides/axes/parallel/resolve.js +6 -2
  118. package/src/actions/guides/axes/parallel.js +5 -5
  119. package/src/actions/guides/axes/remove.js +4 -4
  120. package/src/actions/guides/axes/tickGroups.js +6 -5
  121. package/src/actions/guides/axes/ticks.js +19 -6
  122. package/src/actions/guides/axes/titles.js +21 -13
  123. package/src/actions/guides/grids/grid.js +18 -29
  124. package/src/actions/guides/grids/resolve.js +7 -2
  125. package/src/actions/guides/guides.js +20 -4
  126. package/src/actions/guides/legends/blocks.js +426 -0
  127. package/src/actions/guides/legends/categorical/actions.js +138 -52
  128. package/src/actions/guides/legends/categorical/components.js +55 -45
  129. package/src/actions/guides/legends/categorical/index.js +37 -33
  130. package/src/actions/guides/legends/categorical/layout.js +98 -16
  131. package/src/actions/guides/legends/categorical/options.js +2 -2
  132. package/src/actions/guides/legends/categorical/recipes.js +111 -20
  133. package/src/actions/guides/legends/categorical/resolve.js +12 -8
  134. package/src/actions/guides/legends/categorical/symbols.js +104 -52
  135. package/src/actions/guides/legends/continuous/common.js +76 -20
  136. package/src/actions/guides/legends/continuous/gradient.js +95 -158
  137. package/src/actions/guides/legends/continuous/index.js +2 -0
  138. package/src/actions/guides/legends/continuous/interval.js +21 -16
  139. package/src/actions/guides/legends/continuous/opacity.js +37 -83
  140. package/src/actions/guides/legends/continuous/stroke.js +225 -0
  141. package/src/actions/guides/legends/creation.js +14 -1
  142. package/src/actions/guides/legends/edit.js +159 -60
  143. package/src/actions/guides/legends/focused.js +5 -5
  144. package/src/actions/guides/legends/index.js +2 -0
  145. package/src/actions/guides/legends/lane.js +59 -24
  146. package/src/actions/guides/legends/lifecycle.js +19 -8
  147. package/src/actions/guides/legends/remove.js +17 -8
  148. package/src/actions/guides/legends/sampling.js +140 -0
  149. package/src/actions/guides/legends/size.js +100 -108
  150. package/src/actions/guides/legends/strokeWidth.js +38 -92
  151. package/src/actions/guides/legends/target.js +112 -1
  152. package/src/actions/guides/legends/transition.js +165 -16
  153. package/src/actions/guides/polar/axes/facade.js +8 -8
  154. package/src/actions/guides/polar/axes/labels.js +29 -10
  155. package/src/actions/guides/polar/axes/lines.js +19 -19
  156. package/src/actions/guides/polar/axes/shared.js +16 -0
  157. package/src/actions/guides/polar/axes/ticks.js +10 -16
  158. package/src/actions/guides/polar/axes/titles.js +6 -6
  159. package/src/actions/guides/polar/grids.js +12 -14
  160. package/src/actions/guides/polar/resolve.js +12 -8
  161. package/src/actions/index.js +16 -3
  162. package/src/actions/intervals/revision.js +126 -0
  163. package/src/actions/marks/arc/actions.js +80 -22
  164. package/src/actions/marks/area/actions.js +38 -13
  165. package/src/actions/marks/area/materialize.js +17 -1
  166. package/src/actions/marks/bar/create.js +12 -3
  167. package/src/actions/marks/bar/edit.js +21 -4
  168. package/src/actions/marks/bar/materialize.js +34 -40
  169. package/src/actions/marks/index.js +10 -1
  170. package/src/actions/marks/line/actions.js +40 -17
  171. package/src/actions/marks/line/materialize.js +29 -10
  172. package/src/actions/marks/point/create.js +9 -3
  173. package/src/actions/marks/point/edit.js +19 -7
  174. package/src/actions/marks/point/jitter.js +6 -8
  175. package/src/actions/marks/point/materialize.js +48 -22
  176. package/src/actions/marks/point/packing.js +6 -8
  177. package/src/actions/marks/rect/actions.js +36 -15
  178. package/src/actions/marks/references.js +602 -33
  179. package/src/actions/marks/remove.js +67 -41
  180. package/src/actions/marks/rule/actions.js +51 -14
  181. package/src/actions/marks/shared.js +1 -1
  182. package/src/actions/marks/text/actions.js +79 -14
  183. package/src/actions/marks/text/index.js +4 -0
  184. package/src/actions/marks/text/layout.js +157 -17
  185. package/src/actions/marks/text/removal.js +173 -0
  186. package/src/actions/marks/text/selection.js +72 -0
  187. package/src/actions/marks/tick/actions.js +25 -8
  188. package/src/actions/primitives/createGraphics.js +5 -4
  189. package/src/actions/primitives/editGraphics.js +31 -4
  190. package/src/actions/primitives/graphicProperties.js +6 -0
  191. package/src/actions/primitives/semantic.js +11 -3
  192. package/src/actions/primitives/semanticAction.js +90 -14
  193. package/src/actions/primitives/semanticValidation/dataset.js +2 -4
  194. package/src/actions/primitives/semanticValidation/guide.js +1 -1
  195. package/src/actions/primitives/semanticValidation/index.js +9 -1
  196. package/src/actions/primitives/semanticValidation/layer.js +5 -0
  197. package/src/actions/primitives/semanticValidation/scale.js +28 -7
  198. package/src/actions/regression/components.js +36 -25
  199. package/src/actions/regression/create.js +13 -6
  200. package/src/actions/regression/edit.js +24 -11
  201. package/src/actions/resources/index.js +7 -0
  202. package/src/actions/resources/remove.js +173 -0
  203. package/src/actions/scales/channels.js +212 -0
  204. package/src/actions/scales/consumers/common.js +1 -1
  205. package/src/actions/scales/consumers/families.js +3 -2
  206. package/src/actions/scales/consumers/index.js +19 -2
  207. package/src/actions/scales/consumers/seriesLayout.js +28 -14
  208. package/src/actions/scales/create.js +55 -23
  209. package/src/actions/scales/definitions.js +94 -40
  210. package/src/actions/scales/edit.js +67 -12
  211. package/src/actions/scales/editPolicy.js +81 -11
  212. package/src/actions/scales/index.js +6 -0
  213. package/src/actions/scales/materialize.js +5 -4
  214. package/src/actions/scales/offset.js +48 -0
  215. package/src/actions/scales/parallel.js +71 -0
  216. package/src/actions/scales/preview.js +111 -10
  217. package/src/actions/scales/quantitativeColor.js +4 -5
  218. package/src/actions/selection/actions.js +179 -66
  219. package/src/actions/selection/index.js +3 -0
  220. package/src/actions/textMetrics/index.js +56 -0
  221. package/src/actions/theme/actions.js +70 -15
  222. package/src/actions/theme/composition.js +238 -0
  223. package/src/actions/theme/index.js +9 -2
  224. package/src/actions/theme/reconcile.js +313 -121
  225. package/src/actions/theme/state.js +160 -0
  226. package/src/actions/titles/actions.js +8 -8
  227. package/src/actions/titles/resolve.js +3 -3
  228. package/src/actions/violinPlots/create.js +6 -3
  229. package/src/actions/violinPlots/edit.js +2 -1
  230. package/src/core/action.js +117 -35
  231. package/src/core/compositionState.js +6 -1
  232. package/src/core/diagnostics.js +16 -0
  233. package/src/core/extensionRegistry.js +27 -0
  234. package/src/core/font.js +18 -0
  235. package/src/core/identifiers.js +5 -4
  236. package/src/core/immutable.js +10 -3
  237. package/src/core/optionRequirements.js +8 -0
  238. package/src/core/programState.js +21 -1
  239. package/src/core/resourceReferences.js +341 -0
  240. package/src/core/textMetricProfile.js +28 -0
  241. package/src/core/textMetrics.js +24 -3
  242. package/src/core/validation.js +22 -10
  243. package/src/core/vocabulary.js +16 -3
  244. package/src/diagnostics.js +1 -0
  245. package/src/grammar/bars/aggregate.js +4 -1
  246. package/src/grammar/bars/geometry.js +44 -0
  247. package/src/grammar/bin.js +54 -9
  248. package/src/grammar/complete.js +279 -0
  249. package/src/grammar/computed.js +265 -64
  250. package/src/grammar/coordinates.js +37 -0
  251. package/src/grammar/density.js +239 -37
  252. package/src/grammar/displayLabels.js +57 -0
  253. package/src/grammar/facets/dependencies.js +133 -1
  254. package/src/grammar/facets/guides.js +3 -0
  255. package/src/grammar/facets/index.js +7 -47
  256. package/src/grammar/facets/scales.js +15 -18
  257. package/src/grammar/impute.js +341 -0
  258. package/src/grammar/markLabelSelection.js +57 -0
  259. package/src/grammar/markLabels.js +18 -1
  260. package/src/grammar/normalize.js +366 -0
  261. package/src/grammar/pathSeries.js +26 -8
  262. package/src/grammar/pointShapes.js +13 -4
  263. package/src/grammar/polar.js +90 -8
  264. package/src/grammar/regression/index.js +1 -0
  265. package/src/grammar/regression/parameters.js +23 -0
  266. package/src/grammar/roundedRect.js +180 -0
  267. package/src/grammar/rules.js +1 -0
  268. package/src/grammar/scales/colorConsumers.js +20 -1
  269. package/src/grammar/scales/continuous.js +2 -2
  270. package/src/grammar/scales/definition.js +27 -4
  271. package/src/grammar/scales/index.js +1 -0
  272. package/src/grammar/scales/ordinal.js +12 -3
  273. package/src/grammar/scales/policies.js +2 -2
  274. package/src/grammar/scales/size.js +456 -0
  275. package/src/grammar/scales/types.js +8 -0
  276. package/src/grammar/schemas/concreteGraphic.js +14 -2
  277. package/src/grammar/schemas/graphic.js +15 -0
  278. package/src/grammar/schemas/graphicBounds.js +190 -41
  279. package/src/grammar/schemas/semanticPath.js +7 -2
  280. package/src/grammar/statisticalReference.js +103 -0
  281. package/src/grammar/strokeStyle.js +77 -0
  282. package/src/grammar/summary.js +78 -16
  283. package/src/grammar/timeUnit.js +216 -5
  284. package/src/grammar/transformTopology.js +4 -0
  285. package/src/grammar/transforms.js +394 -28
  286. package/src/grammar/weightedStatistics.js +374 -0
  287. package/src/grammar/window.js +221 -16
  288. package/src/layout/aspect.js +80 -0
  289. package/src/layout/facets.js +59 -5
  290. package/src/layout/labels.js +351 -7
  291. package/src/layout/legend.js +4 -4
  292. package/src/layout/legendItems.js +6 -6
  293. package/src/layout/text.js +10 -9
  294. package/src/layout/title.js +9 -9
  295. package/src/materialization/bars/aggregate.js +25 -2
  296. package/src/materialization/bars/histogram.js +84 -24
  297. package/src/materialization/bars/ranged.js +19 -3
  298. package/src/materialization/bars/resolve.js +7 -4
  299. package/src/materialization/composition.js +3 -1
  300. package/src/materialization/coordinateBounds.js +204 -0
  301. package/src/materialization/dependencies.js +36 -0
  302. package/src/materialization/facetGuides/legacyCategorical.js +3 -0
  303. package/src/materialization/facetGuides/placement.js +9 -3
  304. package/src/materialization/facetGuides/preparation.js +13 -8
  305. package/src/materialization/facetHeaders.js +347 -0
  306. package/src/materialization/facets.js +15 -65
  307. package/src/materialization/guides/layout.js +3 -3
  308. package/src/materialization/guides/resources.js +26 -0
  309. package/src/materialization/legends.js +1 -1
  310. package/src/materialization/marks/index.js +11 -5
  311. package/src/materialization/marks/policies.js +2 -1
  312. package/src/materialization/rect.js +25 -12
  313. package/src/materialization/rectConfig.js +5 -1
  314. package/src/materialization/scaleGuideDependencies.js +10 -0
  315. package/src/materialization/scales/map.js +5 -3
  316. package/src/materialization/scales/policies/binnedPosition.js +2 -2
  317. package/src/materialization/scales/policies/offset.js +50 -8
  318. package/src/materialization/scales/policies/series.js +4 -1
  319. package/src/materialization/scales/resolve.js +22 -6
  320. package/src/materialization/selection/items/arc.js +28 -4
  321. package/src/materialization/selection/items/bar.js +30 -3
  322. package/src/materialization/selection/items/common.js +20 -5
  323. package/src/materialization/selection/items/rect.js +28 -4
  324. package/src/materialization/selection/path.js +1 -1
  325. package/src/materialization/selection/point.js +3 -1
  326. package/src/materialization/text.js +265 -14
  327. package/src/materialization/typography.js +31 -0
  328. package/src/mcp/cli.js +5 -3
  329. package/src/persistence/codec.js +65 -0
  330. package/src/persistence/validation.js +185 -0
  331. package/src/persistence.js +74 -0
  332. package/src/renderers/canvas/circle.js +2 -3
  333. package/src/renderers/canvas/index.js +5 -4
  334. package/src/renderers/canvas/line.js +2 -3
  335. package/src/renderers/canvas/native.js +4 -4
  336. package/src/renderers/canvas/path.js +2 -3
  337. package/src/renderers/canvas/rect.js +2 -3
  338. package/src/renderers/canvas/stroke.js +11 -0
  339. package/src/renderers/nativeBackend.js +10 -0
  340. package/src/renderers/options.js +13 -0
  341. package/src/renderers/pdf.js +25 -54
  342. package/src/renderers/png.js +22 -17
  343. package/src/renderers/svg.js +29 -14
  344. package/src/renderers/text.js +1 -11
  345. package/src/selectors/coordinates.js +3 -1
  346. package/src/selectors/datasets.js +38 -5
  347. package/src/selectors/layers.js +10 -4
  348. package/src/selectors/markOwners.js +46 -0
  349. package/src/selectors/scales.js +6 -4
  350. package/src/theme/defaults.js +103 -1
  351. package/src/version.js +2 -0
  352. package/types/accessibility.d.ts +37 -0
  353. package/types/basic.d.ts +10 -2
  354. package/types/diagnostics.d.ts +15 -0
  355. package/types/index.d.ts +90 -0
  356. package/types/pdf.d.ts +9 -0
  357. package/types/persistence.d.ts +7 -0
  358. package/types/png.d.ts +9 -0
  359. package/types/program.d.ts +944 -118
@@ -0,0 +1,551 @@
1
+ import { action, closedAction } from "../../core/action.js";
2
+ import { isPlainObject } from "../../core/immutable.js";
3
+ import { validateUserId } from "../../core/identifiers.js";
4
+ import { validateKeys } from "../../core/validation.js";
5
+ import {
6
+ findTransformPolicy,
7
+ normalizeDatasetTransformEdit,
8
+ requestedDatasetTransform
9
+ } from "../../grammar/transforms.js";
10
+ import {
11
+ findDataset,
12
+ hasDataset
13
+ } from "../../selectors/datasets.js";
14
+ import { applyLayerDataRematerialization } from
15
+ "../../materialization/dependencies.js";
16
+ import {
17
+ outputFieldChanges,
18
+ rebindLayerOutputFields,
19
+ rebindMaterializationDataReferences
20
+ } from "./revisionBindings.js";
21
+
22
+ const GENERIC_OPTIONS = Object.freeze([
23
+ "target", "definition", "dependents"
24
+ ]);
25
+ const DEPENDENT_POLICIES = new Set(["reject", "recompute"]);
26
+ const CREATOR_BY_TYPE = Object.freeze({
27
+ bin: "createBinData",
28
+ bin2d: "createBin2DData",
29
+ complete: "createCompleteData",
30
+ computed: "createComputedData",
31
+ density: "createDensityData",
32
+ ecdf: "createECDFData",
33
+ filter: "filterData",
34
+ fold: "createFoldData",
35
+ impute: "createImputedData",
36
+ interval: "createIntervalData",
37
+ normalize: "createNormalizedData",
38
+ regression: "createRegressionData",
39
+ stack: "createStackData",
40
+ summary: "createSummaryData",
41
+ timeUnit: "createTimeUnitData",
42
+ window: "createWindowData"
43
+ });
44
+ const REVISION_ROLE = Object.freeze({
45
+ bin: "BinData",
46
+ bin2d: "Bin2DData",
47
+ complete: "CompleteData",
48
+ computed: "ComputedData",
49
+ density: "DensityData",
50
+ ecdf: "ECDFData",
51
+ filter: "FilteredData",
52
+ fold: "FoldData",
53
+ impute: "ImputedData",
54
+ interval: "IntervalData",
55
+ normalize: "NormalizedData",
56
+ regression: "RegressionData",
57
+ stack: "StackData",
58
+ summary: "SummaryData",
59
+ timeUnit: "TimeUnitData",
60
+ window: "WindowData"
61
+ });
62
+
63
+ function ownerRecords(program, type) {
64
+ const families = program.materializationConfigs.data ?? {};
65
+ return Object.entries(families).flatMap(([family, owners]) =>
66
+ Object.entries(owners ?? {}).flatMap(([owner, config]) => {
67
+ if (typeof config?.current !== "string") return [];
68
+ const dataset = findDataset(program, config.current);
69
+ const currentType = dataset?.transform?.[0]?.type ?? family;
70
+ if (type !== undefined && currentType !== type) return [];
71
+ return [{ family, owner, current: config.current, dataset }];
72
+ })
73
+ );
74
+ }
75
+
76
+ function escapedPattern(value) {
77
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
78
+ }
79
+
80
+ function staleOwnerRecord(program, requested, type) {
81
+ return ownerRecords(program, type).find(record => {
82
+ const transformType = record.dataset?.transform?.[0]?.type ?? record.family;
83
+ const role = REVISION_ROLE[transformType];
84
+ if (role === undefined || requested === record.current) return false;
85
+ return new RegExp(
86
+ `^${escapedPattern(record.owner)}${role}Revision\\d+$`
87
+ ).test(requested);
88
+ });
89
+ }
90
+
91
+ function topLevelCreatorRecorded(program, dataset) {
92
+ const creator = CREATOR_BY_TYPE[dataset.transform?.[0]?.type];
93
+ return creator !== undefined && program.trace.children.some(node =>
94
+ node.op === creator && node.args?.id === dataset.id
95
+ );
96
+ }
97
+
98
+ export function resolveDerivedDataOwner(program, target, type) {
99
+ const requested = validateUserId(target, "Derived dataset target");
100
+ const matching = ownerRecords(program, type).filter(record =>
101
+ record.owner === requested || record.current === requested
102
+ );
103
+ if (matching.length > 1) {
104
+ throw new Error(`Derived dataset target "${requested}" is ambiguous.`);
105
+ }
106
+ if (matching.length === 1) {
107
+ const record = matching[0];
108
+ if (record.dataset === undefined) {
109
+ throw new Error(
110
+ `Derived dataset owner "${record.owner}" has no current dataset.`
111
+ );
112
+ }
113
+ return { ...record, lazy: false };
114
+ }
115
+
116
+ const dataset = findDataset(program, requested);
117
+ if (dataset === undefined) {
118
+ throw new Error(staleOwnerRecord(program, requested, type) !== undefined
119
+ ? `Derived dataset revision "${requested}" is stale.`
120
+ : `Unknown derived dataset owner "${requested}".`
121
+ );
122
+ }
123
+ if (staleOwnerRecord(program, requested, type) !== undefined) {
124
+ throw new Error(`Derived dataset revision "${requested}" is stale.`);
125
+ }
126
+ if (dataset.source === undefined || dataset.transform?.length !== 1) {
127
+ throw new Error(
128
+ `Dataset "${requested}" is a source dataset and cannot be edited.`
129
+ );
130
+ }
131
+ const currentType = dataset.transform[0].type;
132
+ if (type !== undefined && currentType !== type) {
133
+ throw new Error(
134
+ `Derived dataset "${requested}" is ${currentType}, not ${type}.`
135
+ );
136
+ }
137
+ if (
138
+ findTransformPolicy(currentType)?.editable !== true ||
139
+ !topLevelCreatorRecorded(program, dataset)
140
+ ) {
141
+ throw new Error(
142
+ `Derived dataset "${requested}" is chart-owned; use its owning chart editor.`
143
+ );
144
+ }
145
+ return {
146
+ family: currentType,
147
+ owner: dataset.id,
148
+ current: dataset.id,
149
+ dataset,
150
+ lazy: true
151
+ };
152
+ }
153
+
154
+ export function normalizeDerivedDependentPolicy(value) {
155
+ const policy = value ?? "reject";
156
+ if (!DEPENDENT_POLICIES.has(policy)) {
157
+ throw new Error(
158
+ 'Derived data dependents must be "reject" or "recompute".'
159
+ );
160
+ }
161
+ return policy;
162
+ }
163
+
164
+ function downstreamDatasets(program, root) {
165
+ const order = new Map(
166
+ program.semanticSpec.datasets.map((dataset, index) => [dataset.id, index])
167
+ );
168
+ const bySource = new Map();
169
+ for (const dataset of program.semanticSpec.datasets) {
170
+ if (dataset.source === undefined) continue;
171
+ const children = bySource.get(dataset.source) ?? [];
172
+ children.push(dataset);
173
+ bySource.set(dataset.source, children);
174
+ }
175
+ for (const children of bySource.values()) {
176
+ children.sort((left, right) => order.get(left.id) - order.get(right.id));
177
+ }
178
+
179
+ const result = [];
180
+ const visiting = new Set();
181
+ const visited = new Set();
182
+ function visit(id) {
183
+ if (visiting.has(id)) {
184
+ throw new Error(`Derived dataset dependency graph contains a cycle at "${id}".`);
185
+ }
186
+ if (visited.has(id)) return;
187
+ visiting.add(id);
188
+ for (const child of bySource.get(id) ?? []) {
189
+ if (child.transform?.length !== 1) {
190
+ throw new Error(
191
+ `Derived dataset "${child.id}" requires one replayable transform.`
192
+ );
193
+ }
194
+ const policy = findTransformPolicy(child.transform[0].type);
195
+ if (typeof policy?.materializeOp !== "string") {
196
+ throw new Error(
197
+ `Derived dataset "${child.id}" has unsupported transform ` +
198
+ `"${child.transform[0].type ?? "unknown"}".`
199
+ );
200
+ }
201
+ // Statistical reference data is an owned cache. Its mark lifecycle
202
+ // reconnects and recomputes it after the visible source mark is rebound.
203
+ if (child.transform[0].type === "statisticalReference") continue;
204
+ result.push(child);
205
+ visit(child.id);
206
+ }
207
+ visiting.delete(id);
208
+ visited.add(id);
209
+ }
210
+ visit(root);
211
+ return result;
212
+ }
213
+
214
+ function ownerForCurrent(program, dataset) {
215
+ const matches = ownerRecords(program).filter(record =>
216
+ record.current === dataset.id
217
+ );
218
+ if (matches.length > 1) {
219
+ throw new Error(
220
+ `Derived dataset "${dataset.id}" has ambiguous logical ownership.`
221
+ );
222
+ }
223
+ return matches[0];
224
+ }
225
+
226
+ function nextRevisionId(program, owner, role, reserved) {
227
+ let revision = 1;
228
+ const prefix = `${owner}${role}Revision`;
229
+ while (
230
+ hasDataset(program, `${prefix}${revision}`) ||
231
+ reserved.has(`${prefix}${revision}`)
232
+ ) revision += 1;
233
+ const id = `${prefix}${revision}`;
234
+ reserved.add(id);
235
+ return id;
236
+ }
237
+
238
+ function buildRevisionPlan(
239
+ program,
240
+ resolved,
241
+ definition,
242
+ dependents,
243
+ rootSource = resolved.dataset.source
244
+ ) {
245
+ const downstream = downstreamDatasets(program, resolved.current);
246
+ if (dependents === "reject" && downstream.length > 0) {
247
+ throw new Error(
248
+ `Cannot edit derived dataset "${resolved.owner}" while derived dataset ` +
249
+ `"${downstream[0].id}" depends on it; use dependents: "recompute".`
250
+ );
251
+ }
252
+ if (downstream.some(dataset => dataset.id === rootSource)) {
253
+ throw new Error(
254
+ `Derived dataset "${resolved.owner}" cannot use one of its dependents as source.`
255
+ );
256
+ }
257
+ const reserved = new Set(resolved.revision === undefined ? [] : [resolved.revision]);
258
+ const revisions = [];
259
+ const replacements = new Map();
260
+ const add = (dataset, ownerRecord, transform, requestedSource) => {
261
+ const owner = ownerRecord?.owner ?? dataset.id;
262
+ const role = REVISION_ROLE[transform.type] ?? "DerivedData";
263
+ const id = nextRevisionId(program, owner, role, reserved);
264
+ const originalSource = requestedSource ?? dataset.source;
265
+ const source = replacements.get(originalSource) ?? originalSource;
266
+ const revision = {
267
+ old: dataset.id,
268
+ id,
269
+ source,
270
+ transform,
271
+ family: ownerRecord?.family,
272
+ owner: ownerRecord?.owner
273
+ };
274
+ revisions.push(revision);
275
+ replacements.set(dataset.id, id);
276
+ };
277
+ if (resolved.revision === undefined) {
278
+ add(resolved.dataset, resolved, definition, rootSource);
279
+ } else {
280
+ revisions.push({ old: resolved.current, id: resolved.revision });
281
+ replacements.set(resolved.current, resolved.revision);
282
+ }
283
+ if (dependents === "recompute") {
284
+ for (const dataset of downstream) {
285
+ const policy = findTransformPolicy(dataset.transform[0].type);
286
+ const transform = policy.replayTransform?.(dataset.transform[0]) ??
287
+ dataset.transform[0];
288
+ add(dataset, ownerForCurrent(program, dataset), transform);
289
+ }
290
+ }
291
+ return { revisions, replacements };
292
+ }
293
+
294
+ function directLayerConsumers(program, data) {
295
+ return program.semanticSpec.layers
296
+ .filter(layer => layer.data === data)
297
+ .map(layer => layer.id);
298
+ }
299
+
300
+ export function applyRevisionPlan(program, resolved, plan, { retain = new Set() } = {}) {
301
+ const root = plan.revisions[0];
302
+ const changes = resolved === undefined ? new Map() : outputFieldChanges(
303
+ resolved.dataset.transform[0],
304
+ root.transform
305
+ );
306
+ let next = program;
307
+ for (const revision of plan.revisions) {
308
+ if (revision.transform === undefined) continue;
309
+ const policy = findTransformPolicy(revision.transform.type);
310
+ next = next.createDerivedData({
311
+ id: revision.id,
312
+ source: revision.source,
313
+ transform: [revision.transform]
314
+ });
315
+ if (revision.transform.type === "markFilter") {
316
+ next = next.rebindLayerData({
317
+ id: revision.transform.target,
318
+ data: revision.source
319
+ });
320
+ }
321
+ next = next[policy.materializeOp]({ id: revision.id });
322
+ }
323
+
324
+ const consumers = plan.revisions.flatMap(revision =>
325
+ directLayerConsumers(program, revision.old).map(id => ({
326
+ id,
327
+ old: revision.old,
328
+ data: revision.id,
329
+ root: revision.old === root.old
330
+ }))
331
+ );
332
+ for (const consumer of consumers) {
333
+ next = next.rebindLayerData({ id: consumer.id, data: consumer.data });
334
+ }
335
+ for (const consumer of consumers) {
336
+ if (consumer.root) {
337
+ next = rebindLayerOutputFields(next, consumer.id, changes);
338
+ }
339
+ }
340
+
341
+ next = rebindMaterializationDataReferences(next, plan.replacements);
342
+ for (const revision of plan.revisions) {
343
+ if (revision.owner !== undefined) {
344
+ next = next._withMaterializationConfig(
345
+ ["data", revision.family, revision.owner],
346
+ { current: revision.id }
347
+ );
348
+ }
349
+ }
350
+ for (const consumer of consumers) {
351
+ next = applyLayerDataRematerialization(next, consumer.id);
352
+ }
353
+ for (const revision of [...plan.revisions].reverse()) {
354
+ if (retain.has(revision.old)) continue;
355
+ next = next.releaseDerivedData({ id: revision.old });
356
+ }
357
+
358
+ const currentData = plan.replacements.get(program.context.currentData) ??
359
+ program.context.currentData;
360
+ return next._withContext({ ...program.context, currentData });
361
+ }
362
+
363
+ export function buildSourceRevisionPlan(program, source, id) {
364
+ return buildRevisionPlan(program, {
365
+ current: source, dataset: findDataset(program, source), revision: id
366
+ }, undefined, "recompute");
367
+ }
368
+
369
+ function sameRequested(left, right) {
370
+ return JSON.stringify(left) === JSON.stringify(right);
371
+ }
372
+
373
+ export function reviseDerivedData(program, {
374
+ resolved,
375
+ definition,
376
+ dependents = "reject",
377
+ source = resolved.dataset.source
378
+ }) {
379
+ const policy = normalizeDerivedDependentPolicy(dependents);
380
+ const plan = buildRevisionPlan(
381
+ program,
382
+ resolved,
383
+ definition,
384
+ policy,
385
+ source
386
+ );
387
+ applyRevisionPlan(program, resolved, plan);
388
+ return applyRevisionPlan(program, resolved, plan);
389
+ }
390
+
391
+ export const editDerivedData = /* @__PURE__ */ closedAction(
392
+ {
393
+ op: "editDerivedData",
394
+ description: "Atomically revise one standalone derived-data definition."
395
+ }, GENERIC_OPTIONS,
396
+ function (args = {}) {
397
+ if (!Object.hasOwn(args, "target")) {
398
+ throw new TypeError("editDerivedData requires target.");
399
+ }
400
+ if (!isPlainObject(args.definition)) {
401
+ throw new TypeError("editDerivedData definition must be a plain object.");
402
+ }
403
+ for (const forbidden of ["id", "source", "resolved", "current"]) {
404
+ if (Object.hasOwn(args.definition, forbidden)) {
405
+ throw new Error(`editDerivedData definition cannot include ${forbidden}.`);
406
+ }
407
+ }
408
+ const dependents = normalizeDerivedDependentPolicy(args.dependents);
409
+ const resolved = resolveDerivedDataOwner(
410
+ this,
411
+ args.target,
412
+ args.definition.type
413
+ );
414
+ const definition = requestedDatasetTransform(args.definition);
415
+ findTransformPolicy(definition.type).validate(definition);
416
+ const previous = requestedDatasetTransform(resolved.dataset.transform[0]);
417
+ if (sameRequested(definition, previous)) return this;
418
+
419
+ return reviseDerivedData(this, { resolved, definition, dependents });
420
+ }
421
+ );
422
+
423
+ function focusedEditor(op, type, properties) {
424
+ const supported = Object.freeze(["target", "dependents", ...properties]);
425
+ return action(
426
+ { op, description: `Partially revise one standalone ${type} dataset.` },
427
+ function (args = {}) {
428
+ validateKeys(args, supported, op);
429
+ if (!Object.hasOwn(args, "target")) {
430
+ throw new TypeError(`${op} requires target.`);
431
+ }
432
+ const patch = Object.fromEntries(Object.entries(args).filter(
433
+ ([key]) => key !== "target" && key !== "dependents"
434
+ ));
435
+ if (Object.keys(patch).length === 0) {
436
+ throw new Error(`${op} requires at least one transform option.`);
437
+ }
438
+ const undefinedKey = Object.keys(patch).find(
439
+ key => patch[key] === undefined
440
+ );
441
+ if (undefinedKey !== undefined) {
442
+ throw new TypeError(
443
+ `${op} ${undefinedKey} cannot be undefined; omission preserves it.`
444
+ );
445
+ }
446
+ const resolved = resolveDerivedDataOwner(this, args.target, type);
447
+ const definition = normalizeDatasetTransformEdit(
448
+ resolved.dataset.transform[0],
449
+ patch,
450
+ resolved.owner
451
+ );
452
+ return this.editDerivedData({
453
+ target: args.target,
454
+ definition,
455
+ ...(args.dependents === undefined
456
+ ? {}
457
+ : { dependents: args.dependents })
458
+ });
459
+ }
460
+ );
461
+ }
462
+
463
+ export const editComputedData = /* @__PURE__ */ focusedEditor(
464
+ "editComputedData", "computed", ["as", "expression"]
465
+ );
466
+ export const editFilteredData = /* @__PURE__ */ focusedEditor(
467
+ "editFilteredData", "filter", ["field", "oneOf", "predicate", "range"]
468
+ );
469
+ export const editFoldData = /* @__PURE__ */ focusedEditor(
470
+ "editFoldData", "fold", ["fields", "as"]
471
+ );
472
+ export const editSummaryData = /* @__PURE__ */ focusedEditor(
473
+ "editSummaryData", "summary", ["groupBy", "aggregates", "members", "weight"]
474
+ );
475
+ export const editBinData = /* @__PURE__ */ focusedEditor(
476
+ "editBinData", "bin", [
477
+ "field", "maxBins", "step", "boundaries", "extent", "nice", "zero",
478
+ "includeEmpty", "members", "as", "weight"
479
+ ]
480
+ );
481
+ export const editTimeUnitData = /* @__PURE__ */ focusedEditor(
482
+ "editTimeUnitData", "timeUnit", [
483
+ "field", "unit", "as", "temporalUnit", "timeZone", "weekStartsOn",
484
+ "weekRule"
485
+ ]
486
+ );
487
+ export const editWindowData = /* @__PURE__ */ focusedEditor(
488
+ "editWindowData", "window", [
489
+ "partitionBy", "sortBy", "operations", "temporalUnit"
490
+ ]
491
+ );
492
+ export const editDensityData = /* @__PURE__ */ focusedEditor(
493
+ "editDensityData", "density", [
494
+ "field", "groupBy", "bandwidth", "extent", "steps", "kernel",
495
+ "normalization", "as", "weight"
496
+ ]
497
+ );
498
+ export const editStackData = /* @__PURE__ */ focusedEditor(
499
+ "editStackData", "stack", ["category", "group", "value", "mode", "as"]
500
+ );
501
+ export const editRegressionData = /* @__PURE__ */ focusedEditor(
502
+ "editRegressionData", "regression", [
503
+ "x", "y", "groupBy", "method", "degree", "span", "confidenceMethod",
504
+ "level", "confidence", "interval"
505
+ ]
506
+ );
507
+ export const editIntervalData = /* @__PURE__ */ focusedEditor(
508
+ "editIntervalData", "interval", [
509
+ "field", "groupBy", "center", "extent", "method", "level", "as"
510
+ ]
511
+ );
512
+ export const editECDFData = /* @__PURE__ */ focusedEditor(
513
+ "editECDFData", "ecdf", [
514
+ "field", "groupBy", "weight", "missing", "as"
515
+ ]
516
+ );
517
+ export const editNormalizedData = /* @__PURE__ */ focusedEditor(
518
+ "editNormalizedData", "normalize", [
519
+ "field", "as", "groupBy", "method", "variance", "zeroDenominator",
520
+ "baseline", "sortBy"
521
+ ]
522
+ );
523
+ export const editCompleteData = /* @__PURE__ */ focusedEditor(
524
+ "editCompleteData", "complete", [
525
+ "key", "groupBy", "values", "sequence", "fill", "members"
526
+ ]
527
+ );
528
+ export const editImputedData = /* @__PURE__ */ focusedEditor(
529
+ "editImputedData", "impute", [
530
+ "fields", "groupBy", "sortBy", "method", "value", "edges", "maxGap"
531
+ ]
532
+ );
533
+
534
+ export const EDIT_DERIVED_DATA_ACTIONS = Object.freeze({
535
+ editDerivedData,
536
+ editComputedData,
537
+ editFilteredData,
538
+ editFoldData,
539
+ editSummaryData,
540
+ editBinData,
541
+ editTimeUnitData,
542
+ editWindowData,
543
+ editDensityData,
544
+ editStackData,
545
+ editRegressionData,
546
+ editIntervalData,
547
+ editECDFData,
548
+ editNormalizedData,
549
+ editCompleteData,
550
+ editImputedData
551
+ });
@@ -1,6 +1,6 @@
1
- import { action } from "../../core/action.js";
1
+ import { closedAction } from "../../core/action.js";
2
2
  import { validateUserId } from "../../core/identifiers.js";
3
- import { validateKeys } from "../../core/validation.js";
3
+
4
4
  import { withPreviewDatasetValues } from
5
5
  "../primitives/semanticAction.js";
6
6
  import {
@@ -22,6 +22,7 @@ import {
22
22
  import {
23
23
  hasDataset,
24
24
  findDataset,
25
+ resolveDatasetReference,
25
26
  requireLayer,
26
27
  resolveEligibleLayer
27
28
  } from "../../selectors/index.js";
@@ -49,10 +50,9 @@ function retainedHistogramBoundaries(layer, items) {
49
50
  return boundaries.length < 2 ? undefined : boundaries;
50
51
  }
51
52
 
52
- export const materializeFilteredData = action(
53
- { op: "materializeFilteredData", description: "Materialize one filtered derived dataset." },
53
+ export const materializeFilteredData = /* @__PURE__ */ closedAction(
54
+ { op: "materializeFilteredData", description: "Materialize one filtered derived dataset." }, MATERIALIZE_OPTIONS,
54
55
  function (args = {}) {
55
- validateKeys(args, MATERIALIZE_OPTIONS, "materializeFilteredData");
56
56
  const { id, source, transform } = requireDerivedDataset(
57
57
  this,
58
58
  args.id,
@@ -65,33 +65,42 @@ export const materializeFilteredData = action(
65
65
  }
66
66
  );
67
67
 
68
- export const filterData = action(
69
- { op: "filterData", description: "Create a named dataset from one field filter." },
68
+ export const filterData = /* @__PURE__ */ closedAction(
69
+ { op: "filterData", description: "Create a named dataset from one field filter." }, OPTIONS,
70
70
  function (args = {}) {
71
- validateKeys(args, OPTIONS, "filterData");
72
71
  const id = validateUserId(args.id, "Filtered dataset id");
73
- const source = validateUserId(
72
+ const requestedSource = validateUserId(
74
73
  args.source ?? this.context.currentData,
75
74
  "Source dataset id"
76
75
  );
76
+ const source = resolveDatasetReference(
77
+ this,
78
+ requestedSource,
79
+ "Filter source dataset"
80
+ ).id;
77
81
  const transform = normalizeFilterTransform(args);
78
- return this
82
+ const next = this
79
83
  .createDerivedData({
80
84
  id,
81
85
  source,
82
86
  transform: [transform]
83
87
  })
84
88
  .materializeFilteredData({ id });
89
+ return this.actionStack.length === 1
90
+ ? next._withMaterializationConfig(
91
+ ["data", "filter", id],
92
+ { current: id }
93
+ )
94
+ : next;
85
95
  }
86
96
  );
87
97
 
88
- export const materializeMarkFilteredData = action(
98
+ export const materializeMarkFilteredData = /* @__PURE__ */ closedAction(
89
99
  {
90
100
  op: "materializeMarkFilteredData",
91
101
  description: "Materialize member rows retained by one final-item mark selection."
92
- },
102
+ }, MATERIALIZE_OPTIONS,
93
103
  function (args = {}) {
94
- validateKeys(args, MATERIALIZE_OPTIONS, "materializeMarkFilteredData");
95
104
  const { id, dataset, source, transform } = requireDerivedDataset(
96
105
  this,
97
106
  args.id,
@@ -158,13 +167,12 @@ export const materializeMarkFilteredData = action(
158
167
  }
159
168
  );
160
169
 
161
- export const materializeEmptyMark = action(
170
+ export const materializeEmptyMark = /* @__PURE__ */ closedAction(
162
171
  {
163
172
  op: "materializeEmptyMark",
164
173
  description: "Clear one active empty-filter mark without changing its domains."
165
- },
174
+ }, EMPTY_MARK_OPTIONS,
166
175
  function (args = {}) {
167
- validateKeys(args, EMPTY_MARK_OPTIONS, "materializeEmptyMark");
168
176
  const id = validateUserId(args.id, "Empty mark id");
169
177
  const layer = requireLayer(this, id);
170
178
  const owner = this.markConfigs[id]?.markFilter;
@@ -249,13 +257,12 @@ function sameSelector(left, right) {
249
257
  return JSON.stringify(left) === JSON.stringify(right);
250
258
  }
251
259
 
252
- export const removeMarkFilter = action(
260
+ export const removeMarkFilter = /* @__PURE__ */ closedAction(
253
261
  {
254
262
  op: "removeMarkFilter",
255
263
  description: "Remove one active final-item filter and restore its source."
256
- },
264
+ }, REMOVE_MARK_FILTER_OPTIONS,
257
265
  function (args = {}) {
258
- validateKeys(args, REMOVE_MARK_FILTER_OPTIONS, "removeMarkFilter");
259
266
  const target = args.target === undefined
260
267
  ? undefined
261
268
  : validateUserId(args.target, "Mark filter target id");
@@ -268,13 +275,12 @@ export const removeMarkFilter = action(
268
275
  }
269
276
  );
270
277
 
271
- export const filterMarks = action(
278
+ export const filterMarks = /* @__PURE__ */ closedAction(
272
279
  {
273
280
  op: "filterMarks",
274
281
  description: "Retain selected final mark items through immutable derived data."
275
- },
282
+ }, MARK_OPTIONS,
276
283
  function (args = {}) {
277
- validateKeys(args, MARK_OPTIONS, "filterMarks");
278
284
  if (args.mode !== undefined && !["replace", "compose"].includes(args.mode)) {
279
285
  throw new Error(`Unknown mark filter mode "${args.mode}".`);
280
286
  }
@@ -6,14 +6,14 @@ import { derivedCreator, derivedMaterializer } from "./shared.js";
6
6
 
7
7
  const OPTIONS = Object.freeze(["id", "source", "fields", "as"]);
8
8
 
9
- export const materializeFoldData = derivedMaterializer(
9
+ export const materializeFoldData = /* @__PURE__ */ derivedMaterializer(
10
10
  "materializeFoldData",
11
11
  "Materialize one immutable wide-to-long dataset.",
12
12
  "fold",
13
13
  deriveFoldRows
14
14
  );
15
15
 
16
- export const createFoldData = derivedCreator(
16
+ export const createFoldData = /* @__PURE__ */ derivedCreator(
17
17
  "createFoldData",
18
18
  "Create reusable key/value rows from selected wide fields.",
19
19
  OPTIONS,