@chartguard/schema-generator 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -2
  3. package/dist/index.d.mts +6 -0
  4. package/dist/lib/chartjs-effects.d.mts +5 -0
  5. package/dist/lib/chartjs-plugins.d.mts +23 -0
  6. package/dist/lib/chartjs-scopes.d.mts +30 -0
  7. package/dist/lib/chartjs.d.mts +32 -0
  8. package/dist/lib/deprecation.d.mts +9 -0
  9. package/dist/lib/digest.d.mts +12 -0
  10. package/dist/lib/echarts-components.d.mts +2 -0
  11. package/dist/lib/echarts-deprecations.d.mts +5 -0
  12. package/dist/lib/echarts-handlers.d.mts +2 -0
  13. package/dist/lib/echarts-runtime.d.mts +2 -0
  14. package/dist/lib/echarts.d.mts +26 -0
  15. package/dist/lib/emitter.d.mts +173 -0
  16. package/dist/lib/generate.d.mts +14 -0
  17. package/dist/lib/highcharts-runtime.d.mts +2 -0
  18. package/dist/lib/highcharts.d.mts +26 -0
  19. package/dist/lib/local.d.mts +14 -0
  20. package/dist/lib/lock.d.mts +31 -0
  21. package/dist/lib/nodes.d.mts +4 -0
  22. package/dist/lib/paths.d.mts +22 -0
  23. package/dist/lib/program.d.mts +13 -0
  24. package/dist/lib/provenance.d.mts +46 -0
  25. package/dist/lib/runtime.d.mts +24 -0
  26. package/dist/lib/scriptable.d.mts +13 -0
  27. package/dist/lib/trace.d.mts +8 -0
  28. package/dist/lib/types.d.mts +52 -0
  29. package/index.mjs +20 -0
  30. package/lib/chartjs-effects.mjs +36 -0
  31. package/lib/chartjs-plugins.mjs +41 -0
  32. package/lib/chartjs-scopes.mjs +96 -0
  33. package/lib/chartjs.mjs +275 -0
  34. package/lib/deprecation.mjs +21 -0
  35. package/lib/digest.mjs +33 -0
  36. package/lib/echarts-components.mjs +176 -0
  37. package/lib/echarts-deprecations.mjs +206 -0
  38. package/lib/echarts-handlers.mjs +31 -0
  39. package/lib/echarts-runtime.mjs +311 -0
  40. package/lib/echarts.mjs +89 -0
  41. package/lib/emitter.mjs +333 -0
  42. package/lib/generate.mjs +35 -0
  43. package/lib/highcharts-runtime.mjs +198 -0
  44. package/lib/highcharts.mjs +105 -0
  45. package/lib/local.mjs +33 -0
  46. package/lib/lock.mjs +123 -0
  47. package/lib/nodes.mjs +17 -0
  48. package/lib/paths.mjs +50 -0
  49. package/lib/program.mjs +46 -0
  50. package/lib/provenance.mjs +100 -0
  51. package/lib/runtime.mjs +38 -0
  52. package/lib/scriptable.mjs +57 -0
  53. package/lib/trace.mjs +124 -0
  54. package/lib/types.mjs +74 -0
  55. package/package.json +47 -5
  56. package/upstream-sources.json +102 -0
