ggaction 0.0.15 → 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.
- package/CHANGELOG.md +68 -0
- package/README.md +22 -2
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/knowledge/task-packet.schema.json +1 -1
- package/knowledge/task-resolver.js +1 -1
- package/package.json +33 -6
- package/src/BasicChartProgram.js +3 -1
- package/src/ChartProgram.js +2 -0
- package/src/accessibility.js +157 -0
- package/src/actions/boxPlots/components.js +6 -8
- package/src/actions/boxPlots/create.js +4 -5
- package/src/actions/boxPlots/edit.js +3 -4
- package/src/actions/boxPlots/materialize.js +1 -1
- package/src/actions/canvas/actions.js +3 -3
- package/src/actions/canvas/fitting.js +1 -1
- package/src/actions/categoryOrder/index.js +6 -8
- package/src/actions/charts/area.js +1 -1
- package/src/actions/charts/bar.js +1 -1
- package/src/actions/charts/beeswarm.js +1 -1
- package/src/actions/charts/density.js +1 -1
- package/src/actions/charts/ecdf.js +2 -2
- package/src/actions/charts/endpoints.js +4 -4
- package/src/actions/charts/heatmap.js +1 -1
- package/src/actions/charts/histogram.js +1 -1
- package/src/actions/charts/horizon.js +1 -1
- package/src/actions/charts/interval-regression.js +2 -2
- package/src/actions/charts/line.js +1 -1
- package/src/actions/charts/parallel.js +1 -1
- package/src/actions/charts/pie.js +1 -1
- package/src/actions/charts/polar.js +2 -2
- package/src/actions/charts/radar.js +1 -1
- package/src/actions/charts/radial.js +2 -2
- package/src/actions/charts/raincloud.js +2 -2
- package/src/actions/charts/rug-strip.js +2 -2
- package/src/actions/charts/scatter.js +1 -1
- package/src/actions/composition/actions.js +20 -24
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +1 -1
- package/src/actions/data/basicBin2d.js +4 -5
- package/src/actions/data/bin.js +2 -2
- package/src/actions/data/bin2d.js +6 -8
- package/src/actions/data/bin2dMaterialize.js +4 -5
- package/src/actions/data/box.js +4 -4
- package/src/actions/data/complete.js +2 -2
- package/src/actions/data/computed.js +2 -2
- package/src/actions/data/create.js +5 -12
- package/src/actions/data/density.js +3 -3
- package/src/actions/data/derived.js +10 -14
- package/src/actions/data/ecdf.js +2 -2
- package/src/actions/data/edit.js +35 -23
- package/src/actions/data/filter.js +14 -20
- package/src/actions/data/fold.js +2 -2
- package/src/actions/data/gradientProfile.js +2 -2
- package/src/actions/data/horizon.js +2 -2
- package/src/actions/data/impute.js +2 -2
- package/src/actions/data/index.js +55 -54
- package/src/actions/data/interval.js +2 -2
- package/src/actions/data/intervalEdit.js +26 -18
- package/src/actions/data/normalize.js +2 -2
- package/src/actions/data/regression.js +5 -6
- package/src/actions/data/revise.js +48 -0
- package/src/actions/data/revisionBindings.js +13 -33
- package/src/actions/data/stack.js +2 -2
- package/src/actions/data/summary.js +2 -2
- package/src/actions/data/timeUnit.js +2 -2
- package/src/actions/data/window.js +2 -2
- package/src/actions/encodings/angle.js +1 -1
- package/src/actions/encodings/appearance.js +4 -4
- package/src/actions/encodings/barWidth.js +1 -1
- package/src/actions/encodings/channels.js +2 -2
- package/src/actions/encodings/color/index.js +1 -1
- package/src/actions/encodings/density.js +2 -2
- package/src/actions/encodings/histogram.js +1 -1
- package/src/actions/encodings/horizon.js +6 -8
- package/src/actions/encodings/offset.js +2 -2
- package/src/actions/encodings/parallel.js +4 -5
- package/src/actions/encodings/pathOrder.js +6 -11
- package/src/actions/encodings/position/index.js +4 -4
- package/src/actions/encodings/ranged.js +5 -5
- package/src/actions/encodings/remove.js +4 -5
- package/src/actions/encodings/ruleAppearance.js +1 -1
- package/src/actions/encodings/seriesLayout.js +1 -1
- package/src/actions/encodings/stroke.js +1 -1
- package/src/actions/encodings/strokeDash.js +2 -2
- package/src/actions/encodings/text.js +1 -1
- package/src/actions/errorBands/create.js +11 -17
- package/src/actions/errorBands/edit.js +13 -131
- package/src/actions/errorBars/create.js +8 -13
- package/src/actions/errorBars/edit.js +10 -121
- package/src/actions/facets/actions.js +30 -16
- package/src/actions/facets/derive.js +8 -2
- package/src/actions/facets/guides.js +4 -5
- package/src/actions/facets/replay.js +4 -5
- package/src/actions/gradientPlots/components.js +5 -6
- package/src/actions/gradientPlots/create.js +4 -5
- package/src/actions/gradientPlots/edit.js +4 -5
- package/src/actions/gradientPlots/materialize.js +2 -2
- package/src/actions/gradientPlots/rebind.js +4 -5
- package/src/actions/guides/axes/axes.js +23 -4
- package/src/actions/guides/axes/axis.js +2 -2
- package/src/actions/guides/axes/edit.js +2 -2
- package/src/actions/guides/axes/labels.js +9 -9
- package/src/actions/guides/axes/lines.js +9 -7
- package/src/actions/guides/axes/parallel/lifecycle.js +13 -13
- package/src/actions/guides/axes/parallel.js +5 -5
- package/src/actions/guides/axes/remove.js +4 -4
- package/src/actions/guides/axes/tickGroups.js +4 -4
- package/src/actions/guides/axes/ticks.js +13 -4
- package/src/actions/guides/axes/titles.js +10 -10
- package/src/actions/guides/grids/grid.js +18 -29
- package/src/actions/guides/guides.js +1 -1
- package/src/actions/guides/legends/blocks.js +1 -1
- package/src/actions/guides/legends/categorical/actions.js +4 -4
- package/src/actions/guides/legends/categorical/components.js +38 -33
- package/src/actions/guides/legends/categorical/layout.js +1 -1
- package/src/actions/guides/legends/categorical/symbols.js +27 -30
- package/src/actions/guides/legends/continuous/common.js +46 -8
- package/src/actions/guides/legends/continuous/gradient.js +89 -155
- package/src/actions/guides/legends/continuous/interval.js +13 -13
- package/src/actions/guides/legends/continuous/opacity.js +30 -79
- package/src/actions/guides/legends/continuous/stroke.js +22 -322
- package/src/actions/guides/legends/edit.js +1 -1
- package/src/actions/guides/legends/focused.js +5 -5
- package/src/actions/guides/legends/lane.js +6 -6
- package/src/actions/guides/legends/remove.js +4 -5
- package/src/actions/guides/legends/size.js +21 -82
- package/src/actions/guides/legends/strokeWidth.js +17 -77
- package/src/actions/guides/polar/axes/facade.js +4 -4
- package/src/actions/guides/polar/axes/labels.js +5 -5
- package/src/actions/guides/polar/axes/lines.js +9 -15
- package/src/actions/guides/polar/axes/ticks.js +9 -15
- package/src/actions/guides/polar/axes/titles.js +5 -5
- package/src/actions/guides/polar/grids.js +11 -13
- package/src/actions/index.js +2 -0
- package/src/actions/intervals/revision.js +126 -0
- package/src/actions/marks/arc/actions.js +3 -3
- package/src/actions/marks/area/actions.js +4 -4
- package/src/actions/marks/bar/create.js +1 -1
- package/src/actions/marks/bar/edit.js +1 -1
- package/src/actions/marks/bar/materialize.js +12 -38
- package/src/actions/marks/line/actions.js +3 -3
- package/src/actions/marks/point/create.js +1 -1
- package/src/actions/marks/point/edit.js +1 -1
- package/src/actions/marks/point/jitter.js +6 -8
- package/src/actions/marks/point/materialize.js +16 -24
- package/src/actions/marks/point/packing.js +6 -8
- package/src/actions/marks/rect/actions.js +3 -3
- package/src/actions/marks/references.js +8 -10
- package/src/actions/marks/remove.js +5 -49
- package/src/actions/marks/rule/actions.js +4 -4
- package/src/actions/marks/text/actions.js +5 -5
- package/src/actions/marks/text/layout.js +12 -20
- package/src/actions/marks/text/removal.js +1 -1
- package/src/actions/marks/text/selection.js +4 -5
- package/src/actions/marks/tick/actions.js +3 -3
- package/src/actions/primitives/createGraphics.js +5 -4
- package/src/actions/primitives/editGraphics.js +5 -4
- package/src/actions/primitives/graphicProperties.js +6 -0
- package/src/actions/primitives/semantic.js +10 -3
- package/src/actions/primitives/semanticAction.js +7 -4
- package/src/actions/primitives/semanticValidation/dataset.js +2 -4
- package/src/actions/regression/components.js +10 -14
- package/src/actions/regression/create.js +3 -4
- package/src/actions/regression/edit.js +3 -4
- package/src/actions/resources/remove.js +12 -11
- package/src/actions/scales/create.js +1 -1
- package/src/actions/scales/edit.js +1 -1
- package/src/actions/scales/materialize.js +1 -1
- package/src/actions/scales/offset.js +2 -2
- package/src/actions/scales/parallel.js +1 -1
- package/src/actions/scales/quantitativeColor.js +4 -5
- package/src/actions/selection/actions.js +25 -36
- package/src/actions/textMetrics/index.js +56 -0
- package/src/actions/theme/actions.js +2 -2
- package/src/actions/theme/reconcile.js +65 -121
- package/src/actions/titles/actions.js +8 -8
- package/src/actions/titles/resolve.js +3 -3
- package/src/actions/violinPlots/create.js +1 -1
- package/src/actions/violinPlots/edit.js +1 -1
- package/src/core/action.js +107 -35
- package/src/core/diagnostics.js +16 -0
- package/src/core/extensionRegistry.js +27 -0
- package/src/core/font.js +18 -0
- package/src/core/identifiers.js +5 -4
- package/src/core/immutable.js +10 -3
- package/src/core/optionRequirements.js +8 -0
- package/src/core/programState.js +1 -1
- package/src/core/resourceReferences.js +15 -6
- package/src/core/textMetricProfile.js +28 -0
- package/src/core/textMetrics.js +24 -3
- package/src/core/validation.js +22 -10
- package/src/diagnostics.js +1 -0
- package/src/grammar/impute.js +3 -2
- package/src/grammar/scales/types.js +8 -0
- package/src/grammar/schemas/graphicBounds.js +11 -11
- package/src/layout/labels.js +11 -10
- package/src/layout/legend.js +4 -4
- package/src/layout/legendItems.js +6 -6
- package/src/layout/text.js +10 -9
- package/src/layout/title.js +9 -9
- package/src/materialization/facetGuides/placement.js +1 -1
- package/src/materialization/facetGuides/preparation.js +1 -1
- package/src/materialization/facetHeaders.js +11 -10
- package/src/materialization/guides/layout.js +3 -3
- package/src/materialization/scaleGuideDependencies.js +10 -0
- package/src/materialization/selection/items/common.js +1 -1
- package/src/materialization/text.js +2 -2
- package/src/materialization/typography.js +31 -0
- package/src/mcp/cli.js +5 -3
- package/src/persistence/codec.js +65 -0
- package/src/persistence/validation.js +185 -0
- package/src/persistence.js +74 -0
- package/src/renderers/canvas/index.js +5 -4
- package/src/renderers/nativeBackend.js +10 -0
- package/src/renderers/options.js +13 -0
- package/src/renderers/pdf.js +25 -54
- package/src/renderers/png.js +22 -17
- package/src/renderers/svg.js +10 -14
- package/src/renderers/text.js +1 -11
- package/src/selectors/coordinates.js +3 -1
- package/src/selectors/datasets.js +8 -6
- package/src/selectors/layers.js +6 -4
- package/src/selectors/markOwners.js +46 -0
- package/src/selectors/scales.js +6 -4
- package/src/theme/defaults.js +2 -1
- package/src/version.js +2 -0
- package/types/accessibility.d.ts +37 -0
- package/types/basic.d.ts +4 -2
- package/types/diagnostics.d.ts +15 -0
- package/types/index.d.ts +5 -0
- package/types/pdf.d.ts +9 -0
- package/types/persistence.d.ts +7 -0
- package/types/png.d.ts +9 -0
- package/types/program.d.ts +34 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { action, closedAction } from "../../core/action.js";
|
|
2
|
+
import { noOptions } from "../../core/validation.js";
|
|
3
|
+
import { normalizeTextMetricProfile } from "../../core/textMetricProfile.js";
|
|
4
|
+
import { rematerializeTypography } from "../../materialization/typography.js";
|
|
5
|
+
|
|
6
|
+
function metricsProgram(parent, child) {
|
|
7
|
+
return typeof child.applyTextMetrics === "function" ? child : new parent.constructor({
|
|
8
|
+
...child, actionSequence: child._actionSequence
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function inheritTextMetrics(parent, child) {
|
|
13
|
+
const profile = parent.materializationConfigs.textMetrics;
|
|
14
|
+
return profile === undefined ? child : metricsProgram(parent, child).applyTextMetrics({ profile });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function applyProfile(program, profile) {
|
|
18
|
+
let next = profile === undefined
|
|
19
|
+
? program._withoutMaterializationConfig(["textMetrics"])
|
|
20
|
+
: program._withMaterializationConfig(["textMetrics"], profile);
|
|
21
|
+
if (next.compositionSpec !== undefined) {
|
|
22
|
+
const children = Object.fromEntries(next.compositionSpec.children.map(id => {
|
|
23
|
+
const child = next.children[id];
|
|
24
|
+
return [id, profile === undefined
|
|
25
|
+
? child.materializationConfigs.textMetrics === undefined ? child : metricsProgram(next, child).removeTextMetrics()
|
|
26
|
+
: inheritTextMetrics(next, child)];
|
|
27
|
+
}));
|
|
28
|
+
next = next._withCompositionState({ children, compositionSpec: next.compositionSpec });
|
|
29
|
+
for (const id of next.compositionSpec.children) next = next.useProgram({ id });
|
|
30
|
+
return next.materializeComposition();
|
|
31
|
+
}
|
|
32
|
+
return rematerializeTypography(next);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const applyTextMetrics = /* @__PURE__ */ closedAction(
|
|
36
|
+
{ op: "applyTextMetrics", description: "Apply measured text widths to chart layout.", scope: "any" }, ["profile"],
|
|
37
|
+
function (args = {}) {
|
|
38
|
+
const profile = normalizeTextMetricProfile(args.profile);
|
|
39
|
+
applyProfile(this, profile);
|
|
40
|
+
return applyProfile(this, profile);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export const removeTextMetrics = /* @__PURE__ */ action(
|
|
45
|
+
{ op: "removeTextMetrics", description: "Restore estimated text layout.", scope: "any" },
|
|
46
|
+
function (args = {}) {
|
|
47
|
+
noOptions(args, "removeTextMetrics");
|
|
48
|
+
if (this.materializationConfigs.textMetrics === undefined) throw new Error("removeTextMetrics requires an active profile.");
|
|
49
|
+
applyProfile(this, undefined);
|
|
50
|
+
return applyProfile(this, undefined);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export function registerTextMetricActions(ProgramClass) {
|
|
55
|
+
Object.assign(ProgramClass.prototype, { applyTextMetrics, removeTextMetrics });
|
|
56
|
+
}
|
|
@@ -28,7 +28,7 @@ function requireCompositionThemeHandlers() {
|
|
|
28
28
|
return compositionThemeHandlers;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export const applyTheme = action(
|
|
31
|
+
export const applyTheme = /* @__PURE__ */ action(
|
|
32
32
|
{
|
|
33
33
|
op: "applyTheme",
|
|
34
34
|
description: "Apply persistent visual defaults to the chart program.",
|
|
@@ -70,7 +70,7 @@ export const applyTheme = action(
|
|
|
70
70
|
}
|
|
71
71
|
);
|
|
72
72
|
|
|
73
|
-
export const removeTheme = action(
|
|
73
|
+
export const removeTheme = /* @__PURE__ */ action(
|
|
74
74
|
{
|
|
75
75
|
op: "removeTheme",
|
|
76
76
|
description: "Remove program theme defaults while preserving local styles.",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { rematerializeTypography } from "../../materialization/typography.js";
|
|
1
2
|
import { themeTokens } from "../../theme/defaults.js";
|
|
2
3
|
import {
|
|
3
4
|
normalizeThemeState,
|
|
@@ -46,10 +47,7 @@ function tokenFor(value, sourceTokens, targetTokens) {
|
|
|
46
47
|
if (matches.length === 0) return undefined;
|
|
47
48
|
const targets = new Set(matches.map(token => targetTokens[token]));
|
|
48
49
|
if (targets.size !== 1) return undefined;
|
|
49
|
-
|
|
50
|
-
if (targetTokens[token] === targets.values().next().value) return token;
|
|
51
|
-
}
|
|
52
|
-
return undefined;
|
|
50
|
+
return matches[0];
|
|
53
51
|
}
|
|
54
52
|
|
|
55
53
|
function roleValues(role, sourceTokens, legacy = []) {
|
|
@@ -411,6 +409,11 @@ function recolorConfig(
|
|
|
411
409
|
return { changed, value: changed ? next : value };
|
|
412
410
|
}
|
|
413
411
|
|
|
412
|
+
function addMarkPropertyOverrides(overrides, id, property) {
|
|
413
|
+
overrides.add(`c:marks.${id}.${property}`);
|
|
414
|
+
overrides.add(`g:${id}.${property}`);
|
|
415
|
+
}
|
|
416
|
+
|
|
414
417
|
function addMarkOverrides(overrides, program, op, args) {
|
|
415
418
|
const match = /^(?:create|edit)(Point|Line|Area|Arc|Bar|Rect|Rule|Tick|Text)Mark$/u
|
|
416
419
|
.exec(op);
|
|
@@ -425,42 +428,43 @@ function addMarkOverrides(overrides, program, op, args) {
|
|
|
425
428
|
if (typeof id !== "string") return;
|
|
426
429
|
for (const property of ["fill", "stroke", "fontFamily"]) {
|
|
427
430
|
if (!Object.hasOwn(args, property)) continue;
|
|
428
|
-
overrides
|
|
429
|
-
overrides.add(`g:${id}.${property}`);
|
|
431
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
430
432
|
}
|
|
431
433
|
}
|
|
432
434
|
|
|
433
435
|
function addFacadeMarkOverrides(overrides, op, args) {
|
|
434
436
|
const facades = {
|
|
435
|
-
createScatterPlot: ["
|
|
436
|
-
createLinePlot: ["
|
|
437
|
-
createPolarScatterPlot: ["
|
|
438
|
-
createPolarLinePlot: ["
|
|
439
|
-
createRadarPlot: ["
|
|
440
|
-
createRugPlot: ["
|
|
441
|
-
createStripPlot: ["
|
|
442
|
-
createBeeswarmPlot: ["
|
|
443
|
-
createBarPlot: ["
|
|
444
|
-
createAreaPlot: ["
|
|
445
|
-
createPiePlot: ["
|
|
446
|
-
createRosePlot: ["
|
|
447
|
-
createRadialBarPlot: ["
|
|
448
|
-
createDensityPlot: ["
|
|
449
|
-
createParallelCoordinates: ["
|
|
450
|
-
createHistogram: ["
|
|
451
|
-
createHeatmap: ["
|
|
452
|
-
createHorizonPlot: ["
|
|
453
|
-
editHorizon: ["
|
|
454
|
-
createViolinPlot: ["
|
|
437
|
+
createScatterPlot: ["point", true],
|
|
438
|
+
createLinePlot: ["line", false],
|
|
439
|
+
createPolarScatterPlot: ["point", true],
|
|
440
|
+
createPolarLinePlot: ["line", false],
|
|
441
|
+
createRadarPlot: ["line", false],
|
|
442
|
+
createRugPlot: ["tick", false],
|
|
443
|
+
createStripPlot: ["point", true],
|
|
444
|
+
createBeeswarmPlot: ["point", true],
|
|
445
|
+
createBarPlot: ["bar", true],
|
|
446
|
+
createAreaPlot: ["area", true],
|
|
447
|
+
createPiePlot: ["arc", true],
|
|
448
|
+
createRosePlot: ["arc", true],
|
|
449
|
+
createRadialBarPlot: ["arc", true],
|
|
450
|
+
createDensityPlot: ["area", true],
|
|
451
|
+
createParallelCoordinates: ["line", false],
|
|
452
|
+
createHistogram: ["bar", true],
|
|
453
|
+
createHeatmap: ["rect", false],
|
|
454
|
+
createHorizonPlot: ["area", false],
|
|
455
|
+
editHorizon: ["area", false],
|
|
456
|
+
createViolinPlot: ["area", true]
|
|
455
457
|
};
|
|
456
458
|
const facade = facades[op];
|
|
457
459
|
if (facade === undefined) return;
|
|
458
|
-
const [
|
|
460
|
+
const [option, includesFill] = facade;
|
|
461
|
+
const subject = op === "editHorizon" ? "HorizonPlot" : op.slice(6);
|
|
462
|
+
const defaultId = subject[0].toLowerCase() + subject.slice(1);
|
|
463
|
+
const properties = includesFill ? ["fill", "stroke"] : ["stroke"];
|
|
459
464
|
const id = args.id ?? defaultId;
|
|
460
465
|
for (const property of properties) {
|
|
461
466
|
if (!Object.hasOwn(args[option] ?? {}, property)) continue;
|
|
462
|
-
overrides
|
|
463
|
-
overrides.add(`g:${id}.${property}`);
|
|
467
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
464
468
|
}
|
|
465
469
|
}
|
|
466
470
|
|
|
@@ -492,8 +496,7 @@ function addTextFacadeOverrides(overrides, node) {
|
|
|
492
496
|
if (typeof id !== "string") return;
|
|
493
497
|
for (const property of ["fill", "fontFamily"]) {
|
|
494
498
|
if (!Object.hasOwn(node.args, property)) continue;
|
|
495
|
-
overrides
|
|
496
|
-
overrides.add(`g:${id}.${property}`);
|
|
499
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
497
500
|
}
|
|
498
501
|
}
|
|
499
502
|
|
|
@@ -509,8 +512,7 @@ function addErrorBarOverrides(overrides, program, node) {
|
|
|
509
512
|
if (id === undefined) return;
|
|
510
513
|
const config = program.markConfigs[id].errorBar;
|
|
511
514
|
for (const target of [id, config.lowerCapId, config.upperCapId]) {
|
|
512
|
-
overrides
|
|
513
|
-
overrides.add(`g:${target}.stroke`);
|
|
515
|
+
addMarkPropertyOverrides(overrides, target, "stroke");
|
|
514
516
|
}
|
|
515
517
|
}
|
|
516
518
|
|
|
@@ -536,8 +538,7 @@ function addErrorBandOverrides(overrides, program, node) {
|
|
|
536
538
|
const targets = errorBandTargets(program, node);
|
|
537
539
|
if (targets === undefined) return;
|
|
538
540
|
if (Object.hasOwn(node.args, "fill") && node.args.fill !== false) {
|
|
539
|
-
overrides
|
|
540
|
-
overrides.add(`g:${targets.id}.fill`);
|
|
541
|
+
addMarkPropertyOverrides(overrides, targets.id, "fill");
|
|
541
542
|
}
|
|
542
543
|
const boundary = node.op === "editErrorBandBoundary"
|
|
543
544
|
? node.args
|
|
@@ -548,8 +549,7 @@ function addErrorBandOverrides(overrides, program, node) {
|
|
|
548
549
|
? [targets.lower, targets.upper]
|
|
549
550
|
: [(node.args.boundary === "lower" ? targets.lower : targets.upper)];
|
|
550
551
|
for (const id of ids) {
|
|
551
|
-
overrides
|
|
552
|
-
overrides.add(`g:${id}.stroke`);
|
|
552
|
+
addMarkPropertyOverrides(overrides, id, "stroke");
|
|
553
553
|
}
|
|
554
554
|
}
|
|
555
555
|
|
|
@@ -589,8 +589,7 @@ function addReferenceOverrides(overrides, op, args) {
|
|
|
589
589
|
const id = args.id ?? defaultId;
|
|
590
590
|
for (const property of properties) {
|
|
591
591
|
if (!Object.hasOwn(args, property)) continue;
|
|
592
|
-
overrides
|
|
593
|
-
overrides.add(`g:${id}.${property}`);
|
|
592
|
+
addMarkPropertyOverrides(overrides, id, property);
|
|
594
593
|
}
|
|
595
594
|
}
|
|
596
595
|
|
|
@@ -611,12 +610,10 @@ function addRegressionOverrides(overrides, node) {
|
|
|
611
610
|
: child?.args.id ?? child?.args.target;
|
|
612
611
|
if (typeof id !== "string") return;
|
|
613
612
|
if (Object.hasOwn(style ?? {}, "color")) {
|
|
614
|
-
overrides
|
|
615
|
-
overrides.add(`g:${id}.fill`);
|
|
613
|
+
addMarkPropertyOverrides(overrides, id, "fill");
|
|
616
614
|
}
|
|
617
615
|
if (Object.hasOwn(style ?? {}, "stroke")) {
|
|
618
|
-
overrides
|
|
619
|
-
overrides.add(`g:${id}.stroke`);
|
|
616
|
+
addMarkPropertyOverrides(overrides, id, "stroke");
|
|
620
617
|
}
|
|
621
618
|
}
|
|
622
619
|
|
|
@@ -835,39 +832,18 @@ function addParallelOverrides(overrides, program, op, args) {
|
|
|
835
832
|
}
|
|
836
833
|
|
|
837
834
|
function addLegendOverrides(overrides, program, args) {
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
};
|
|
851
|
-
for (const kind of Object.keys(legend)) {
|
|
852
|
-
if (Object.hasOwn(args.labels ?? {}, "color")) {
|
|
853
|
-
overrides.add(`c:guides.legend.${kind}.labels.color`);
|
|
854
|
-
if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Labels.fill`);
|
|
855
|
-
}
|
|
856
|
-
if (Object.hasOwn(args.labels ?? {}, "fontFamily")) {
|
|
857
|
-
overrides.add(`c:guides.legend.${kind}.labels.fontFamily`);
|
|
858
|
-
if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Labels.fontFamily`);
|
|
859
|
-
}
|
|
860
|
-
if (Object.hasOwn(args.titleStyle ?? {}, "color")) {
|
|
861
|
-
overrides.add(`c:guides.legend.${kind}.titleStyle.color`);
|
|
862
|
-
if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Title.fill`);
|
|
863
|
-
}
|
|
864
|
-
if (Object.hasOwn(args.titleStyle ?? {}, "fontFamily")) {
|
|
865
|
-
overrides.add(`c:guides.legend.${kind}.titleStyle.fontFamily`);
|
|
866
|
-
if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Title.fontFamily`);
|
|
867
|
-
}
|
|
868
|
-
if (Object.hasOwn(args.border ?? {}, "color")) {
|
|
869
|
-
overrides.add(`c:guides.legend.${kind}.border.color`);
|
|
870
|
-
if (prefixes[kind]) overrides.add(`g:${prefixes[kind]}Background.stroke`);
|
|
835
|
+
for (const kind of Object.keys(program.guideConfigs.legend ?? {})) {
|
|
836
|
+
const prefix = legendPrefix(kind);
|
|
837
|
+
for (const [option, suffix, property, graphicProperty] of [
|
|
838
|
+
["labels", "Labels", "color", "fill"],
|
|
839
|
+
["labels", "Labels", "fontFamily", "fontFamily"],
|
|
840
|
+
["titleStyle", "Title", "color", "fill"],
|
|
841
|
+
["titleStyle", "Title", "fontFamily", "fontFamily"],
|
|
842
|
+
["border", "Background", "color", "stroke"]
|
|
843
|
+
]) {
|
|
844
|
+
if (!Object.hasOwn(args[option] ?? {}, property)) continue;
|
|
845
|
+
overrides.add(`c:guides.legend.${kind}.${option}.${property}`);
|
|
846
|
+
if (prefix) overrides.add(`g:${prefix}${suffix}.${graphicProperty}`);
|
|
871
847
|
}
|
|
872
848
|
}
|
|
873
849
|
}
|
|
@@ -1002,51 +978,18 @@ function collectOverrides(program) {
|
|
|
1002
978
|
return overrides;
|
|
1003
979
|
}
|
|
1004
980
|
|
|
1005
|
-
function callExisting(program, graphicId, operation) {
|
|
1006
|
-
return program.graphicSpec.objects[graphicId] !== undefined &&
|
|
1007
|
-
typeof program[operation] === "function"
|
|
1008
|
-
? program[operation]()
|
|
1009
|
-
: program;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
function rematerializeThemeTypography(program) {
|
|
1013
|
-
if (program.compositionSpec !== undefined) {
|
|
1014
|
-
return program.compositionSpec.type === "facet"
|
|
1015
|
-
? program.materializeComposition()
|
|
1016
|
-
: program;
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
let next = program;
|
|
1020
|
-
for (const layer of next.semanticSpec.layers) {
|
|
1021
|
-
if (layer.mark?.type === "text" &&
|
|
1022
|
-
next.graphicSpec.objects[layer.id] !== undefined &&
|
|
1023
|
-
typeof next.rematerializeTextMark === "function") {
|
|
1024
|
-
next = next.rematerializeTextMark({ id: layer.id });
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
for (const [id, operation] of [
|
|
1028
|
-
["xAxisLabels", "editXAxisLabels"],
|
|
1029
|
-
["yAxisLabels", "editYAxisLabels"],
|
|
1030
|
-
["xAxisTitle", "editXAxisTitle"],
|
|
1031
|
-
["yAxisTitle", "editYAxisTitle"],
|
|
1032
|
-
["thetaAxisLabels", "editThetaAxisLabels"],
|
|
1033
|
-
["radialAxisLabels", "editRadialAxisLabels"],
|
|
1034
|
-
["thetaAxisTitle", "editThetaAxisTitle"],
|
|
1035
|
-
["radialAxisTitle", "editRadialAxisTitle"]
|
|
1036
|
-
]) {
|
|
1037
|
-
next = callExisting(next, id, operation);
|
|
1038
|
-
}
|
|
1039
|
-
if (Object.keys(next.guideConfigs.legend ?? {}).length > 0 &&
|
|
1040
|
-
typeof next.rematerializeLegend === "function") {
|
|
1041
|
-
next = next.rematerializeLegend();
|
|
1042
|
-
}
|
|
1043
|
-
next = callExisting(next, "chartTitle", "rematerializeTitle");
|
|
1044
|
-
return next;
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
981
|
export function reconcileProgramTheme(program, { source, metadata }) {
|
|
1048
982
|
const storedState = program.materializationConfigs.theme;
|
|
1049
983
|
if (storedState === undefined) return program;
|
|
984
|
+
// Data/context/trace-only transitions cannot change authored appearance.
|
|
985
|
+
// Preserve all visual identities instead of rescanning every existing mark.
|
|
986
|
+
if (program.graphicSpec === source.graphicSpec &&
|
|
987
|
+
program.materializationConfigs === source.materializationConfigs &&
|
|
988
|
+
program.children === source.children &&
|
|
989
|
+
program.compositionSpec === source.compositionSpec &&
|
|
990
|
+
["layers", "scales", "coordinates", "guides", "title"].every(
|
|
991
|
+
key => program.semanticSpec[key] === source.semanticSpec[key]
|
|
992
|
+
)) return program;
|
|
1050
993
|
const composition = program.compositionSpec !== undefined;
|
|
1051
994
|
const state = normalizeThemeState(storedState, { composition });
|
|
1052
995
|
const sourceState = normalizeThemeState(
|
|
@@ -1088,15 +1031,16 @@ export function reconcileProgramTheme(program, { source, metadata }) {
|
|
|
1088
1031
|
|
|
1089
1032
|
const facetsChanged = configs.value.facets !==
|
|
1090
1033
|
themed.materializationConfigs.facets;
|
|
1034
|
+
const typographyChanged = sourceTokens.fontFamily !== targetTokens.fontFamily;
|
|
1091
1035
|
if (facetsChanged && next.compositionSpec?.type === "facet") {
|
|
1092
1036
|
next = next.materializeComposition();
|
|
1093
|
-
} else if (
|
|
1094
|
-
next =
|
|
1037
|
+
} else if (typographyChanged) {
|
|
1038
|
+
next = rematerializeTypography(next);
|
|
1095
1039
|
}
|
|
1096
1040
|
|
|
1097
1041
|
const parallelChanged = configs.value.guides.axis?.parallel !==
|
|
1098
1042
|
themed.guideConfigs.axis?.parallel;
|
|
1099
|
-
if (parallelChanged && typeof next.rematerializeParallelAxes === "function") {
|
|
1043
|
+
if (parallelChanged && !typographyChanged && typeof next.rematerializeParallelAxes === "function") {
|
|
1100
1044
|
next = next.rematerializeParallelAxes();
|
|
1101
1045
|
}
|
|
1102
1046
|
|
|
@@ -115,12 +115,12 @@ function titleTextAction(kind, create) {
|
|
|
115
115
|
}));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
export const editTitleText = titleTextAction("title", false);
|
|
119
|
-
export const createTitleText = titleTextAction("title", true);
|
|
120
|
-
export const editSubtitleText = titleTextAction("subtitle", false);
|
|
121
|
-
export const createSubtitleText = titleTextAction("subtitle", true);
|
|
118
|
+
export const editTitleText = /* @__PURE__ */ titleTextAction("title", false);
|
|
119
|
+
export const createTitleText = /* @__PURE__ */ titleTextAction("title", true);
|
|
120
|
+
export const editSubtitleText = /* @__PURE__ */ titleTextAction("subtitle", false);
|
|
121
|
+
export const createSubtitleText = /* @__PURE__ */ titleTextAction("subtitle", true);
|
|
122
122
|
|
|
123
|
-
export const rematerializeTitle = action(
|
|
123
|
+
export const rematerializeTitle = /* @__PURE__ */ action(
|
|
124
124
|
{ op: "rematerializeTitle", description: "Rematerialize chart title graphics." },
|
|
125
125
|
withGuideLayoutValidation(function (args = {}) {
|
|
126
126
|
noOptions(args, "rematerializeTitle");
|
|
@@ -142,7 +142,7 @@ export const rematerializeTitle = action(
|
|
|
142
142
|
})
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
-
export const createTitle = action(
|
|
145
|
+
export const createTitle = /* @__PURE__ */ action(
|
|
146
146
|
{
|
|
147
147
|
op: "createTitle",
|
|
148
148
|
description: "Create a chart title and optional subtitle.",
|
|
@@ -176,7 +176,7 @@ export const createTitle = action(
|
|
|
176
176
|
})
|
|
177
177
|
);
|
|
178
178
|
|
|
179
|
-
export const editTitle = action(
|
|
179
|
+
export const editTitle = /* @__PURE__ */ action(
|
|
180
180
|
{
|
|
181
181
|
op: "editTitle",
|
|
182
182
|
description: "Edit one stable chart title resource.",
|
|
@@ -214,7 +214,7 @@ export const editTitle = action(
|
|
|
214
214
|
})
|
|
215
215
|
);
|
|
216
216
|
|
|
217
|
-
export const removeTitle = action(
|
|
217
|
+
export const removeTitle = /* @__PURE__ */ action(
|
|
218
218
|
{
|
|
219
219
|
op: "removeTitle",
|
|
220
220
|
description: "Remove the complete chart title resource.",
|
|
@@ -212,7 +212,7 @@ export function resolveTitleLayout(program, config) {
|
|
|
212
212
|
program.semanticSpec.title.subtitle,
|
|
213
213
|
"Chart subtitle"
|
|
214
214
|
)
|
|
215
|
-
}, config);
|
|
215
|
+
}, config, program.materializationConfigs.textMetrics);
|
|
216
216
|
const horizontal = ["top", "bottom"].includes(config.position);
|
|
217
217
|
let component;
|
|
218
218
|
if (horizontal) {
|
|
@@ -249,10 +249,10 @@ export function resolveTitleLayout(program, config) {
|
|
|
249
249
|
? undefined
|
|
250
250
|
: component(block.subtitleLines, block.subtitleCenters);
|
|
251
251
|
const titleBounds = unionTitleBounds([
|
|
252
|
-
resolveTitleComponentBounds(title, config.titleStyle),
|
|
252
|
+
resolveTitleComponentBounds(title, config.titleStyle, program.materializationConfigs.textMetrics),
|
|
253
253
|
...(subtitle === undefined
|
|
254
254
|
? []
|
|
255
|
-
: [resolveTitleComponentBounds(subtitle, config.subtitleStyle)])
|
|
255
|
+
: [resolveTitleComponentBounds(subtitle, config.subtitleStyle, program.materializationConfigs.textMetrics)])
|
|
256
256
|
]);
|
|
257
257
|
validateLayout(
|
|
258
258
|
config.position,
|
|
@@ -132,7 +132,7 @@ function guideOptions(guides, x, y, category, color) {
|
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
export const createViolinPlot = action(
|
|
135
|
+
export const createViolinPlot = /* @__PURE__ */ action(
|
|
136
136
|
{
|
|
137
137
|
op: "createViolinPlot",
|
|
138
138
|
description: "Create a categorical kernel-density violin plot."
|
|
@@ -74,7 +74,7 @@ function assertSplitSelectionCompatibility(program, owner, changesSplit) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
export const editViolinPlot = action(
|
|
77
|
+
export const editViolinPlot = /* @__PURE__ */ action(
|
|
78
78
|
{
|
|
79
79
|
op: OPERATION,
|
|
80
80
|
description: "Revise one violin plot's data and statistical roles."
|
package/src/core/action.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateKeys } from "./validation.js";
|
|
2
|
+
import { annotateError } from "./diagnostics.js";
|
|
3
|
+
import { cloneAndFreeze, freezeOwned, isOwned, isPlainObject } from "./immutable.js";
|
|
2
4
|
|
|
3
5
|
const metadataByWrappedAction = new WeakMap();
|
|
4
6
|
const implementationByWrappedAction = new WeakMap();
|
|
@@ -35,6 +37,8 @@ function summarizeObject(value, ancestors = new WeakSet()) {
|
|
|
35
37
|
summary[`${key}Count`] = item.length;
|
|
36
38
|
} else if (isPlainObject(item)) {
|
|
37
39
|
summary[key] = summarizeObject(item, ancestors);
|
|
40
|
+
} else if (typeof item === "function") {
|
|
41
|
+
summary[`${key}Type`] = "function";
|
|
38
42
|
} else if (item !== null && typeof item === "object") {
|
|
39
43
|
summary[`${key}Type`] = item.constructor?.name ?? "object";
|
|
40
44
|
} else {
|
|
@@ -71,13 +75,59 @@ export function createActionNode({ id, op, description, args }) {
|
|
|
71
75
|
});
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
// Persistent child tails make appending the active (last) branch independent
|
|
79
|
+
// of sibling count. The public children property remains a stable frozen Array.
|
|
80
|
+
const childLists = new WeakMap();
|
|
81
|
+
|
|
82
|
+
function childList(node) {
|
|
83
|
+
let list = childLists.get(node);
|
|
84
|
+
if (list !== undefined) return list;
|
|
85
|
+
let tail;
|
|
86
|
+
for (const child of node.children) tail = { value: child, previous: tail };
|
|
87
|
+
list = { tail, length: node.children.length };
|
|
88
|
+
if (isOwned(node)) childLists.set(node, list);
|
|
89
|
+
return list;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function withChildList(node, list) {
|
|
93
|
+
let children;
|
|
94
|
+
const next = {};
|
|
95
|
+
for (const key of Object.keys(node)) {
|
|
96
|
+
if (key !== "children") Object.defineProperty(next, key, {
|
|
97
|
+
value: node[key], enumerable: true
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
Object.defineProperty(next, "children", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
get() {
|
|
103
|
+
if (children === undefined) {
|
|
104
|
+
const values = new Array(list.length);
|
|
105
|
+
let item = list.tail;
|
|
106
|
+
for (let index = list.length - 1; index >= 0; index -= 1) {
|
|
107
|
+
values[index] = item.value;
|
|
108
|
+
item = item.previous;
|
|
109
|
+
}
|
|
110
|
+
children = freezeOwned(values);
|
|
111
|
+
}
|
|
112
|
+
return children;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
childLists.set(next, list);
|
|
116
|
+
return freezeOwned(next);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function childAt(node, index) {
|
|
120
|
+
const list = childList(node);
|
|
121
|
+
return index === list.length - 1 ? list.tail.value : node.children[index];
|
|
122
|
+
}
|
|
123
|
+
|
|
74
124
|
function nodeAtPath(root, path) {
|
|
75
125
|
let node = root;
|
|
76
126
|
for (const index of path) {
|
|
77
|
-
if (!Number.isInteger(index) || index < 0 || index >= node.
|
|
127
|
+
if (!Number.isInteger(index) || index < 0 || index >= childList(node).length) {
|
|
78
128
|
throw new Error(`Unknown parent action path "${path.join(".")}".`);
|
|
79
129
|
}
|
|
80
|
-
node = node
|
|
130
|
+
node = childAt(node, index);
|
|
81
131
|
}
|
|
82
132
|
return node;
|
|
83
133
|
}
|
|
@@ -87,19 +137,28 @@ export function appendActionNodeAtPath(root, parentPath, actionNode) {
|
|
|
87
137
|
throw new TypeError("Parent action path must be an array.");
|
|
88
138
|
}
|
|
89
139
|
const parent = nodeAtPath(root, parentPath);
|
|
90
|
-
const path = [...parentPath, parent.
|
|
140
|
+
const path = [...parentPath, childList(parent).length];
|
|
91
141
|
|
|
92
142
|
function append(node, depth) {
|
|
143
|
+
const list = childList(node);
|
|
93
144
|
if (depth === parentPath.length) {
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
children: freezeOwned([...node.children, actionNode])
|
|
145
|
+
return withChildList(node, {
|
|
146
|
+
tail: { value: actionNode, previous: list.tail }, length: list.length + 1
|
|
97
147
|
});
|
|
98
148
|
}
|
|
99
149
|
const index = parentPath[depth];
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
150
|
+
const child = append(childAt(node, index), depth + 1);
|
|
151
|
+
if (index === list.length - 1) {
|
|
152
|
+
return withChildList(node, {
|
|
153
|
+
tail: { value: child, previous: list.tail.previous }, length: list.length
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Restored/extension traces may address an older sibling explicitly.
|
|
157
|
+
let tail;
|
|
158
|
+
for (let position = 0; position < list.length; position += 1) {
|
|
159
|
+
tail = { value: position === index ? child : node.children[position], previous: tail };
|
|
160
|
+
}
|
|
161
|
+
return withChildList(node, { tail, length: list.length });
|
|
103
162
|
}
|
|
104
163
|
|
|
105
164
|
return { root: append(root, 0), path };
|
|
@@ -137,39 +196,52 @@ export function action(metadata, implementation) {
|
|
|
137
196
|
});
|
|
138
197
|
|
|
139
198
|
const wrappedAction = function wrappedAction(args = {}) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
let result = implementation.call(entered, args);
|
|
153
|
-
|
|
154
|
-
if (!(result instanceof this.constructor)) {
|
|
155
|
-
throw new TypeError(`${ownedMetadata.op} must return a ChartProgram.`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
if (this.actionStack.length === 0 && actionCompletionHook !== undefined) {
|
|
159
|
-
result = actionCompletionHook(result, {
|
|
160
|
-
source: this,
|
|
161
|
-
metadata: ownedMetadata,
|
|
199
|
+
try {
|
|
200
|
+
if (!isPlainObject(args)) {
|
|
201
|
+
throw new TypeError("Action arguments must be a plain object.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (scope === "unit") this._assertUnitProgram(ownedMetadata.op);
|
|
205
|
+
if (scope === "composition") this._assertCompositionProgram(ownedMetadata.op);
|
|
206
|
+
|
|
207
|
+
const summarizedArgs = summarizeArgs(args);
|
|
208
|
+
const entered = this._enterAction({
|
|
209
|
+
...ownedMetadata,
|
|
162
210
|
args: summarizedArgs
|
|
163
211
|
});
|
|
212
|
+
let result = implementation.call(entered, args);
|
|
213
|
+
|
|
164
214
|
if (!(result instanceof this.constructor)) {
|
|
165
|
-
throw new TypeError(
|
|
215
|
+
throw new TypeError(`${ownedMetadata.op} must return a ChartProgram.`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (this.actionStack.length === 0 && actionCompletionHook !== undefined) {
|
|
219
|
+
result = actionCompletionHook(result, {
|
|
220
|
+
source: this,
|
|
221
|
+
metadata: ownedMetadata,
|
|
222
|
+
args: summarizedArgs
|
|
223
|
+
});
|
|
224
|
+
if (!(result instanceof this.constructor)) {
|
|
225
|
+
throw new TypeError("Action completion hook must return a ChartProgram.");
|
|
226
|
+
}
|
|
166
227
|
}
|
|
228
|
+
|
|
229
|
+
return result._exitAction();
|
|
230
|
+
} catch (error) {
|
|
231
|
+
throw annotateError(error, { code: "action-failed", operation: ownedMetadata.op });
|
|
167
232
|
}
|
|
168
|
-
|
|
169
|
-
return result._exitAction();
|
|
170
233
|
};
|
|
171
234
|
|
|
172
235
|
metadataByWrappedAction.set(wrappedAction, ownedMetadata);
|
|
173
236
|
implementationByWrappedAction.set(wrappedAction, implementation);
|
|
174
237
|
return wrappedAction;
|
|
175
238
|
}
|
|
239
|
+
|
|
240
|
+
// Private built-in convenience: action() owns the object boundary and trace,
|
|
241
|
+
// while each definition supplies its closed option vocabulary exactly once.
|
|
242
|
+
export function closedAction(metadata, options, implementation) {
|
|
243
|
+
return action(metadata, function (args = {}) {
|
|
244
|
+
if (options !== undefined) validateKeys(args, options, metadata.op);
|
|
245
|
+
return implementation.call(this, args);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const detailsByError = new WeakMap();
|
|
2
|
+
|
|
3
|
+
export function getErrorDetails(error) {
|
|
4
|
+
return error !== null && (typeof error === "object" || typeof error === "function")
|
|
5
|
+
? detailsByError.get(error) : undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Attach metadata without modifying the thrown value or its existing class.
|
|
9
|
+
export function annotateError(error, details) {
|
|
10
|
+
if (!(error instanceof Error)) return error;
|
|
11
|
+
const previous = getErrorDetails(error);
|
|
12
|
+
const merged = { ...details, ...previous };
|
|
13
|
+
if (merged.candidates !== undefined) merged.candidates = Object.freeze([...merged.candidates]);
|
|
14
|
+
detailsByError.set(error, Object.freeze(merged));
|
|
15
|
+
return error;
|
|
16
|
+
}
|