ggaction 0.0.16 → 0.0.18
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.
- package/CHANGELOG.md +66 -0
- package/README.md +3 -2
- package/knowledge/action-card.schema.json +1 -1
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +10 -4
- package/src/BasicChartProgram.js +1 -1
- package/src/accessibility.js +3 -2
- package/src/actions/boxPlots/create.js +14 -4
- package/src/actions/boxPlots/edit.js +39 -33
- package/src/actions/boxPlots/materialize.js +7 -3
- package/src/actions/boxPlots/options.js +37 -11
- package/src/actions/canvas/actions.js +70 -95
- package/src/actions/canvas/fitting.js +16 -1
- package/src/actions/canvas/index.js +3 -0
- package/src/actions/canvas/plotBorder.js +28 -0
- package/src/actions/charts/area.js +2 -2
- package/src/actions/charts/bar.js +34 -7
- package/src/actions/charts/ecdf.js +4 -18
- package/src/actions/charts/endpoints.js +4 -18
- package/src/actions/charts/heatmap.js +4 -4
- package/src/actions/charts/index.js +2 -0
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/pie.js +3 -47
- package/src/actions/charts/radial.js +34 -12
- package/src/actions/charts/scatter.js +16 -2
- package/src/actions/charts/shared.js +2 -2
- package/src/actions/charts/text.js +35 -0
- package/src/actions/composition/actions.js +6 -2
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/box.js +2 -1
- package/src/actions/data/create.js +18 -5
- package/src/actions/data/density.js +1 -1
- package/src/actions/data/derived.js +15 -1
- package/src/actions/data/edit.js +20 -7
- package/src/actions/data/filter.js +15 -4
- package/src/actions/data/index.js +3 -0
- package/src/actions/data/interval.js +2 -0
- package/src/actions/data/regression.js +6 -2
- package/src/actions/data/revise.js +37 -5
- package/src/actions/data/shared.js +33 -4
- package/src/actions/data/sort.js +21 -0
- package/src/actions/data/stack.js +3 -1
- package/src/actions/data/summary.js +1 -1
- package/src/actions/encodings/angle.js +4 -1
- package/src/actions/encodings/appearance.js +43 -31
- package/src/actions/encodings/barWidth.js +36 -11
- package/src/actions/encodings/channels.js +0 -1
- package/src/actions/encodings/color/continuous.js +5 -2
- package/src/actions/encodings/color/index.js +5 -2
- package/src/actions/encodings/color/policy.js +10 -3
- package/src/actions/encodings/density/resolve.js +1 -6
- package/src/actions/encodings/offset.js +4 -1
- package/src/actions/encodings/position/policies/arc.js +12 -10
- package/src/actions/encodings/position/policies/area.js +3 -3
- package/src/actions/encodings/position/policies/bar.js +15 -6
- package/src/actions/encodings/position/policies/line.js +17 -6
- package/src/actions/encodings/position/resolve.js +9 -3
- package/src/actions/encodings/ranged.js +23 -1
- package/src/actions/encodings/remove.js +3 -2
- package/src/actions/encodings/ruleAppearance.js +4 -1
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/stroke.js +4 -1
- package/src/actions/encodings/strokeDash.js +6 -1
- package/src/actions/encodings/text.js +12 -4
- package/src/actions/errorBands/create.js +2 -2
- package/src/actions/errorBands/edit.js +2 -1
- package/src/actions/facets/actions.js +158 -240
- package/src/actions/gradientPlots/resolve.js +2 -17
- package/src/actions/guides/applicability.js +1 -1
- package/src/actions/guides/axes/axis.js +16 -4
- package/src/actions/guides/axes/edit.js +4 -4
- package/src/actions/guides/axes/labels.js +120 -46
- package/src/actions/guides/axes/lines.js +6 -5
- package/src/actions/guides/axes/parallel/lifecycle.js +1 -1
- package/src/actions/guides/axes/parallel/policy.js +8 -7
- package/src/actions/guides/axes/remove.js +3 -2
- package/src/actions/guides/axes/tickGroups.js +5 -2
- package/src/actions/guides/axes/ticks.js +6 -3
- package/src/actions/guides/axes/titles.js +71 -26
- package/src/actions/guides/basic.js +2 -0
- package/src/actions/guides/facadeLegend.js +10 -2
- package/src/actions/guides/grids/grid.js +7 -1
- package/src/actions/guides/grids/resolve.js +59 -12
- package/src/actions/guides/legends/blocks.js +3 -1
- package/src/actions/guides/legends/categorical/actions.js +70 -17
- package/src/actions/guides/legends/categorical/components.js +12 -8
- package/src/actions/guides/legends/categorical/index.js +6 -0
- package/src/actions/guides/legends/categorical/layout.js +37 -8
- package/src/actions/guides/legends/categorical/options.js +31 -20
- package/src/actions/guides/legends/categorical/resolve.js +2 -2
- package/src/actions/guides/legends/categorical/symbols.js +18 -12
- package/src/actions/guides/legends/categorical/validation.js +1 -6
- package/src/actions/guides/legends/continuous/colorSymbols.js +1 -0
- package/src/actions/guides/legends/continuous/common.js +1 -447
- package/src/actions/guides/legends/continuous/gradient.js +47 -34
- package/src/actions/guides/legends/continuous/interval.js +4 -4
- package/src/actions/guides/legends/continuous/opacity.js +19 -12
- package/src/actions/guides/legends/edit.js +1 -604
- package/src/actions/guides/legends/focused.js +2 -2
- package/src/actions/guides/legends/group.js +1 -0
- package/src/actions/guides/legends/lane.js +1 -516
- package/src/actions/guides/legends/remove.js +3 -0
- package/src/actions/guides/legends/sampling.js +1 -1
- package/src/actions/guides/legends/size.js +51 -43
- package/src/actions/guides/legends/strokeWidth.js +11 -29
- package/src/actions/guides/legends/target.js +3 -3
- package/src/actions/guides/legends/transition.js +21 -7
- package/src/actions/guides/polar/axes/labels.js +4 -2
- package/src/actions/guides/polar/axes/titles.js +7 -5
- package/src/actions/guides/polar/resolve.js +6 -14
- package/src/actions/index.js +16 -16
- package/src/actions/marks/arc/actions.js +3 -10
- package/src/actions/marks/area/actions.js +8 -5
- package/src/actions/marks/area/materialize.js +8 -9
- package/src/actions/marks/bar/create.js +17 -4
- package/src/actions/marks/bar/edit.js +18 -7
- package/src/actions/marks/bar/materialize.js +9 -6
- package/src/actions/marks/line/actions.js +11 -4
- package/src/actions/marks/line/materialize.js +9 -5
- package/src/actions/marks/point/create.js +8 -1
- package/src/actions/marks/point/edit.js +13 -6
- package/src/actions/marks/point/materialize.js +4 -2
- package/src/actions/marks/rect/actions.js +26 -11
- package/src/actions/marks/remove.js +2 -0
- package/src/actions/marks/rule/actions.js +31 -12
- package/src/actions/marks/text/actions.js +42 -11
- package/src/actions/marks/text/layout.js +3 -0
- package/src/actions/marks/tick/actions.js +23 -8
- package/src/actions/primitives/semanticAction.js +37 -3
- package/src/actions/primitives/semanticValidation/dataset.js +13 -1
- package/src/actions/primitives/semanticValidation/layer.js +26 -2
- package/src/actions/primitives/semanticValidation/scale.js +6 -0
- package/src/actions/regression/create.js +26 -5
- package/src/actions/regression/edit.js +50 -15
- package/src/actions/regression/follow.js +22 -0
- package/src/actions/regression/resolve.js +2 -6
- package/src/actions/scales/channels.js +20 -15
- package/src/actions/scales/consumers/common.js +2 -1
- package/src/actions/scales/consumers/families.js +16 -5
- package/src/actions/scales/create.js +10 -4
- package/src/actions/scales/definitions.js +1 -425
- package/src/actions/scales/edit.js +3 -3
- package/src/actions/scales/editPolicy.js +9 -3
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/parallel.js +1 -1
- package/src/actions/scales/patch.js +1 -1
- package/src/actions/scales/preview.js +49 -2
- package/src/actions/scales/quantitativeColor.js +3 -3
- package/src/actions/selection/actions.js +1 -13
- package/src/actions/theme/reconcile.js +83 -27
- package/src/actions/theme/state.js +12 -0
- package/src/actions/titles/actions.js +5 -18
- package/src/actions/titles/resolve.js +41 -19
- package/src/actions/violinPlots/edit.js +3 -20
- package/src/core/action.js +3 -2
- package/src/core/compositionState.js +4 -1
- package/src/core/font.js +9 -2
- package/src/core/optionRequirements.js +1 -0
- package/src/core/programState.js +5 -1
- package/src/core/resourceReferences.js +3 -0
- package/src/core/textMetricProfile.js +3 -2
- package/src/core/textMetrics.js +41 -4
- package/src/core/validation.js +26 -0
- package/src/core/vocabulary.js +4 -0
- package/src/grammar/aggregate.js +4 -18
- package/src/grammar/arcs.js +1 -386
- package/src/grammar/areaPaint.js +10 -0
- package/src/grammar/areaSeries.js +18 -10
- package/src/grammar/bars/geometry.js +30 -3
- package/src/grammar/bars/policy.js +12 -1
- package/src/grammar/bin.js +41 -22
- package/src/grammar/bin2d.js +1 -6
- package/src/grammar/boxPlot.js +56 -9
- package/src/grammar/categoryOrder.js +1 -6
- package/src/grammar/complete.js +15 -19
- package/src/grammar/computed.js +92 -25
- package/src/grammar/curveCommands.js +29 -16
- package/src/grammar/datasetSchema.js +382 -0
- package/src/grammar/density.js +53 -5
- package/src/grammar/displayLabels.js +28 -0
- package/src/grammar/ecdf.js +2 -9
- package/src/grammar/facets/dependencies.js +6 -2
- package/src/grammar/facets/guides.js +5 -0
- package/src/grammar/facets/index.js +2 -5
- package/src/grammar/filter.js +133 -39
- package/src/grammar/fold.js +2 -12
- package/src/grammar/gradientProfile.js +1 -6
- package/src/grammar/impute.js +21 -48
- package/src/grammar/interval.js +34 -10
- package/src/grammar/itemMissing.js +59 -0
- package/src/grammar/legendGradient.js +18 -0
- package/src/grammar/legendGroups.js +1 -0
- package/src/grammar/legendTitleStyle.js +22 -0
- package/src/grammar/lineSeries.js +32 -17
- package/src/grammar/markLabels.js +5 -2
- package/src/grammar/normalize.js +4 -54
- package/src/grammar/numeric.js +1 -290
- package/src/grammar/parallelCoordinates.js +1 -6
- package/src/grammar/parseTime.js +41 -0
- package/src/grammar/pathCommands.js +11 -4
- package/src/grammar/polar.js +1 -184
- package/src/grammar/positionCompatibility.js +3 -3
- package/src/grammar/regression/derive.js +63 -16
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/models.js +116 -59
- package/src/grammar/regression/parameters.js +69 -6
- package/src/grammar/roundedRect.js +42 -54
- package/src/grammar/scales/appearance.js +1 -206
- package/src/grammar/scales/color.js +1 -382
- package/src/grammar/scales/colorConsumers.js +5 -2
- package/src/grammar/scales/definition.js +1 -190
- package/src/grammar/scales/discretized.js +1 -195
- package/src/grammar/scales/size.js +1 -456
- package/src/grammar/scales/temporal.js +2 -13
- package/src/grammar/scales/transformed.js +1 -383
- package/src/grammar/scales/types.js +1 -1
- package/src/grammar/scales/validation.js +1 -66
- package/src/grammar/schemas/concreteGraphic.js +11 -0
- package/src/grammar/schemas/graphic.js +3 -0
- package/src/grammar/schemas/semanticPath.js +5 -3
- package/src/grammar/sort.js +88 -0
- package/src/grammar/stack.js +2 -10
- package/src/grammar/statisticalReference.js +1 -6
- package/src/grammar/summary.js +120 -45
- package/src/grammar/text.js +19 -0
- package/src/grammar/timeUnit.js +16 -7
- package/src/grammar/transformKeys.js +47 -0
- package/src/grammar/transformTopology.js +1 -0
- package/src/grammar/transforms.js +25 -12
- package/src/grammar/valueFormat.js +17 -5
- package/src/grammar/weightedStatistics.js +1 -6
- package/src/grammar/window.js +4 -58
- package/src/inspection.js +495 -0
- package/src/inspectionDescriptors.js +3 -0
- package/src/layout/canvas.js +1 -110
- package/src/layout/facets.js +1 -244
- package/src/layout/labels.js +1 -0
- package/src/layout/legend.js +13 -0
- package/src/layout/legendGroup.js +1 -0
- package/src/layout/legendItems.js +59 -15
- package/src/layout/legendLane.js +1 -441
- package/src/layout/legendText.js +1 -0
- package/src/layout/title.js +35 -13
- package/src/materialization/bars/aggregate.js +11 -5
- package/src/materialization/bars/histogram.js +4 -1
- package/src/materialization/bars/ranged.js +19 -6
- package/src/materialization/bars/resolve.js +26 -3
- package/src/materialization/coordinateBounds.js +8 -0
- package/src/materialization/dependencies.js +4 -44
- package/src/materialization/facetGuides/layout.js +1 -0
- package/src/materialization/facetGuides/placement.js +19 -8
- package/src/materialization/facetGuides/preparation.js +15 -7
- package/src/materialization/facetHeaders.js +1 -347
- package/src/materialization/facets.js +1 -185
- package/src/materialization/graphicHierarchy.js +1 -1
- package/src/materialization/guides/layout.js +1 -57
- package/src/materialization/guides/resources.js +1 -123
- package/src/materialization/layout.js +53 -0
- package/src/materialization/legendGroups.js +1 -0
- package/src/materialization/legendText.js +1 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/capabilities.js +5 -6
- package/src/materialization/rect.js +1 -1
- package/src/materialization/revisionIdentity.js +19 -0
- package/src/materialization/scaleGuideDependencies.js +1 -1
- package/src/materialization/scales/map.js +3 -0
- package/src/materialization/scales/policies/arc.js +8 -7
- package/src/materialization/scales/policies/bar.js +19 -7
- package/src/materialization/scales/policies/series.js +2 -1
- package/src/materialization/scales/resolve.js +72 -7
- package/src/materialization/selection/items/arc.js +1 -1
- package/src/materialization/selection/items/bar.js +7 -6
- package/src/materialization/selection/items/path.js +7 -6
- package/src/materialization/selection/items/rect.js +2 -2
- package/src/materialization/text.js +17 -6
- package/src/materialization/textLines.js +15 -0
- package/src/persistence/codec.js +37 -6
- package/src/persistence/validation.js +47 -0
- package/src/persistence.js +94 -8
- package/src/renderers/canvas/circle.js +6 -3
- package/src/renderers/canvas/fill.js +7 -1
- package/src/renderers/canvas/index.js +2 -1
- package/src/renderers/canvas/line.js +1 -0
- package/src/renderers/canvas/path.js +1 -1
- package/src/renderers/canvas/rect.js +11 -8
- package/src/renderers/canvas/stroke.js +2 -1
- package/src/renderers/canvas/text.js +5 -3
- package/src/renderers/svg.js +4 -1
- package/src/selectors/layers.js +19 -0
- package/src/theme/defaults.js +8 -2
- package/src/version.js +1 -1
- package/types/basic.d.ts +1 -0
- package/types/index.d.ts +7 -0
- package/types/inspection.d.ts +17 -0
- package/types/program.d.ts +494 -112
package/src/grammar/computed.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {parseTimeValue,validateParseTimeFormat} from "./parseTime.js";
|
|
2
|
+
import { rejectUnknownProperties as rejectUnknownKeys } from "../core/validation.js";
|
|
3
|
+
import { requireStringValue as requireField } from "../core/validation.js";
|
|
1
4
|
import { cloneAndFreeze, isPlainObject } from "../core/immutable.js";
|
|
2
5
|
|
|
3
6
|
const TRANSFORM_KEYS = Object.freeze(["type", "as", "expression"]);
|
|
@@ -7,23 +10,12 @@ const NUMERIC_UNARY = new Set(["negate", "absolute", "log", "sqrt"]);
|
|
|
7
10
|
const BOOLEAN_UNARY = new Set(["not", "isNull"]);
|
|
8
11
|
const LOGICAL = new Set(["and", "or"]);
|
|
9
12
|
const VARIADIC = new Set(["coalesce", "concat"]);
|
|
13
|
+
const RESERVED_PATH_KEYS = new Set(["__proto__", "prototype", "constructor"]);
|
|
10
14
|
const MAX_DEPTH = 16;
|
|
11
15
|
const MAX_NODES = 128;
|
|
12
16
|
const MAX_WORK = 10_000_000;
|
|
13
17
|
|
|
14
|
-
function rejectUnknownKeys(value, supported, label) {
|
|
15
|
-
const unknown = Object.keys(value).find(key => !supported.includes(key));
|
|
16
|
-
if (unknown !== undefined) {
|
|
17
|
-
throw new Error(`Unknown ${label} property "${unknown}".`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
18
|
|
|
21
|
-
function requireField(value, label) {
|
|
22
|
-
if (typeof value !== "string" || value.length === 0) {
|
|
23
|
-
throw new TypeError(`${label} must be a non-empty string.`);
|
|
24
|
-
}
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
19
|
|
|
28
20
|
function validateConstant(value) {
|
|
29
21
|
if (value === null || typeof value === "string" || typeof value === "boolean") return;
|
|
@@ -50,7 +42,7 @@ function validateOperands(expression, minimum) {
|
|
|
50
42
|
}
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
function inspectExpression(expression, depth, state) {
|
|
45
|
+
function inspectExpression(expression, depth, state, optionalPathRoot = false) {
|
|
54
46
|
if (!isPlainObject(expression)) {
|
|
55
47
|
throw new TypeError("Computed expression nodes must be plain objects.");
|
|
56
48
|
}
|
|
@@ -72,7 +64,9 @@ function inspectExpression(expression, depth, state) {
|
|
|
72
64
|
}
|
|
73
65
|
if (kinds[0] === "field") {
|
|
74
66
|
rejectUnknownKeys(expression, ["field"], "computed field expression");
|
|
75
|
-
|
|
67
|
+
const field = requireField(expression.field, "Computed expression field");
|
|
68
|
+
state.fields.add(field);
|
|
69
|
+
if (!optionalPathRoot) state.requiredFields.add(field);
|
|
76
70
|
return;
|
|
77
71
|
}
|
|
78
72
|
if (kinds[0] === "constant") {
|
|
@@ -88,7 +82,39 @@ function inspectExpression(expression, depth, state) {
|
|
|
88
82
|
inspectExpression(expression.right, depth + 1, state);
|
|
89
83
|
return;
|
|
90
84
|
}
|
|
91
|
-
if (
|
|
85
|
+
if (expression.op === "getPath") {
|
|
86
|
+
rejectUnknownKeys(expression, ["op", "operand", "path"], "computed path expression");
|
|
87
|
+
if (!isPlainObject(expression.operand) ||
|
|
88
|
+
Object.keys(expression.operand).length !== 1 ||
|
|
89
|
+
!Object.hasOwn(expression.operand, "field")) {
|
|
90
|
+
throw new TypeError("Computed getPath operand must be a literal field reference.");
|
|
91
|
+
}
|
|
92
|
+
if (!Array.isArray(expression.path) || expression.path.length < 1 ||
|
|
93
|
+
expression.path.length > 8 || !Array.from(expression.path).every(key =>
|
|
94
|
+
typeof key === "string" && key.length > 0 && !RESERVED_PATH_KEYS.has(key))) {
|
|
95
|
+
throw new TypeError("Computed getPath path must contain 1 to 8 nonempty, nonreserved string keys.");
|
|
96
|
+
}
|
|
97
|
+
inspectExpression(expression.operand, depth + 1, state, true);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (expression.op === "replaceLiteral") {
|
|
101
|
+
rejectUnknownKeys(expression, ["op", "operand", "search", "replacement"], "computed literal replacement");
|
|
102
|
+
requireNode(expression, "operand", expression.op);
|
|
103
|
+
if (typeof expression.search !== "string" || expression.search.length === 0 ||
|
|
104
|
+
typeof expression.replacement !== "string") {
|
|
105
|
+
throw new TypeError("Computed replaceLiteral requires a nonempty string search and string replacement.");
|
|
106
|
+
}
|
|
107
|
+
inspectExpression(expression.operand, depth + 1, state);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (expression.op === "parseTime") {
|
|
111
|
+
rejectUnknownKeys(expression, ["op", "operand", "format"], "computed time expression");
|
|
112
|
+
requireNode(expression, "operand", expression.op);
|
|
113
|
+
validateParseTimeFormat(expression.format);
|
|
114
|
+
inspectExpression(expression.operand, depth + 1, state);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (NUMERIC_UNARY.has(expression.op) || BOOLEAN_UNARY.has(expression.op) || expression.op === "toNumber") {
|
|
92
118
|
rejectUnknownKeys(expression, ["op", "operand"], "computed unary expression");
|
|
93
119
|
requireNode(expression, "operand", expression.op);
|
|
94
120
|
inspectExpression(expression.operand, depth + 1, state);
|
|
@@ -131,7 +157,7 @@ function inspectComputedTransform(transform) {
|
|
|
131
157
|
throw new Error(`Unsupported computed transform "${transform.type}".`);
|
|
132
158
|
}
|
|
133
159
|
requireField(transform.as, "Computed output field");
|
|
134
|
-
const state = { nodes: 0, fields: new Set(), ancestors: new WeakSet() };
|
|
160
|
+
const state = { nodes: 0, fields: new Set(), requiredFields: new Set(), ancestors: new WeakSet() };
|
|
135
161
|
inspectExpression(transform.expression, 1, state);
|
|
136
162
|
return state;
|
|
137
163
|
}
|
|
@@ -233,6 +259,27 @@ function compare(left, right, operation, rowIndex) {
|
|
|
233
259
|
: order >= 0;
|
|
234
260
|
}
|
|
235
261
|
|
|
262
|
+
function ownDataProperty(record, key, rowIndex) {
|
|
263
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
264
|
+
if (descriptor === undefined) return undefined;
|
|
265
|
+
if (!Object.hasOwn(descriptor, "value")) {
|
|
266
|
+
throw new TypeError(`Computed getPath cannot read an accessor at row ${rowIndex}.`);
|
|
267
|
+
}
|
|
268
|
+
return descriptor.value;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function nestedScalar(expression, row, rowIndex) {
|
|
272
|
+
let value = ownDataProperty(row, expression.operand.field, rowIndex);
|
|
273
|
+
for (const key of expression.path) {
|
|
274
|
+
if (value === undefined || value === null) return null;
|
|
275
|
+
if (!isPlainObject(value)) {
|
|
276
|
+
throw new TypeError(`Computed getPath requires plain records at row ${rowIndex}.`);
|
|
277
|
+
}
|
|
278
|
+
value = ownDataProperty(value, key, rowIndex);
|
|
279
|
+
}
|
|
280
|
+
return scalar(value, "Computed getPath result", rowIndex);
|
|
281
|
+
}
|
|
282
|
+
|
|
236
283
|
function evaluate(expression, row, rowIndex) {
|
|
237
284
|
if (Object.hasOwn(expression, "field")) {
|
|
238
285
|
return scalar(
|
|
@@ -243,6 +290,16 @@ function evaluate(expression, row, rowIndex) {
|
|
|
243
290
|
}
|
|
244
291
|
if (Object.hasOwn(expression, "constant")) return expression.constant;
|
|
245
292
|
const operation = expression.op;
|
|
293
|
+
if (operation === "getPath") return nestedScalar(expression, row, rowIndex);
|
|
294
|
+
if (operation === "replaceLiteral") {
|
|
295
|
+
const value = evaluate(expression.operand, row, rowIndex);
|
|
296
|
+
if (typeof value !== "string") {
|
|
297
|
+
throw new TypeError(`Computed replaceLiteral requires a string at row ${rowIndex}.`);
|
|
298
|
+
}
|
|
299
|
+
const index = value.indexOf(expression.search);
|
|
300
|
+
return index < 0 ? value
|
|
301
|
+
: value.slice(0, index) + expression.replacement + value.slice(index + expression.search.length);
|
|
302
|
+
}
|
|
246
303
|
if (operation === "if") {
|
|
247
304
|
return boolean(evaluate(expression.condition, row, rowIndex), operation, rowIndex)
|
|
248
305
|
? evaluate(expression.then, row, rowIndex)
|
|
@@ -282,6 +339,17 @@ function evaluate(expression, row, rowIndex) {
|
|
|
282
339
|
? value === null
|
|
283
340
|
: !boolean(value, operation, rowIndex);
|
|
284
341
|
}
|
|
342
|
+
if (operation === "parseTime") {
|
|
343
|
+
return parseTimeValue(evaluate(expression.operand, row, rowIndex), expression.format);
|
|
344
|
+
}
|
|
345
|
+
if (operation === "toNumber") {
|
|
346
|
+
const value = evaluate(expression.operand, row, rowIndex);
|
|
347
|
+
if (typeof value === "number") return number(value, operation, rowIndex);
|
|
348
|
+
if (typeof value !== "string" || !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(value.trim())) {
|
|
349
|
+
throw new TypeError(`Computed toNumber requires a finite number or decimal string at row ${rowIndex}.`);
|
|
350
|
+
}
|
|
351
|
+
return number(Number(value.trim()), operation, rowIndex);
|
|
352
|
+
}
|
|
285
353
|
if (NUMERIC_UNARY.has(operation)) {
|
|
286
354
|
const value = number(evaluate(expression.operand, row, rowIndex), operation, rowIndex);
|
|
287
355
|
if (operation === "log" && value <= 0) {
|
|
@@ -318,7 +386,7 @@ function evaluate(expression, row, rowIndex) {
|
|
|
318
386
|
}
|
|
319
387
|
|
|
320
388
|
export function deriveComputedRows(rows, transform) {
|
|
321
|
-
const { nodes, fields } = inspectComputedTransform(transform);
|
|
389
|
+
const { nodes, fields, requiredFields } = inspectComputedTransform(transform);
|
|
322
390
|
if (rows.length * nodes > MAX_WORK) {
|
|
323
391
|
throw new RangeError(`Computed work cannot exceed ${MAX_WORK} row-nodes.`);
|
|
324
392
|
}
|
|
@@ -326,15 +394,14 @@ export function deriveComputedRows(rows, transform) {
|
|
|
326
394
|
throw new Error(`Computed output field "${transform.as}" already exists.`);
|
|
327
395
|
}
|
|
328
396
|
if (rows.length > 0) {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
);
|
|
397
|
+
for (const field of fields) {
|
|
398
|
+
const knownOptional = !requiredFields.has(field) && rows.some(row => Object.hasOwn(row, field));
|
|
399
|
+
rows.forEach((row, index) => {
|
|
400
|
+
if (!knownOptional && !Object.hasOwn(row, field)) {
|
|
401
|
+
throw new Error(`Computed source does not contain field "${field}" at row ${index}.`);
|
|
335
402
|
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
338
405
|
}
|
|
339
406
|
let outputType;
|
|
340
407
|
return rows.map((row, index) => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { freezeOwned, isPlainObject } from "../core/immutable.js";
|
|
2
|
-
import {
|
|
3
|
-
import { buildLinearPathCommands } from "./pathCommands.js";
|
|
2
|
+
import { buildLinearPathCommands, validatePathCommandCount } from "./pathCommands.js";
|
|
4
3
|
|
|
5
4
|
export const CURVE_INTERPOLATIONS = Object.freeze([
|
|
6
5
|
"linear",
|
|
@@ -42,6 +41,15 @@ function validatePoints(points) {
|
|
|
42
41
|
return points;
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
export function validateCardinalTension(tension, curve) {
|
|
45
|
+
if (tension === undefined) return 0;
|
|
46
|
+
if (curve !== "cardinal") throw new Error("Line tension requires cardinal interpolation.");
|
|
47
|
+
if (!Number.isFinite(tension) || tension < 0 || tension > 1) {
|
|
48
|
+
throw new RangeError("Cardinal tension must be between 0 and 1.");
|
|
49
|
+
}
|
|
50
|
+
return tension;
|
|
51
|
+
}
|
|
52
|
+
|
|
45
53
|
export function validateCurveInterpolation(curve) {
|
|
46
54
|
if (!CURVE_INTERPOLATIONS.includes(curve)) {
|
|
47
55
|
throw new Error(`Unsupported curve interpolation "${curve}".`);
|
|
@@ -134,7 +142,7 @@ function buildBasisCommands(points) {
|
|
|
134
142
|
return ownCommands(commands);
|
|
135
143
|
}
|
|
136
144
|
|
|
137
|
-
function buildCardinalCommands(points) {
|
|
145
|
+
function buildCardinalCommands(points, tension = 0) {
|
|
138
146
|
const commands = [{ op: "M", x: points[0].x, y: points[0].y }];
|
|
139
147
|
for (let index = 0; index < points.length - 1; index += 1) {
|
|
140
148
|
const before = points[index - 1] ?? points[index];
|
|
@@ -143,10 +151,10 @@ function buildCardinalCommands(points) {
|
|
|
143
151
|
const after = points[index + 2] ?? next;
|
|
144
152
|
commands.push({
|
|
145
153
|
op: "C",
|
|
146
|
-
x1: current.x + (next.x - before.x) / 6,
|
|
147
|
-
y1: current.y + (next.y - before.y) / 6,
|
|
148
|
-
x2: next.x - (after.x - current.x) / 6,
|
|
149
|
-
y2: next.y - (after.y - current.y) / 6,
|
|
154
|
+
x1: current.x + (next.x - before.x) / 6 * (1 - tension),
|
|
155
|
+
y1: current.y + (next.y - before.y) / 6 * (1 - tension),
|
|
156
|
+
x2: next.x - (after.x - current.x) / 6 * (1 - tension),
|
|
157
|
+
y2: next.y - (after.y - current.y) / 6 * (1 - tension),
|
|
150
158
|
x: next.x,
|
|
151
159
|
y: next.y
|
|
152
160
|
});
|
|
@@ -265,7 +273,7 @@ function buildNaturalCommands(points) {
|
|
|
265
273
|
return ownCommands(commands);
|
|
266
274
|
}
|
|
267
275
|
|
|
268
|
-
function buildValidatedCurvePathCommands(points, curve) {
|
|
276
|
+
function buildValidatedCurvePathCommands(points, curve, tension) {
|
|
269
277
|
if (curve === "linear" ||
|
|
270
278
|
(SMOOTH_CURVES.has(curve) &&
|
|
271
279
|
curve !== "monotone" &&
|
|
@@ -277,7 +285,7 @@ function buildValidatedCurvePathCommands(points, curve) {
|
|
|
277
285
|
}
|
|
278
286
|
if (curve === "basis") return buildBasisCommands(points);
|
|
279
287
|
if (curve === "cardinal") {
|
|
280
|
-
return buildCardinalCommands(points);
|
|
288
|
+
return buildCardinalCommands(points, tension);
|
|
281
289
|
}
|
|
282
290
|
if (curve === "monotone") {
|
|
283
291
|
return buildMonotoneCommands(points);
|
|
@@ -301,17 +309,19 @@ function curveCommandCount(pointCount, curve) {
|
|
|
301
309
|
: pointCount;
|
|
302
310
|
}
|
|
303
311
|
|
|
304
|
-
export function buildCurvePathCommands(points, curve = "linear") {
|
|
312
|
+
export function buildCurvePathCommands(points, curve = "linear", tension) {
|
|
313
|
+
validateCardinalTension(tension, curve);
|
|
305
314
|
const validatedPoints = validatePoints(points);
|
|
306
315
|
const validatedCurve = validateCurveInterpolation(curve);
|
|
307
|
-
|
|
316
|
+
validatePathCommandCount(
|
|
308
317
|
curveCommandCount(validatedPoints.length, validatedCurve),
|
|
309
318
|
"Curve path command count"
|
|
310
319
|
);
|
|
311
320
|
const direct = buildValidatedCurvePathCommands(
|
|
312
321
|
validatedPoints,
|
|
313
|
-
validatedCurve
|
|
322
|
+
validatedCurve, tension
|
|
314
323
|
);
|
|
324
|
+
validatePathCommandCount(direct.length, "Curve path command count");
|
|
315
325
|
if (finiteCommands(direct)) return direct;
|
|
316
326
|
|
|
317
327
|
let xScale = 0;
|
|
@@ -327,7 +337,7 @@ export function buildCurvePathCommands(points, curve = "linear") {
|
|
|
327
337
|
x: point.x / xScale,
|
|
328
338
|
y: point.y / yScale
|
|
329
339
|
})),
|
|
330
|
-
validatedCurve
|
|
340
|
+
validatedCurve, tension
|
|
331
341
|
);
|
|
332
342
|
const restored = normalized.map(command => {
|
|
333
343
|
const copy = {};
|
|
@@ -341,6 +351,7 @@ export function buildCurvePathCommands(points, curve = "linear") {
|
|
|
341
351
|
if (!finiteCommands(restored)) {
|
|
342
352
|
throw new RangeError("Curve interpolation exceeds the finite numeric range.");
|
|
343
353
|
}
|
|
354
|
+
validatePathCommandCount(restored.length, "Curve path command count");
|
|
344
355
|
return ownCommands(restored);
|
|
345
356
|
}
|
|
346
357
|
|
|
@@ -403,7 +414,7 @@ export function buildAreaCurvePathCommands(
|
|
|
403
414
|
const validatedCurve = validateCurveInterpolation(curve);
|
|
404
415
|
const validatedLower = validatePoints(lowerPoints);
|
|
405
416
|
const validatedUpper = validatePoints(upperPoints);
|
|
406
|
-
|
|
417
|
+
validatePathCommandCount(
|
|
407
418
|
curveCommandCount(validatedLower.length, validatedCurve) +
|
|
408
419
|
curveCommandCount(validatedUpper.length, validatedCurve) + 1,
|
|
409
420
|
"Area path command count"
|
|
@@ -416,10 +427,12 @@ export function buildAreaCurvePathCommands(
|
|
|
416
427
|
const upper = reverseOpenPathCommands(
|
|
417
428
|
buildOrientedCurvePathCommands(validatedUpper, validatedCurve, independentAxis)
|
|
418
429
|
);
|
|
419
|
-
|
|
430
|
+
const commands = [
|
|
420
431
|
...lower,
|
|
421
432
|
{ op: "L", x: upper[0].x, y: upper[0].y },
|
|
422
433
|
...upper.slice(1),
|
|
423
434
|
{ op: "Z" }
|
|
424
|
-
]
|
|
435
|
+
];
|
|
436
|
+
validatePathCommandCount(commands.length, "Area path command count");
|
|
437
|
+
return ownCommands(commands);
|
|
425
438
|
}
|