@@ -0,0 +1,176 @@
1
+ import { resolveIn, upstreamChanged } from './nodes.mjs';
2
+ import { runtimeProvenance } from './runtime.mjs';
3
+
4
+ /**
5
+ * ECharts picks a component's subtype at runtime (`registerSubTypeDefaulter`), while the
6
+ * declarations type every variant's `type` as plain `string`. This pass writes the runtime's
7
+ * choice into the IR, each fact citing the line of the pinned package it comes from.
8
+ */
9
+
10
+ /** Variant definition name → the runtime subtype literal, and the default subtype. */
11
+ const COMPONENTS = {
12
+ dataZoom: {
13
+ variants: {
14
+ SliderDataZoomOption: {
15
+ literal: 'slider',
16
+ file: 'lib/component/dataZoom/SliderZoomModel.js',
17
+ pattern: "SliderZoomModel.type = 'dataZoom.slider'"
18
+ },
19
+ InsideDataZoomOption: {
20
+ literal: 'inside',
21
+ file: 'lib/component/dataZoom/InsideZoomModel.js',
22
+ pattern: "InsideZoomModel.type = 'dataZoom.inside'"
23
+ }
24
+ },
25
+ default: {
26
+ literal: 'slider',
27
+ file: 'lib/component/dataZoom/installCommon.js',
28
+ pattern: "registerSubTypeDefaulter('dataZoom'"
29
+ }
30
+ },
31
+ legend: {
32
+ variants: {
33
+ LegendOption: {
34
+ literal: 'plain',
35
+ file: 'lib/component/legend/LegendModel.js',
36
+ pattern: "LegendModel.type = 'legend.plain'"
37
+ },
38
+ ScrollableLegendOption: {
39
+ literal: 'scroll',
40
+ file: 'lib/component/legend/ScrollableLegendModel.js',
41
+ pattern: "ScrollableLegendModel.type = 'legend.scroll'"
42
+ }
43
+ },
44
+ default: {
45
+ literal: 'plain',
46
+ file: 'lib/component/legend/installLegendPlain.js',
47
+ pattern: "registerSubTypeDefaulter('legend'"
48
+ }
49
+ },
50
+ timeline: {
51
+ // The base option and the slider option are identical; `slider` is the only registered subtype.
52
+ variants: {
53
+ TimelineOption: {
54
+ literal: 'slider',
55
+ file: 'lib/component/timeline/SliderTimelineModel.js',
56
+ pattern: "SliderTimelineModel.type = 'timeline.slider'"
57
+ },
58
+ SliderTimelineOption: {
59
+ literal: 'slider',
60
+ file: 'lib/component/timeline/SliderTimelineModel.js',
61
+ pattern: "SliderTimelineModel.type = 'timeline.slider'"
62
+ }
63
+ },
64
+ default: {
65
+ literal: 'slider',
66
+ file: 'lib/component/timeline/install.js',
67
+ pattern: "registerSubTypeDefaulter('timeline'"
68
+ }
69
+ },
70
+ visualMap: {
71
+ variants: {
72
+ ContinuousVisualMapOption: {
73
+ literal: 'continuous',
74
+ file: 'lib/component/visualMap/ContinuousModel.js',
75
+ pattern: "ContinuousModel.type = 'visualMap.continuous'"
76
+ },
77
+ PiecewiseVisualMapOption: {
78
+ literal: 'piecewise',
79
+ file: 'lib/component/visualMap/PiecewiseModel.js',
80
+ pattern: "PiecewiseModel.type = 'visualMap.piecewise'"
81
+ }
82
+ },
83
+ // The whole expression is the pattern: a change to it fails generation instead of going stale.
84
+ default: {
85
+ literal: 'continuous',
86
+ file: 'lib/component/visualMap/installCommon.js',
87
+ pattern:
88
+ "return !option.categories && (!(option.pieces ? option.pieces.length > 0 : option.splitNumber > 0) || option.calculable) ? 'continuous' : 'piecewise';"
89
+ },
90
+ when: [
91
+ { test: 'truthy', property: 'categories', literal: 'piecewise' },
92
+ { test: 'truthy', property: 'calculable', literal: 'continuous' },
93
+ { test: 'nonEmpty', property: 'pieces', literal: 'piecewise' },
94
+ { test: 'truthy', property: 'pieces', literal: 'continuous' },
95
+ { test: 'positive', property: 'splitNumber', literal: 'piecewise' }
96
+ ]
97
+ }
98
+ };
99
+
100
+ /** Component subtypes are looked up as `<mainType>.<subType>`; one that is not registered throws. */
101
+ const SUBTYPE_LOOKUP = {
102
+ file: 'lib/util/clazz.js',
103
+ pattern: "'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.'"
104
+ };
105
+
106
+ /** Every axis created by `axisModelCreator` resolves its type with `getAxisType`. */
107
+ const AXES = ['xAxis', 'yAxis', 'angleAxis', 'radiusAxis', 'singleAxis', 'parallelAxis'];
108
+ const AXIS_TYPE = {
109
+ file: 'lib/coord/axisModelCreator.js',
110
+ pattern: "option.type || (option.data ? 'category' : 'value')"
111
+ };
112
+
113
+ const changed = upstreamChanged('echarts runtime');
114
+
115
+ const provenance = (root, fact) => runtimeProvenance(root, { sourceId: 'echarts', packageName: 'echarts' }, fact);
116
+
117
+ /** The union a root property holds, and the union of its array items when it is `T | T[]`. */
118
+ function unionsOf(definitions, node) {
119
+ const resolve = resolveIn(definitions);
120
+ const outer = resolve(node);
121
+ if (outer.kind !== 'union') throw changed('expected a union');
122
+ const list = outer.anyOf.map(resolve).find((member) => member.kind === 'array');
123
+ return list === undefined ? [outer] : [outer, resolve(list.items)];
124
+ }
125
+
126
+ const memberName = (member) => (member.kind === 'ref' ? member.ref : undefined);
127
+
128
+ function subtypeComponent(root, definitions, union, [mainType, component]) {
129
+ const objects = union.anyOf.filter((member) => member.kind === 'ref');
130
+ for (const member of objects) {
131
+ const variant = component.variants[memberName(member)];
132
+ if (variant === undefined) throw changed(`unexpected variant ${String(memberName(member))}`);
133
+ const typed = definitions[member.ref].properties.type;
134
+ typed.node = { kind: 'literal', value: variant.literal };
135
+ typed.provenance = provenance(root, variant);
136
+ }
137
+ const indexOf = (literal) =>
138
+ union.anyOf.findIndex((member) => component.variants[memberName(member)]?.literal === literal);
139
+ const defaultWhen = (component.when ?? []).map(({ literal, ...rule }) => ({ ...rule, variant: indexOf(literal) }));
140
+ Object.assign(union, {
141
+ discriminator: 'type',
142
+ defaultVariant: indexOf(component.default.literal),
143
+ ...(defaultWhen.length === 0 ? {} : { defaultWhen }),
144
+ extensible: true,
145
+ registry: { id: 'echarts', prefix: `${mainType}.`, enforced: true },
146
+ // Extension subtypes (`dataZoom.custom`) are added like extension series (CG-02-05 part 2).
147
+ extensionPoint: 'echarts',
148
+ provenance: {
149
+ confidence: 'authoritative',
150
+ sources: [component.default, SUBTYPE_LOOKUP].flatMap((fact) => provenance(root, fact).sources)
151
+ }
152
+ });
153
+ }
154
+
155
+ function axisDefaults(root, definitions, union) {
156
+ const indexOf = (literal) =>
157
+ union.anyOf.findIndex(
158
+ (member) => member.kind === 'ref' && definitions[member.ref].properties.type?.node.value === literal
159
+ );
160
+ const [category, value] = [indexOf('category'), indexOf('value')];
161
+ if (category === -1 || value === -1) throw changed('axis variants');
162
+ Object.assign(union, {
163
+ defaultWhen: [{ test: 'truthy', property: 'data', variant: category }],
164
+ defaultVariant: value,
165
+ provenance: provenance(root, AXIS_TYPE)
166
+ });
167
+ }
168
+
169
+ /** Rewrites, in place, the component and axis unions under the root `EChartsOption` definition. */
170
+ export function resolveComponentSubtypes(root, definitions, option) {
171
+ for (const entry of Object.entries(COMPONENTS))
172
+ for (const union of unionsOf(definitions, option.properties[entry[0]].node))
173
+ subtypeComponent(root, definitions, union, entry);
174
+ for (const name of AXES)
175
+ for (const union of unionsOf(definitions, option.properties[name].node)) axisDefaults(root, definitions, union);
176
+ }
@@ -0,0 +1,206 @@
1
+ import { resolveIn, upstreamChanged } from './nodes.mjs';
2
+ import { runtimePointer } from './runtime.mjs';
3
+
4
+ /**
5
+ * Options echarts 6.1.0 no longer declares but still reads: its backward-compat preprocessor
6
+ * (lib/preprocessor/backwardCompat.js, registered for every chart by `registerPreprocessor`) copies
7
+ * them into their current name and logs "<old> is deprecated; use <new> instead.". Each becomes a
8
+ * deprecated alias of the current option, typed like it. A rename into a sibling key (`to`) is a safe
9
+ * fix: the runtime already reads the value under that name. A copy into another option (`target`,
10
+ * `emphasis.focus`) is named, never offered as a fix.
11
+ *
12
+ * `within` is the path from the series to the object the runtime renames in (`[]` an item of a list).
13
+ * Not modelled, since the runtime never applies them: gauge `pointer.color` (its branch follows the
14
+ * `pie || gauge` branch, so it is unreachable) and `backgroundStyle` (renamed inside
15
+ * `backgroundStyle.itemStyle`, which the bar background does not read).
16
+ */
17
+ const FILE = 'lib/preprocessor/backwardCompat.js';
18
+ const BAR_STYLE =
19
+ "var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']];";
20
+ const barStyle = (within, call) =>
21
+ ['Radius', 'Color', 'Width'].map((part) => ({
22
+ series: ['bar'],
23
+ within,
24
+ key: `barBorder${part}`,
25
+ to: `border${part}`,
26
+ patterns: [BAR_STYLE, ...call]
27
+ }));
28
+ const RENAMES = [
29
+ {
30
+ series: ['line'],
31
+ key: 'clipOverflow',
32
+ to: 'clip',
33
+ patterns: ["deprecateReplaceLog('clipOverflow', 'clip', 'line');"]
34
+ },
35
+ {
36
+ series: ['pie', 'gauge'],
37
+ key: 'clockWise',
38
+ to: 'clockwise',
39
+ patterns: ["deprecateReplaceLog('clockWise', 'clockwise');"]
40
+ },
41
+ { series: ['map'], key: 'mapType', to: 'map', patterns: ["deprecateReplaceLog('mapType', 'map', 'map');"] },
42
+ ...barStyle(['itemStyle'], []),
43
+ ...barStyle(['emphasis', 'itemStyle'], ['compatBarItemStyle(seriesOpt.emphasis);']),
44
+ ...barStyle(['data', '[]', 'itemStyle'], ['compatBarItemStyle(data[i]);']),
45
+ ...barStyle(['data', '[]', 'emphasis', 'itemStyle'], ['compatBarItemStyle(data[i] && data[i].emphasis);']),
46
+ // `option.downplay && !option.blur`: a set `downplay` is an object, so it is truthy whenever it is valid.
47
+ { series: ['sunburst'], key: 'downplay', to: 'blur', patterns: ['if (option.downplay && !option.blur) {'] },
48
+ {
49
+ series: ['sunburst'],
50
+ within: ['data', '[]'],
51
+ key: 'downplay',
52
+ to: 'blur',
53
+ patterns: ['if (option.downplay && !option.blur) {', 'traverseTree(seriesOpt.data, compatSunburstState);']
54
+ },
55
+ {
56
+ series: ['sunburst'],
57
+ key: 'highlightPolicy',
58
+ target: ['emphasis', 'focus'],
59
+ patterns: ["deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst');"]
60
+ },
61
+ // Copied for every series type; an alias only where the series reads `emphasis.scale`.
62
+ {
63
+ series: 'reading',
64
+ key: 'hoverAnimation',
65
+ target: ['emphasis', 'scale'],
66
+ patterns: ["deprecateReplaceLog('hoverAnimation', 'emphasis.scale');"]
67
+ }
68
+ ];
69
+
70
+ const changed = upstreamChanged('echarts runtime');
71
+ const resolve = (definitions, node) => resolveIn(definitions)(node);
72
+
73
+ /** The series variant definition names, by their `type` literal. */
74
+ function seriesVariants(definitions, option) {
75
+ const series = resolve(definitions, option.properties.series.node);
76
+ const list = series.anyOf.map((member) => resolve(definitions, member)).find((member) => member.kind === 'array');
77
+ const items = resolve(definitions, list.items);
78
+ return new Map(items.anyOf.map((member) => [definitions[member.ref].properties.type.node.value, member.ref]));
79
+ }
80
+
81
+ /** The node at `path` (property names) below definition `name`; undefined when it is not declared. */
82
+ function nodeAt(definitions, name, path) {
83
+ let node = { kind: 'ref', ref: name };
84
+ for (const key of path) {
85
+ const property = resolve(definitions, node)?.properties?.[key];
86
+ if (property === undefined) return undefined;
87
+ node = property.node;
88
+ }
89
+ return node;
90
+ }
91
+
92
+ /** The alias property: the current option's value type, deprecated, citing the runtime lines. */
93
+ function alias(node, { key, to, target }, pointers) {
94
+ const replacement = to ?? target.join('.');
95
+ return {
96
+ node,
97
+ provenance: { confidence: 'authoritative', sources: pointers.map((pointer) => ({ source: 'echarts', pointer })) },
98
+ deprecated: {
99
+ message: `${key} is deprecated; use ${replacement} instead.`,
100
+ replacement,
101
+ ...(to === undefined ? {} : { renameTo: to })
102
+ }
103
+ };
104
+ }
105
+
106
+ /** `definition` with the alias of `rename`, typed like its sibling `to` or like the series' `target`. */
107
+ function withAlias(definitions, definition, rename, context) {
108
+ const node =
109
+ rename.to === undefined
110
+ ? nodeAt(definitions, context.variant, rename.target)
111
+ : definition?.properties?.[rename.to]?.node;
112
+ if (node === undefined) throw changed(`${context.name} no longer declares ${rename.to ?? rename.target.join('.')}`);
113
+ const property = alias(node, rename, context.pointers);
114
+ return { ...definition, properties: { ...definition.properties, [rename.key]: property } };
115
+ }
116
+
117
+ /** A copy of `definition` whose references to `from` (its recursion: `children`) name `to` instead. */
118
+ function retargeted(definition, from, to) {
119
+ return JSON.parse(JSON.stringify(definition), (key, value) => (key === 'ref' && value === from ? to : value));
120
+ }
121
+
122
+ /** The list's items, aliased along the rest of the path. */
123
+ function aliasInList(definitions, node, path, at) {
124
+ if (path[0] !== '[]') throw changed(`${at.name} is a list where ${path.join('.')} is expected`);
125
+ const done = aliasAlong(definitions, node.items, path.slice(1), { ...at, name: `${at.name}[]` });
126
+ return { definitions: done.definitions, node: { ...node, items: done.node } };
127
+ }
128
+
129
+ /** The union's object members that have what the path continues with, each aliased. */
130
+ function aliasInUnion(definitions, node, path, at) {
131
+ let next = definitions;
132
+ const anyOf = node.anyOf.map((member) => {
133
+ const object = member.kind === 'ref' ? definitions[member.ref] : undefined;
134
+ if (object?.properties?.[path[0] ?? at.rename.to] === undefined) return member;
135
+ const done = aliasAlong(next, member, path, at);
136
+ next = done.definitions;
137
+ return done.node;
138
+ });
139
+ if (next === definitions) throw changed(`${at.name} has no member with ${path[0] ?? at.rename.to}`);
140
+ return { definitions: next, node: { ...node, anyOf } };
141
+ }
142
+
143
+ /** A copy of the referenced definition the series owns, with the alias, or with the path continued in it. */
144
+ function aliasInDefinition(definitions, node, path, at) {
145
+ const owned = `${at.name}<compat>`;
146
+ const base = definitions[owned] ?? retargeted(definitions[node.ref], node.ref, owned);
147
+ const ref = { kind: 'ref', ref: owned };
148
+ if (path.length === 0)
149
+ return { definitions: { ...definitions, [owned]: withAlias(definitions, base, at.rename, at) }, node: ref };
150
+ const [key, ...rest] = path;
151
+ const property = base.properties?.[key];
152
+ if (property === undefined) throw changed(`${owned} no longer declares ${key}`);
153
+ const done = aliasAlong(definitions, property.node, rest, { ...at, name: `${at.name}.${key}` });
154
+ const definition = { ...base, properties: { ...base.properties, [key]: { ...property, node: done.node } } };
155
+ return { definitions: { ...done.definitions, [owned]: definition }, node: ref };
156
+ }
157
+
158
+ /**
159
+ * The definitions with the alias added inside the object `node` reaches along `path` (`[]`: the
160
+ * object members of a list's items). Every definition on the way is copied under a name the series
161
+ * owns (`<variant>.<path><compat>`), so other series do not gain the alias; a copy keeps its recursion.
162
+ */
163
+ function aliasAlong(definitions, node, path, at) {
164
+ if (node.kind === 'array') return aliasInList(definitions, node, path, at);
165
+ if (node.kind === 'union') return aliasInUnion(definitions, node, path, at);
166
+ if (node.kind !== 'ref') throw changed(`${at.name} is not an object`);
167
+ return aliasInDefinition(definitions, node, path, at);
168
+ }
169
+
170
+ /** One rename applied to one series variant: on the series itself, or in copies of the options it owns. */
171
+ function applyRename(definitions, variant, rename, pointers) {
172
+ const at = { variant, rename, pointers, name: variant };
173
+ const [key, ...rest] = rename.within ?? [];
174
+ if (key === undefined) return { ...definitions, [variant]: withAlias(definitions, definitions[variant], rename, at) };
175
+ const property = definitions[variant].properties[key];
176
+ if (property === undefined) throw changed(`${variant} no longer declares ${key}`);
177
+ const done = aliasAlong(definitions, property.node, rest, { ...at, name: `${variant}.${key}` });
178
+ const properties = { ...definitions[variant].properties, [key]: { ...property, node: done.node } };
179
+ return { ...done.definitions, [variant]: { ...definitions[variant], properties } };
180
+ }
181
+
182
+ /** The series a rename applies to: those it names, or every series that declares its target. */
183
+ function seriesOf(definitions, variants, rename) {
184
+ if (rename.series !== 'reading') return rename.series;
185
+ return [...variants]
186
+ .filter(([, variant]) => nodeAt(definitions, variant, rename.target) !== undefined)
187
+ .map(([type]) => type);
188
+ }
189
+
190
+ /**
191
+ * New definitions with the runtime's renames as deprecated aliases. An alias inside a nested
192
+ * option (`itemStyle`) goes into a copy owned by that series type: the runtime renames it there only.
193
+ */
194
+ export function withDeprecatedAliases(root, definitions, optionName) {
195
+ const variants = seriesVariants(definitions, definitions[optionName]);
196
+ let next = definitions;
197
+ for (const rename of RENAMES) {
198
+ const pointers = rename.patterns.map((pattern) => runtimePointer(root, 'echarts', { file: FILE, pattern }));
199
+ for (const type of seriesOf(definitions, variants, rename)) {
200
+ const variant = variants.get(type);
201
+ if (variant === undefined) throw changed(`no ${type} series`);
202
+ next = applyRename(next, variant, rename, pointers);
203
+ }
204
+ }
205
+ return next;
206
+ }
@@ -0,0 +1,31 @@
1
+ import { runtimePointer, runtimeSection } from './runtime.mjs';
2
+
3
+ /**
4
+ * A root option of a built-in component is handled only when that component is registered:
5
+ * `checkMissingComponents` (lib/model/Global.js) logs an error for every root key listed in
6
+ * `BUITIN_COMPONENTS_MAP` whose component class is missing (`ComponentModel.hasClass(mainType)`).
7
+ * The list is read from the pinned runtime, commented-out entries excluded, so it cannot go stale.
8
+ */
9
+ const COMPONENTS = { file: 'lib/model/Global.js', pattern: 'var BUITIN_COMPONENTS_MAP = {', end: '};' };
10
+ const CHECK = { file: 'lib/model/Global.js', pattern: 'error("Component " + mainType + " is used but not imported.' };
11
+ const ENTRY = /^\s*(\w+): '\w+',?$/;
12
+
13
+ function componentKeys(root) {
14
+ const { pointer, lines } = runtimeSection(root, 'echarts', COMPONENTS);
15
+ const keys = lines.flatMap((line) => ENTRY.exec(line)?.[1] ?? []);
16
+ if (keys.length === 0) throw new Error(`echarts runtime changed: no component keys at ${pointer}`);
17
+ return { keys, pointer };
18
+ }
19
+
20
+ /** Marks, in place, every root option of `EChartsOption` that names a built-in component with its handler. */
21
+ export function addComponentHandlers(root, option) {
22
+ const { keys, pointer } = componentKeys(root);
23
+ const cited = [pointer, runtimePointer(root, 'echarts', CHECK)].map((at) => ({ source: 'echarts', pointer: at }));
24
+ const handled = keys.filter((key) => Object.hasOwn(option.properties, key));
25
+ for (const key of handled) {
26
+ const property = option.properties[key];
27
+ property.handledBy = { id: 'echarts', name: key, enforced: true };
28
+ property.provenance = { ...property.provenance, sources: [...property.provenance.sources, ...cited] };
29
+ }
30
+ return handled;
31
+ }