ggaction 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/README.md +20 -11
- package/knowledge/action-cards.json +1 -1
- package/knowledge/intent-taxonomy.json +1 -1
- package/knowledge/mcp-resources.json +1 -1
- package/package.json +8 -3
- package/src/actions/basic.js +2 -2
- package/src/actions/boxPlots/components.js +14 -4
- package/src/actions/boxPlots/edit.js +31 -5
- package/src/actions/boxPlots/materialize.js +11 -3
- package/src/actions/boxPlots/options.js +48 -16
- package/src/actions/categoryOrder/index.js +15 -4
- package/src/actions/charts/area.js +5 -1
- package/src/actions/charts/bar.js +2 -1
- package/src/actions/charts/density.js +6 -2
- package/src/actions/charts/ecdf.js +4 -1
- package/src/actions/charts/endpoints.js +25 -3
- package/src/actions/charts/heatmap.js +2 -1
- package/src/actions/charts/histogram.js +4 -3
- package/src/actions/charts/horizon.js +3 -1
- package/src/actions/charts/interval-regression.js +5 -2
- package/src/actions/charts/line.js +3 -1
- package/src/actions/charts/parallel.js +8 -2
- package/src/actions/charts/pie.js +10 -2
- package/src/actions/charts/polar.js +15 -4
- package/src/actions/charts/radar.js +8 -2
- package/src/actions/charts/radial.js +10 -2
- package/src/actions/charts/rug-strip.js +6 -2
- package/src/actions/charts/scatter.js +3 -1
- package/src/actions/composition/actions.js +13 -4
- package/src/actions/coordinates/actions.js +1 -1
- package/src/actions/coordinates/edit.js +76 -0
- package/src/actions/data/bin.js +1 -1
- package/src/actions/data/bin2d.js +57 -185
- package/src/actions/data/complete.js +26 -0
- package/src/actions/data/computed.js +2 -2
- package/src/actions/data/create.js +2 -2
- package/src/actions/data/density.js +12 -20
- package/src/actions/data/derived.js +47 -17
- package/src/actions/data/edit.js +539 -0
- package/src/actions/data/filter.js +14 -2
- package/src/actions/data/impute.js +26 -0
- package/src/actions/data/index.js +14 -0
- package/src/actions/data/normalize.js +27 -0
- package/src/actions/data/regression.js +16 -21
- package/src/actions/data/revisionBindings.js +145 -0
- package/src/actions/data/shared.js +20 -4
- package/src/actions/data/summary.js +1 -1
- package/src/actions/data/timeUnit.js +4 -1
- package/src/actions/data/window.js +1 -1
- package/src/actions/encodings/channels.js +415 -0
- package/src/actions/encodings/color/index.js +2 -1
- package/src/actions/encodings/density.js +5 -2
- package/src/actions/encodings/histogram.js +3 -2
- package/src/actions/encodings/index.js +4 -0
- package/src/actions/encodings/offset.js +20 -13
- package/src/actions/encodings/position/apply.js +53 -7
- package/src/actions/encodings/position/index.js +10 -2
- package/src/actions/encodings/position/policies/bar.js +9 -0
- package/src/actions/encodings/position/resolve.js +24 -7
- package/src/actions/encodings/remove.js +9 -4
- package/src/actions/encodings/ruleAppearance.js +1 -23
- package/src/actions/encodings/seriesLayout.js +1 -0
- package/src/actions/encodings/shared.js +82 -14
- package/src/actions/encodings/stroke.js +226 -0
- package/src/actions/errorBands/create.js +23 -3
- package/src/actions/errorBands/edit.js +21 -6
- package/src/actions/errorBars/create.js +35 -7
- package/src/actions/errorBars/edit.js +19 -4
- package/src/actions/facets/actions.js +237 -57
- package/src/actions/facets/derive.js +91 -26
- package/src/actions/gradientPlots/components.js +10 -2
- package/src/actions/gradientPlots/edit.js +14 -4
- package/src/actions/gradientPlots/materialize.js +3 -1
- package/src/actions/gradientPlots/options.js +10 -2
- package/src/actions/guides/applicability.js +8 -2
- package/src/actions/guides/axes/labels.js +58 -12
- package/src/actions/guides/axes/lines.js +6 -2
- package/src/actions/guides/axes/parallel/resolve.js +6 -2
- package/src/actions/guides/axes/tickGroups.js +2 -1
- package/src/actions/guides/axes/ticks.js +6 -2
- package/src/actions/guides/axes/titles.js +11 -3
- package/src/actions/guides/grids/resolve.js +7 -2
- package/src/actions/guides/guides.js +19 -3
- package/src/actions/guides/legends/blocks.js +426 -0
- package/src/actions/guides/legends/categorical/actions.js +134 -48
- package/src/actions/guides/legends/categorical/components.js +17 -12
- package/src/actions/guides/legends/categorical/index.js +37 -33
- package/src/actions/guides/legends/categorical/layout.js +97 -15
- package/src/actions/guides/legends/categorical/options.js +2 -2
- package/src/actions/guides/legends/categorical/recipes.js +111 -20
- package/src/actions/guides/legends/categorical/resolve.js +12 -8
- package/src/actions/guides/legends/categorical/symbols.js +85 -30
- package/src/actions/guides/legends/continuous/common.js +30 -12
- package/src/actions/guides/legends/continuous/gradient.js +10 -7
- package/src/actions/guides/legends/continuous/index.js +2 -0
- package/src/actions/guides/legends/continuous/interval.js +8 -3
- package/src/actions/guides/legends/continuous/opacity.js +8 -5
- package/src/actions/guides/legends/continuous/stroke.js +525 -0
- package/src/actions/guides/legends/creation.js +14 -1
- package/src/actions/guides/legends/edit.js +158 -59
- package/src/actions/guides/legends/index.js +2 -0
- package/src/actions/guides/legends/lane.js +54 -19
- package/src/actions/guides/legends/lifecycle.js +19 -8
- package/src/actions/guides/legends/remove.js +13 -3
- package/src/actions/guides/legends/sampling.js +140 -0
- package/src/actions/guides/legends/size.js +93 -40
- package/src/actions/guides/legends/strokeWidth.js +34 -28
- package/src/actions/guides/legends/target.js +112 -1
- package/src/actions/guides/legends/transition.js +165 -16
- package/src/actions/guides/polar/axes/facade.js +4 -4
- package/src/actions/guides/polar/axes/labels.js +24 -5
- package/src/actions/guides/polar/axes/lines.js +10 -4
- package/src/actions/guides/polar/axes/shared.js +16 -0
- package/src/actions/guides/polar/axes/ticks.js +1 -1
- package/src/actions/guides/polar/axes/titles.js +1 -1
- package/src/actions/guides/polar/grids.js +1 -1
- package/src/actions/guides/polar/resolve.js +12 -8
- package/src/actions/index.js +14 -3
- package/src/actions/marks/arc/actions.js +77 -19
- package/src/actions/marks/area/actions.js +34 -9
- package/src/actions/marks/area/materialize.js +17 -1
- package/src/actions/marks/bar/create.js +11 -2
- package/src/actions/marks/bar/edit.js +20 -3
- package/src/actions/marks/bar/materialize.js +22 -2
- package/src/actions/marks/index.js +10 -1
- package/src/actions/marks/line/actions.js +37 -14
- package/src/actions/marks/line/materialize.js +29 -10
- package/src/actions/marks/point/create.js +8 -2
- package/src/actions/marks/point/edit.js +18 -6
- package/src/actions/marks/point/materialize.js +49 -15
- package/src/actions/marks/rect/actions.js +33 -12
- package/src/actions/marks/references.js +601 -30
- package/src/actions/marks/remove.js +71 -1
- package/src/actions/marks/rule/actions.js +47 -10
- package/src/actions/marks/shared.js +1 -1
- package/src/actions/marks/text/actions.js +74 -9
- package/src/actions/marks/text/index.js +4 -0
- package/src/actions/marks/text/layout.js +154 -6
- package/src/actions/marks/text/removal.js +173 -0
- package/src/actions/marks/text/selection.js +73 -0
- package/src/actions/marks/tick/actions.js +22 -5
- package/src/actions/primitives/editGraphics.js +26 -0
- package/src/actions/primitives/semantic.js +2 -1
- package/src/actions/primitives/semanticAction.js +83 -10
- package/src/actions/primitives/semanticValidation/guide.js +1 -1
- package/src/actions/primitives/semanticValidation/index.js +9 -1
- package/src/actions/primitives/semanticValidation/layer.js +5 -0
- package/src/actions/primitives/semanticValidation/scale.js +28 -7
- package/src/actions/regression/components.js +26 -11
- package/src/actions/regression/create.js +10 -2
- package/src/actions/regression/edit.js +21 -7
- package/src/actions/resources/index.js +7 -0
- package/src/actions/resources/remove.js +172 -0
- package/src/actions/scales/channels.js +212 -0
- package/src/actions/scales/consumers/common.js +1 -1
- package/src/actions/scales/consumers/families.js +3 -2
- package/src/actions/scales/consumers/index.js +19 -2
- package/src/actions/scales/consumers/seriesLayout.js +28 -14
- package/src/actions/scales/create.js +54 -22
- package/src/actions/scales/definitions.js +94 -40
- package/src/actions/scales/edit.js +66 -11
- package/src/actions/scales/editPolicy.js +81 -11
- package/src/actions/scales/index.js +6 -0
- package/src/actions/scales/materialize.js +4 -3
- package/src/actions/scales/offset.js +48 -0
- package/src/actions/scales/parallel.js +71 -0
- package/src/actions/scales/preview.js +111 -10
- package/src/actions/selection/actions.js +154 -30
- package/src/actions/selection/index.js +3 -0
- package/src/actions/theme/actions.js +68 -13
- package/src/actions/theme/composition.js +238 -0
- package/src/actions/theme/index.js +9 -2
- package/src/actions/theme/reconcile.js +315 -67
- package/src/actions/theme/state.js +160 -0
- package/src/actions/violinPlots/create.js +5 -2
- package/src/actions/violinPlots/edit.js +1 -0
- package/src/core/action.js +10 -0
- package/src/core/compositionState.js +6 -1
- package/src/core/programState.js +20 -0
- package/src/core/resourceReferences.js +332 -0
- package/src/core/vocabulary.js +16 -3
- package/src/grammar/bars/aggregate.js +4 -1
- package/src/grammar/bars/geometry.js +44 -0
- package/src/grammar/bin.js +54 -9
- package/src/grammar/complete.js +279 -0
- package/src/grammar/computed.js +265 -64
- package/src/grammar/coordinates.js +37 -0
- package/src/grammar/density.js +239 -37
- package/src/grammar/displayLabels.js +57 -0
- package/src/grammar/facets/dependencies.js +133 -1
- package/src/grammar/facets/guides.js +3 -0
- package/src/grammar/facets/index.js +7 -47
- package/src/grammar/facets/scales.js +15 -18
- package/src/grammar/impute.js +340 -0
- package/src/grammar/markLabelSelection.js +57 -0
- package/src/grammar/markLabels.js +18 -1
- package/src/grammar/normalize.js +366 -0
- package/src/grammar/pathSeries.js +26 -8
- package/src/grammar/pointShapes.js +13 -4
- package/src/grammar/polar.js +90 -8
- package/src/grammar/regression/index.js +1 -0
- package/src/grammar/regression/parameters.js +23 -0
- package/src/grammar/roundedRect.js +180 -0
- package/src/grammar/rules.js +1 -0
- package/src/grammar/scales/colorConsumers.js +20 -1
- package/src/grammar/scales/continuous.js +2 -2
- package/src/grammar/scales/definition.js +27 -4
- package/src/grammar/scales/index.js +1 -0
- package/src/grammar/scales/ordinal.js +12 -3
- package/src/grammar/scales/policies.js +2 -2
- package/src/grammar/scales/size.js +456 -0
- package/src/grammar/schemas/concreteGraphic.js +14 -2
- package/src/grammar/schemas/graphic.js +15 -0
- package/src/grammar/schemas/graphicBounds.js +179 -30
- package/src/grammar/schemas/semanticPath.js +7 -2
- package/src/grammar/statisticalReference.js +103 -0
- package/src/grammar/strokeStyle.js +77 -0
- package/src/grammar/summary.js +78 -16
- package/src/grammar/timeUnit.js +216 -5
- package/src/grammar/transformTopology.js +4 -0
- package/src/grammar/transforms.js +394 -28
- package/src/grammar/weightedStatistics.js +374 -0
- package/src/grammar/window.js +221 -16
- package/src/layout/aspect.js +80 -0
- package/src/layout/facets.js +59 -5
- package/src/layout/labels.js +343 -0
- package/src/materialization/bars/aggregate.js +25 -2
- package/src/materialization/bars/histogram.js +84 -24
- package/src/materialization/bars/ranged.js +19 -3
- package/src/materialization/bars/resolve.js +7 -4
- package/src/materialization/composition.js +3 -1
- package/src/materialization/coordinateBounds.js +204 -0
- package/src/materialization/dependencies.js +36 -0
- package/src/materialization/facetGuides/legacyCategorical.js +3 -0
- package/src/materialization/facetGuides/placement.js +8 -2
- package/src/materialization/facetGuides/preparation.js +12 -7
- package/src/materialization/facetHeaders.js +346 -0
- package/src/materialization/facets.js +15 -65
- package/src/materialization/guides/resources.js +26 -0
- package/src/materialization/legends.js +1 -1
- package/src/materialization/marks/index.js +11 -5
- package/src/materialization/marks/policies.js +2 -1
- package/src/materialization/rect.js +25 -12
- package/src/materialization/rectConfig.js +5 -1
- package/src/materialization/scales/map.js +5 -3
- package/src/materialization/scales/policies/binnedPosition.js +2 -2
- package/src/materialization/scales/policies/offset.js +50 -8
- package/src/materialization/scales/policies/series.js +4 -1
- package/src/materialization/scales/resolve.js +22 -6
- package/src/materialization/selection/items/arc.js +28 -4
- package/src/materialization/selection/items/bar.js +30 -3
- package/src/materialization/selection/items/common.js +19 -4
- package/src/materialization/selection/items/rect.js +28 -4
- package/src/materialization/selection/path.js +1 -1
- package/src/materialization/selection/point.js +3 -1
- package/src/materialization/text.js +265 -14
- package/src/renderers/canvas/circle.js +2 -3
- package/src/renderers/canvas/line.js +2 -3
- package/src/renderers/canvas/native.js +4 -4
- package/src/renderers/canvas/path.js +2 -3
- package/src/renderers/canvas/rect.js +2 -3
- package/src/renderers/canvas/stroke.js +11 -0
- package/src/renderers/svg.js +19 -0
- package/src/selectors/datasets.js +32 -1
- package/src/selectors/layers.js +4 -0
- package/src/theme/defaults.js +101 -0
- package/types/basic.d.ts +6 -0
- package/types/index.d.ts +85 -0
- package/types/program.d.ts +910 -117
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import { action } 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();
|
|
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
|
+
add(resolved.dataset, resolved, definition, rootSource);
|
|
278
|
+
if (dependents === "recompute") {
|
|
279
|
+
for (const dataset of downstream) {
|
|
280
|
+
const policy = findTransformPolicy(dataset.transform[0].type);
|
|
281
|
+
const transform = policy.replayTransform?.(dataset.transform[0]) ??
|
|
282
|
+
dataset.transform[0];
|
|
283
|
+
add(dataset, ownerForCurrent(program, dataset), transform);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return { revisions, replacements };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function directLayerConsumers(program, data) {
|
|
290
|
+
return program.semanticSpec.layers
|
|
291
|
+
.filter(layer => layer.data === data)
|
|
292
|
+
.map(layer => layer.id);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function applyRevisionPlan(program, resolved, plan) {
|
|
296
|
+
const root = plan.revisions[0];
|
|
297
|
+
const changes = outputFieldChanges(
|
|
298
|
+
resolved.dataset.transform[0],
|
|
299
|
+
root.transform
|
|
300
|
+
);
|
|
301
|
+
let next = program;
|
|
302
|
+
for (const revision of plan.revisions) {
|
|
303
|
+
const policy = findTransformPolicy(revision.transform.type);
|
|
304
|
+
next = next.createDerivedData({
|
|
305
|
+
id: revision.id,
|
|
306
|
+
source: revision.source,
|
|
307
|
+
transform: [revision.transform]
|
|
308
|
+
});
|
|
309
|
+
if (revision.transform.type === "markFilter") {
|
|
310
|
+
next = next.rebindLayerData({
|
|
311
|
+
id: revision.transform.target,
|
|
312
|
+
data: revision.source
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
next = next[policy.materializeOp]({ id: revision.id });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const consumers = plan.revisions.flatMap(revision =>
|
|
319
|
+
directLayerConsumers(program, revision.old).map(id => ({
|
|
320
|
+
id,
|
|
321
|
+
old: revision.old,
|
|
322
|
+
data: revision.id,
|
|
323
|
+
root: revision.old === root.old
|
|
324
|
+
}))
|
|
325
|
+
);
|
|
326
|
+
for (const consumer of consumers) {
|
|
327
|
+
next = next.rebindLayerData({ id: consumer.id, data: consumer.data });
|
|
328
|
+
}
|
|
329
|
+
for (const consumer of consumers) {
|
|
330
|
+
if (consumer.root) {
|
|
331
|
+
next = rebindLayerOutputFields(next, consumer.id, changes);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
next = rebindMaterializationDataReferences(next, plan.replacements);
|
|
336
|
+
for (const revision of plan.revisions) {
|
|
337
|
+
if (revision.owner !== undefined) {
|
|
338
|
+
next = next._withMaterializationConfig(
|
|
339
|
+
["data", revision.family, revision.owner],
|
|
340
|
+
{ current: revision.id }
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const consumer of consumers) {
|
|
345
|
+
next = applyLayerDataRematerialization(next, consumer.id);
|
|
346
|
+
}
|
|
347
|
+
for (const revision of [...plan.revisions].reverse()) {
|
|
348
|
+
next = next.releaseDerivedData({ id: revision.old });
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const currentData = plan.replacements.get(program.context.currentData) ??
|
|
352
|
+
program.context.currentData;
|
|
353
|
+
return next._withContext({ ...program.context, currentData });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function sameRequested(left, right) {
|
|
357
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function reviseDerivedData(program, {
|
|
361
|
+
resolved,
|
|
362
|
+
definition,
|
|
363
|
+
dependents = "reject",
|
|
364
|
+
source = resolved.dataset.source
|
|
365
|
+
}) {
|
|
366
|
+
const policy = normalizeDerivedDependentPolicy(dependents);
|
|
367
|
+
const plan = buildRevisionPlan(
|
|
368
|
+
program,
|
|
369
|
+
resolved,
|
|
370
|
+
definition,
|
|
371
|
+
policy,
|
|
372
|
+
source
|
|
373
|
+
);
|
|
374
|
+
applyRevisionPlan(program, resolved, plan);
|
|
375
|
+
return applyRevisionPlan(program, resolved, plan);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export const editDerivedData = action(
|
|
379
|
+
{
|
|
380
|
+
op: "editDerivedData",
|
|
381
|
+
description: "Atomically revise one standalone derived-data definition."
|
|
382
|
+
},
|
|
383
|
+
function (args = {}) {
|
|
384
|
+
validateKeys(args, GENERIC_OPTIONS, "editDerivedData");
|
|
385
|
+
if (!Object.hasOwn(args, "target")) {
|
|
386
|
+
throw new TypeError("editDerivedData requires target.");
|
|
387
|
+
}
|
|
388
|
+
if (!isPlainObject(args.definition)) {
|
|
389
|
+
throw new TypeError("editDerivedData definition must be a plain object.");
|
|
390
|
+
}
|
|
391
|
+
for (const forbidden of ["id", "source", "resolved", "current"]) {
|
|
392
|
+
if (Object.hasOwn(args.definition, forbidden)) {
|
|
393
|
+
throw new Error(`editDerivedData definition cannot include ${forbidden}.`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const dependents = normalizeDerivedDependentPolicy(args.dependents);
|
|
397
|
+
const resolved = resolveDerivedDataOwner(
|
|
398
|
+
this,
|
|
399
|
+
args.target,
|
|
400
|
+
args.definition.type
|
|
401
|
+
);
|
|
402
|
+
const definition = requestedDatasetTransform(args.definition);
|
|
403
|
+
findTransformPolicy(definition.type).validate(definition);
|
|
404
|
+
const previous = requestedDatasetTransform(resolved.dataset.transform[0]);
|
|
405
|
+
if (sameRequested(definition, previous)) return this;
|
|
406
|
+
|
|
407
|
+
return reviseDerivedData(this, { resolved, definition, dependents });
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
function focusedEditor(op, type, properties) {
|
|
412
|
+
const supported = Object.freeze(["target", "dependents", ...properties]);
|
|
413
|
+
return action(
|
|
414
|
+
{ op, description: `Partially revise one standalone ${type} dataset.` },
|
|
415
|
+
function (args = {}) {
|
|
416
|
+
validateKeys(args, supported, op);
|
|
417
|
+
if (!Object.hasOwn(args, "target")) {
|
|
418
|
+
throw new TypeError(`${op} requires target.`);
|
|
419
|
+
}
|
|
420
|
+
const patch = Object.fromEntries(Object.entries(args).filter(
|
|
421
|
+
([key]) => key !== "target" && key !== "dependents"
|
|
422
|
+
));
|
|
423
|
+
if (Object.keys(patch).length === 0) {
|
|
424
|
+
throw new Error(`${op} requires at least one transform option.`);
|
|
425
|
+
}
|
|
426
|
+
const undefinedKey = Object.keys(patch).find(
|
|
427
|
+
key => patch[key] === undefined
|
|
428
|
+
);
|
|
429
|
+
if (undefinedKey !== undefined) {
|
|
430
|
+
throw new TypeError(
|
|
431
|
+
`${op} ${undefinedKey} cannot be undefined; omission preserves it.`
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
const resolved = resolveDerivedDataOwner(this, args.target, type);
|
|
435
|
+
const definition = normalizeDatasetTransformEdit(
|
|
436
|
+
resolved.dataset.transform[0],
|
|
437
|
+
patch,
|
|
438
|
+
resolved.owner
|
|
439
|
+
);
|
|
440
|
+
return this.editDerivedData({
|
|
441
|
+
target: args.target,
|
|
442
|
+
definition,
|
|
443
|
+
...(args.dependents === undefined
|
|
444
|
+
? {}
|
|
445
|
+
: { dependents: args.dependents })
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export const editComputedData = focusedEditor(
|
|
452
|
+
"editComputedData", "computed", ["as", "expression"]
|
|
453
|
+
);
|
|
454
|
+
export const editFilteredData = focusedEditor(
|
|
455
|
+
"editFilteredData", "filter", ["field", "oneOf", "predicate", "range"]
|
|
456
|
+
);
|
|
457
|
+
export const editFoldData = focusedEditor(
|
|
458
|
+
"editFoldData", "fold", ["fields", "as"]
|
|
459
|
+
);
|
|
460
|
+
export const editSummaryData = focusedEditor(
|
|
461
|
+
"editSummaryData", "summary", ["groupBy", "aggregates", "members", "weight"]
|
|
462
|
+
);
|
|
463
|
+
export const editBinData = focusedEditor(
|
|
464
|
+
"editBinData", "bin", [
|
|
465
|
+
"field", "maxBins", "step", "boundaries", "extent", "nice", "zero",
|
|
466
|
+
"includeEmpty", "members", "as", "weight"
|
|
467
|
+
]
|
|
468
|
+
);
|
|
469
|
+
export const editTimeUnitData = focusedEditor(
|
|
470
|
+
"editTimeUnitData", "timeUnit", [
|
|
471
|
+
"field", "unit", "as", "temporalUnit", "timeZone", "weekStartsOn",
|
|
472
|
+
"weekRule"
|
|
473
|
+
]
|
|
474
|
+
);
|
|
475
|
+
export const editWindowData = focusedEditor(
|
|
476
|
+
"editWindowData", "window", [
|
|
477
|
+
"partitionBy", "sortBy", "operations", "temporalUnit"
|
|
478
|
+
]
|
|
479
|
+
);
|
|
480
|
+
export const editDensityData = focusedEditor(
|
|
481
|
+
"editDensityData", "density", [
|
|
482
|
+
"field", "groupBy", "bandwidth", "extent", "steps", "kernel",
|
|
483
|
+
"normalization", "as", "weight"
|
|
484
|
+
]
|
|
485
|
+
);
|
|
486
|
+
export const editStackData = focusedEditor(
|
|
487
|
+
"editStackData", "stack", ["category", "group", "value", "mode", "as"]
|
|
488
|
+
);
|
|
489
|
+
export const editRegressionData = focusedEditor(
|
|
490
|
+
"editRegressionData", "regression", [
|
|
491
|
+
"x", "y", "groupBy", "method", "degree", "span", "confidenceMethod",
|
|
492
|
+
"level", "confidence", "interval"
|
|
493
|
+
]
|
|
494
|
+
);
|
|
495
|
+
export const editIntervalData = focusedEditor(
|
|
496
|
+
"editIntervalData", "interval", [
|
|
497
|
+
"field", "groupBy", "center", "extent", "method", "level", "as"
|
|
498
|
+
]
|
|
499
|
+
);
|
|
500
|
+
export const editECDFData = focusedEditor(
|
|
501
|
+
"editECDFData", "ecdf", [
|
|
502
|
+
"field", "groupBy", "weight", "missing", "as"
|
|
503
|
+
]
|
|
504
|
+
);
|
|
505
|
+
export const editNormalizedData = focusedEditor(
|
|
506
|
+
"editNormalizedData", "normalize", [
|
|
507
|
+
"field", "as", "groupBy", "method", "variance", "zeroDenominator",
|
|
508
|
+
"baseline", "sortBy"
|
|
509
|
+
]
|
|
510
|
+
);
|
|
511
|
+
export const editCompleteData = focusedEditor(
|
|
512
|
+
"editCompleteData", "complete", [
|
|
513
|
+
"key", "groupBy", "values", "sequence", "fill", "members"
|
|
514
|
+
]
|
|
515
|
+
);
|
|
516
|
+
export const editImputedData = focusedEditor(
|
|
517
|
+
"editImputedData", "impute", [
|
|
518
|
+
"fields", "groupBy", "sortBy", "method", "value", "edges", "maxGap"
|
|
519
|
+
]
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
export const EDIT_DERIVED_DATA_ACTIONS = Object.freeze({
|
|
523
|
+
editDerivedData,
|
|
524
|
+
editComputedData,
|
|
525
|
+
editFilteredData,
|
|
526
|
+
editFoldData,
|
|
527
|
+
editSummaryData,
|
|
528
|
+
editBinData,
|
|
529
|
+
editTimeUnitData,
|
|
530
|
+
editWindowData,
|
|
531
|
+
editDensityData,
|
|
532
|
+
editStackData,
|
|
533
|
+
editRegressionData,
|
|
534
|
+
editIntervalData,
|
|
535
|
+
editECDFData,
|
|
536
|
+
editNormalizedData,
|
|
537
|
+
editCompleteData,
|
|
538
|
+
editImputedData
|
|
539
|
+
});
|
|
@@ -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";
|
|
@@ -70,18 +71,29 @@ export const filterData = action(
|
|
|
70
71
|
function (args = {}) {
|
|
71
72
|
validateKeys(args, OPTIONS, "filterData");
|
|
72
73
|
const id = validateUserId(args.id, "Filtered dataset id");
|
|
73
|
-
const
|
|
74
|
+
const requestedSource = validateUserId(
|
|
74
75
|
args.source ?? this.context.currentData,
|
|
75
76
|
"Source dataset id"
|
|
76
77
|
);
|
|
78
|
+
const source = resolveDatasetReference(
|
|
79
|
+
this,
|
|
80
|
+
requestedSource,
|
|
81
|
+
"Filter source dataset"
|
|
82
|
+
).id;
|
|
77
83
|
const transform = normalizeFilterTransform(args);
|
|
78
|
-
|
|
84
|
+
const next = this
|
|
79
85
|
.createDerivedData({
|
|
80
86
|
id,
|
|
81
87
|
source,
|
|
82
88
|
transform: [transform]
|
|
83
89
|
})
|
|
84
90
|
.materializeFilteredData({ id });
|
|
91
|
+
return this.actionStack.length === 1
|
|
92
|
+
? next._withMaterializationConfig(
|
|
93
|
+
["data", "filter", id],
|
|
94
|
+
{ current: id }
|
|
95
|
+
)
|
|
96
|
+
: next;
|
|
85
97
|
}
|
|
86
98
|
);
|
|
87
99
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deriveImputedRows,
|
|
3
|
+
normalizeImputeTransform
|
|
4
|
+
} from "../../grammar/impute.js";
|
|
5
|
+
import { derivedCreator, derivedMaterializer } from "./shared.js";
|
|
6
|
+
|
|
7
|
+
const OPTIONS = Object.freeze([
|
|
8
|
+
"id", "source", "fields", "groupBy", "sortBy", "method", "value", "edges", "maxGap"
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
export const materializeImputedData = derivedMaterializer(
|
|
12
|
+
"materializeImputedData",
|
|
13
|
+
"Materialize one immutable missing-value dataset.",
|
|
14
|
+
"impute",
|
|
15
|
+
deriveImputedRows
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const createImputedData = derivedCreator(
|
|
19
|
+
"createImputedData",
|
|
20
|
+
"Replace explicit missing cells within independent groups.",
|
|
21
|
+
OPTIONS,
|
|
22
|
+
"Imputed dataset id",
|
|
23
|
+
"Imputed source dataset id",
|
|
24
|
+
normalizeImputeTransform,
|
|
25
|
+
"materializeImputedData"
|
|
26
|
+
);
|
|
@@ -40,10 +40,18 @@ import { createSummaryData, materializeSummaryData } from "./summary.js";
|
|
|
40
40
|
import { createBinData, materializeBinData } from "./bin.js";
|
|
41
41
|
import { createFoldData, materializeFoldData } from "./fold.js";
|
|
42
42
|
import { createComputedData, materializeComputedData } from "./computed.js";
|
|
43
|
+
import {
|
|
44
|
+
createNormalizedData,
|
|
45
|
+
materializeNormalizedData
|
|
46
|
+
} from "./normalize.js";
|
|
47
|
+
import { createCompleteData, materializeCompleteData } from "./complete.js";
|
|
48
|
+
import { createImputedData, materializeImputedData } from "./impute.js";
|
|
43
49
|
import { createStackData, materializeStackData } from "./stack.js";
|
|
50
|
+
import { EDIT_DERIVED_DATA_ACTIONS } from "./edit.js";
|
|
44
51
|
import { createBoxSummaryData, createBoxOutlierData, materializeBoxSummaryData, materializeBoxOutlierData } from "./box.js";
|
|
45
52
|
|
|
46
53
|
export function registerDataActions(ProgramClass) {
|
|
54
|
+
Object.assign(ProgramClass.prototype, EDIT_DERIVED_DATA_ACTIONS);
|
|
47
55
|
ProgramClass.prototype.createData = createData;
|
|
48
56
|
ProgramClass.prototype.createDerivedData = createDerivedData;
|
|
49
57
|
ProgramClass.prototype.bindMarkData = bindMarkData;
|
|
@@ -80,6 +88,12 @@ export function registerDataActions(ProgramClass) {
|
|
|
80
88
|
ProgramClass.prototype.materializeFoldData = materializeFoldData;
|
|
81
89
|
ProgramClass.prototype.createComputedData = createComputedData;
|
|
82
90
|
ProgramClass.prototype.materializeComputedData = materializeComputedData;
|
|
91
|
+
ProgramClass.prototype.createNormalizedData = createNormalizedData;
|
|
92
|
+
ProgramClass.prototype.materializeNormalizedData = materializeNormalizedData;
|
|
93
|
+
ProgramClass.prototype.createCompleteData = createCompleteData;
|
|
94
|
+
ProgramClass.prototype.materializeCompleteData = materializeCompleteData;
|
|
95
|
+
ProgramClass.prototype.createImputedData = createImputedData;
|
|
96
|
+
ProgramClass.prototype.materializeImputedData = materializeImputedData;
|
|
83
97
|
ProgramClass.prototype.createStackData = createStackData;
|
|
84
98
|
ProgramClass.prototype.materializeStackData = materializeStackData;
|
|
85
99
|
ProgramClass.prototype.createTimeUnitData = createTimeUnitData;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deriveNormalizedRows,
|
|
3
|
+
normalizeNormalizeTransform
|
|
4
|
+
} from "../../grammar/normalize.js";
|
|
5
|
+
import { derivedCreator, derivedMaterializer } from "./shared.js";
|
|
6
|
+
|
|
7
|
+
const OPTIONS = Object.freeze([
|
|
8
|
+
"id", "source", "field", "as", "groupBy", "method",
|
|
9
|
+
"variance", "zeroDenominator", "baseline", "sortBy"
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
export const materializeNormalizedData = derivedMaterializer(
|
|
13
|
+
"materializeNormalizedData",
|
|
14
|
+
"Materialize one immutable grouped normalization dataset.",
|
|
15
|
+
"normalize",
|
|
16
|
+
deriveNormalizedRows
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export const createNormalizedData = derivedCreator(
|
|
20
|
+
"createNormalizedData",
|
|
21
|
+
"Create a row-preserving grouped normalization dataset.",
|
|
22
|
+
OPTIONS,
|
|
23
|
+
"Normalized dataset id",
|
|
24
|
+
"Normalized source dataset id",
|
|
25
|
+
normalizeNormalizeTransform,
|
|
26
|
+
"materializeNormalizedData"
|
|
27
|
+
);
|