ggaction 0.0.16 → 0.0.17

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 (83) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -1
  3. package/knowledge/action-cards.json +1 -1
  4. package/knowledge/intent-taxonomy.json +1 -1
  5. package/knowledge/mcp-resources.json +1 -1
  6. package/package.json +8 -2
  7. package/src/actions/data/bin.js +1 -1
  8. package/src/actions/data/create.js +18 -5
  9. package/src/actions/data/density.js +1 -1
  10. package/src/actions/data/derived.js +15 -1
  11. package/src/actions/data/edit.js +19 -6
  12. package/src/actions/data/filter.js +15 -4
  13. package/src/actions/data/index.js +3 -0
  14. package/src/actions/data/interval.js +2 -0
  15. package/src/actions/data/regression.js +4 -1
  16. package/src/actions/data/revise.js +37 -5
  17. package/src/actions/data/shared.js +30 -2
  18. package/src/actions/data/sort.js +21 -0
  19. package/src/actions/data/summary.js +1 -1
  20. package/src/actions/encodings/angle.js +4 -1
  21. package/src/actions/encodings/appearance.js +6 -2
  22. package/src/actions/encodings/color/continuous.js +4 -1
  23. package/src/actions/encodings/color/index.js +4 -1
  24. package/src/actions/encodings/offset.js +4 -1
  25. package/src/actions/encodings/position/resolve.js +6 -1
  26. package/src/actions/encodings/ranged.js +6 -1
  27. package/src/actions/encodings/ruleAppearance.js +4 -1
  28. package/src/actions/encodings/stroke.js +4 -1
  29. package/src/actions/encodings/strokeDash.js +6 -1
  30. package/src/actions/encodings/text.js +6 -1
  31. package/src/actions/marks/bar/create.js +8 -1
  32. package/src/actions/marks/bar/edit.js +12 -5
  33. package/src/actions/marks/point/create.js +8 -1
  34. package/src/actions/marks/point/edit.js +12 -5
  35. package/src/actions/marks/point/materialize.js +4 -2
  36. package/src/actions/marks/rect/actions.js +23 -8
  37. package/src/actions/marks/rule/actions.js +24 -8
  38. package/src/actions/marks/text/actions.js +25 -6
  39. package/src/actions/marks/tick/actions.js +23 -8
  40. package/src/actions/primitives/semanticAction.js +37 -3
  41. package/src/actions/primitives/semanticValidation/dataset.js +13 -1
  42. package/src/actions/primitives/semanticValidation/layer.js +22 -2
  43. package/src/actions/primitives/semanticValidation/scale.js +6 -0
  44. package/src/actions/regression/create.js +25 -4
  45. package/src/actions/regression/edit.js +47 -12
  46. package/src/actions/regression/follow.js +22 -0
  47. package/src/actions/scales/channels.js +11 -11
  48. package/src/actions/scales/consumers/common.js +2 -1
  49. package/src/actions/scales/create.js +2 -1
  50. package/src/actions/scales/definitions.js +1 -1
  51. package/src/actions/scales/edit.js +1 -1
  52. package/src/actions/scales/parallel.js +1 -1
  53. package/src/actions/scales/patch.js +1 -1
  54. package/src/actions/scales/preview.js +28 -1
  55. package/src/actions/theme/reconcile.js +2 -0
  56. package/src/core/action.js +3 -2
  57. package/src/core/programState.js +5 -1
  58. package/src/grammar/bin.js +39 -10
  59. package/src/grammar/datasetSchema.js +386 -0
  60. package/src/grammar/density.js +53 -5
  61. package/src/grammar/filter.js +133 -39
  62. package/src/grammar/interval.js +32 -1
  63. package/src/grammar/itemMissing.js +59 -0
  64. package/src/grammar/regression/derive.js +57 -16
  65. package/src/grammar/regression/index.js +1 -0
  66. package/src/grammar/regression/models.js +51 -22
  67. package/src/grammar/regression/parameters.js +56 -4
  68. package/src/grammar/scales/definition.js +7 -0
  69. package/src/grammar/schemas/semanticPath.js +3 -2
  70. package/src/grammar/sort.js +88 -0
  71. package/src/grammar/summary.js +117 -19
  72. package/src/grammar/transformTopology.js +1 -0
  73. package/src/grammar/transforms.js +21 -11
  74. package/src/inspection.js +495 -0
  75. package/src/inspectionDescriptors.js +3 -0
  76. package/src/materialization/bars/resolve.js +4 -2
  77. package/src/materialization/revisionIdentity.js +19 -0
  78. package/src/materialization/scales/resolve.js +67 -4
  79. package/src/materialization/text.js +4 -2
  80. package/src/persistence.js +79 -6
  81. package/src/version.js +1 -1
  82. package/types/inspection.d.ts +17 -0
  83. package/types/program.d.ts +147 -27
@@ -9,6 +9,7 @@ import { formatTextValue } from "../grammar/text.js";
9
9
  import { normalizePositionDatum } from "../grammar/positionDatum.js";
10
10
  import { mapOrdinalPositionValues } from "../grammar/scales/index.js";
11
11
  import { findDataset } from "../selectors/datasets.js";
12
+ import { applyItemMissingPolicy } from "../grammar/itemMissing.js";
12
13
  import { findCoordinate } from "../selectors/coordinates.js";
13
14
  import { findLayer } from "../selectors/layers.js";
14
15
  import { unionConcreteGraphicBounds } from
@@ -414,10 +415,11 @@ function resolveSourceTextItems(program, layer, config) {
414
415
  }
415
416
 
416
417
  function resolveRowTextItems(program, layer, config) {
417
- const dataset = findDataset(program, layer.data);
418
- if (dataset === undefined) {
418
+ const sourceDataset = findDataset(program, layer.data);
419
+ if (sourceDataset === undefined) {
419
420
  throw new Error(`Text mark "${layer.id}" requires an existing dataset.`);
420
421
  }
422
+ const dataset = applyItemMissingPolicy(layer, sourceDataset);
421
423
  const encodings = [layer.encoding?.x, layer.encoding?.y, layer.encoding?.text];
422
424
  const length = encodings.some(encoding => encoding !== undefined && Object.hasOwn(encoding, "field"))
423
425
  ? dataset.values.length
@@ -5,6 +5,12 @@ import { hasRegisteredExtension, isBuiltinProgramClass } from "./core/extensionR
5
5
  import { packageVersion } from "./version.js";
6
6
  import { decodeValue, encodeValue, invalidSnapshot } from "./persistence/codec.js";
7
7
  import { exactKeys, requireObject, STATE_KEYS, validateGraphic, validateProgramState, validateTrace } from "./persistence/validation.js";
8
+ import {
9
+ deriveTransformSchema,
10
+ inferDatasetSchema,
11
+ validateDatasetSchema
12
+ } from "./grammar/datasetSchema.js";
13
+ import { restoreResolvedScaleBindings } from "./actions/scales/preview.js";
8
14
 
9
15
  function canonicalState(program, extensions, path = "payload") {
10
16
  if (!(program instanceof ChartProgram || program instanceof BasicChartProgram) || !isBuiltinProgramClass(program.constructor)) {
@@ -19,7 +25,7 @@ function canonicalState(program, extensions, path = "payload") {
19
25
  }
20
26
 
21
27
  function envelope(kind, payload, extensions = []) {
22
- return JSON.stringify({ schemaVersion: 1, kind, packageVersion, extensions, payload: encodeValue(payload) });
28
+ return JSON.stringify({ schemaVersion: kind === "editable" ? 2 : 1, kind, packageVersion, extensions, payload: encodeValue(payload) });
23
29
  }
24
30
 
25
31
  function readEnvelope(text, kind) {
@@ -27,7 +33,13 @@ function readEnvelope(text, kind) {
27
33
  let value;
28
34
  try { value = JSON.parse(text); } catch { throw invalidSnapshot("Snapshot must be valid JSON.", "snapshot"); }
29
35
  exactKeys(value, ["schemaVersion", "kind", "packageVersion", "extensions", "payload"], "snapshot");
30
- if (value.schemaVersion !== 1 || value.kind !== kind) throw invalidSnapshot(`Unsupported snapshot schema or kind; expected schema 1 ${kind}.`, "snapshot");
36
+ const versions = kind === "editable" ? [1, 2] : [1];
37
+ if (!versions.includes(value.schemaVersion) || value.kind !== kind) {
38
+ throw invalidSnapshot(
39
+ `Unsupported snapshot schema or kind; expected schema ${versions.join(" or ")} ${kind}.`,
40
+ "snapshot"
41
+ );
42
+ }
31
43
  if (typeof value.packageVersion !== "string" || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value.packageVersion)) throw invalidSnapshot("Invalid snapshot packageVersion.", "snapshot.packageVersion");
32
44
  if (!Array.isArray(value.extensions) || new Set(value.extensions).size !== value.extensions.length ||
33
45
  value.extensions.some(name => typeof name !== "string" || !hasRegisteredExtension(name)) || (kind === "graphic" && value.extensions.length)) {
@@ -36,15 +48,69 @@ function readEnvelope(text, kind) {
36
48
  return { ...value, payload: decodeValue(value.payload) };
37
49
  }
38
50
 
39
- function restoreState(state, extensions, path = "payload") {
51
+ function restoreState(state, extensions, path = "payload", { requireSchemas = false } = {}) {
40
52
  exactKeys(state, STATE_KEYS, path);
41
53
  if (!Array.isArray(state.actionStack) || state.actionStack.length) throw invalidSnapshot("Snapshot actionStack must be empty.", `${path}.actionStack`);
42
54
  requireObject(state.children, `${path}.children`);
43
55
  validateTrace(state.trace, ChartProgram, extensions, `${path}.trace`);
44
- const children = Object.fromEntries(Object.entries(state.children).map(([id, child]) => [id, restoreState(child, extensions, `${path}.children.${id}`)]));
56
+ const children = Object.fromEntries(Object.entries(state.children).map(([id, child]) => [id, restoreState(
57
+ child,
58
+ extensions,
59
+ `${path}.children.${id}`,
60
+ { requireSchemas }
61
+ )]));
45
62
  const program = new ChartProgram({ ...state, children });
46
63
  validateProgramState(program, path);
47
- return program;
64
+ if (requireSchemas) {
65
+ for (const dataset of program.semanticSpec.datasets) {
66
+ if (dataset.schema === undefined) {
67
+ throw invalidSnapshot(`Dataset "${dataset.id}" requires schema in editable snapshot version 2.`, `${path}.semanticSpec.datasets`);
68
+ }
69
+ validateDatasetSchema(dataset.schema);
70
+ }
71
+ }
72
+ return restoreResolvedScaleBindings(program);
73
+ }
74
+
75
+ function migrateStateSchemas(state) {
76
+ const datasets = state.semanticSpec.datasets;
77
+ const byId = new Map(datasets.map(dataset => [dataset.id, dataset]));
78
+ const schemas = new Map();
79
+ const active = new Set();
80
+ function schemaFor(id) {
81
+ if (schemas.has(id)) return schemas.get(id);
82
+ if (active.has(id)) throw invalidSnapshot(`Cyclic data source "${id}".`, "payload.semanticSpec.datasets");
83
+ active.add(id);
84
+ const dataset = byId.get(id);
85
+ let schema;
86
+ if (dataset.schema !== undefined) {
87
+ schema = validateDatasetSchema(dataset.schema);
88
+ } else if (dataset.source === undefined) {
89
+ schema = inferDatasetSchema(dataset.values ?? []);
90
+ } else {
91
+ const sourceSchema = schemaFor(dataset.source);
92
+ schema = deriveTransformSchema(
93
+ sourceSchema,
94
+ dataset.transform?.[0] ?? {},
95
+ dataset.values,
96
+ { sourceId: dataset.source, ownerId: dataset.id }
97
+ );
98
+ }
99
+ active.delete(id);
100
+ schemas.set(id, schema);
101
+ return schema;
102
+ }
103
+ const semanticSpec = {
104
+ ...state.semanticSpec,
105
+ datasets: datasets.map(dataset => ({ ...dataset, schema: schemaFor(dataset.id) }))
106
+ };
107
+ return {
108
+ ...state,
109
+ semanticSpec,
110
+ children: Object.fromEntries(Object.entries(state.children).map(
111
+ ([id, child]) => [id, migrateStateSchemas(child)]
112
+ ))
113
+ };
48
114
  }
49
115
 
50
116
  export function serializeProgram(program) {
@@ -55,7 +121,14 @@ export function serializeProgram(program) {
55
121
  export function deserializeProgram(text) {
56
122
  const stored = readEnvelope(text, "editable");
57
123
  const extensions = new Set();
58
- const program = restoreState(stored.payload, extensions);
124
+ let payload = stored.payload;
125
+ if (stored.schemaVersion === 1) {
126
+ // Validate the historical state before adding any inferred metadata. Migration
127
+ // must never repair an invalid reference graph or malformed payload.
128
+ restoreState(payload, new Set());
129
+ payload = migrateStateSchemas(payload);
130
+ }
131
+ const program = restoreState(payload, extensions, "payload", { requireSchemas: true });
59
132
  if (extensions.size !== stored.extensions.length || stored.extensions.some(name => !extensions.has(name))) {
60
133
  throw invalidSnapshot("Snapshot extensions must exactly match its action traces.", "snapshot.extensions");
61
134
  }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Must match package.json; enforced by the snapshot producer-version test.
2
- export const packageVersion = "0.0.16";
2
+ export const packageVersion = "0.0.17";
@@ -0,0 +1,17 @@
1
+ import type { BasicChartProgram } from "./basic.js";
2
+ import type { ChartProgram, DatasetSchema } from "./program.js";
3
+ export type ProgramInput = ChartProgram | BasicChartProgram;
4
+ export interface ResourceRef { readonly kind: "data" | "mark" | "scale" | "coordinate" | "guide" | "canvas" | "composition"; readonly id: string; readonly childPath?: readonly string[]; }
5
+ export interface InspectionTarget { readonly kind: "data" | "mark" | "scale" | "coordinate" | "guide"; readonly id: string; readonly childPath?: readonly string[]; }
6
+ export interface Finding { readonly severity: "error" | "warning" | "info"; readonly code: string; readonly reason: string; readonly message: string; readonly resource?: ResourceRef; readonly optionPath?: string; }
7
+ export interface CheckResult { readonly name: string; readonly status: "not_run" | "passed" | "failed" | "not_applicable"; readonly evidencePaths: readonly string[]; readonly findings: readonly Finding[]; }
8
+ export interface ParameterDefinition { readonly path: string; readonly kind: "field" | "enum" | "number" | "string" | "boolean" | "range" | "array" | "object" | "resource"; readonly required: boolean; readonly active: boolean; readonly choices?: readonly (string | number | boolean | null)[]; readonly minimum?: number; readonly maximum?: number; readonly unit?: string; readonly fieldRole?: string; }
9
+ export interface ActionDescription { readonly version: 1; readonly action: string; readonly applicability: "supported" | "needs-input" | "incompatible" | "unsupported" | "unverified"; readonly parameterDefinitions: readonly ParameterDefinition[]; readonly requirements: readonly { readonly reason: string; readonly optionPath?: string }[]; readonly checks: readonly CheckResult[]; readonly findings: readonly Finding[]; }
10
+ export interface Change { readonly resource: ResourceRef; readonly operation: "add" | "remove" | "modify"; readonly effect: "data" | "binding" | "structure" | "scale" | "guide" | "style"; readonly roles: readonly string[]; }
11
+ export interface ProgramComparison { readonly version: 1; readonly equivalence: "equal" | "different" | "unknown"; readonly completeProgramComparison: boolean; readonly changes: readonly Change[]; readonly affected: readonly ResourceRef[]; readonly findings: readonly Finding[]; }
12
+ export interface OwnerInspection { readonly owner: ResourceRef; readonly grain: "item" | "series" | "composite" | "unknown"; readonly rawRows: number | null; readonly logicalDataItems: number | null; readonly drawablePrimitives: number | null; readonly visibleCandidates: number | null; readonly flags: readonly { readonly reason: "opacity-zero" | "zero-size" | "outside-clip" | "missing-skipped"; readonly count: number | null }[]; readonly bounds: Readonly<{x:number;y:number;width:number;height:number}> | null; readonly sourceRefs: readonly ResourceRef[]; readonly checks: readonly CheckResult[]; }
13
+ export interface ProgramInspection { readonly version: 1; readonly views: readonly OwnerInspection[]; readonly calculations: readonly unknown[]; readonly checks: readonly CheckResult[]; readonly findings: readonly Finding[]; readonly coverage: Readonly<{partial:boolean;unsupported:readonly ResourceRef[]}>; }
14
+ export function getDatasetSchema(program: ProgramInput, options: {readonly data:string;readonly childPath?:readonly string[]}): Readonly<{data:string;schema:DatasetSchema}>;
15
+ export function describeAction(program: ProgramInput, options: {readonly action:string;readonly target?:InspectionTarget;readonly options?:Readonly<Record<string,unknown>>}): ActionDescription;
16
+ export function comparePrograms(before: ProgramInput, after: ProgramInput, options?: {readonly target?:InspectionTarget}): ProgramComparison;
17
+ export function inspectProgram(program: ProgramInput, options?: {readonly target?:InspectionTarget}): ProgramInspection;
@@ -339,21 +339,29 @@ export type DensityPlacement =
339
339
  export type FilterComparison =
340
340
  | { op: "eq" | "neq"; value: unknown }
341
341
  | { op: "lt" | "lte" | "gt" | "gte"; value: number | string };
342
- export type FilterRange = {
343
- min: number | string;
344
- max: number | string;
345
- inclusive?: boolean;
346
- };
342
+ type FilterBound = number | string;
343
+ type FilterRangeBounds =
344
+ | { min: FilterBound; max?: FilterBound }
345
+ | { min?: FilterBound; max: FilterBound };
346
+ type FilterRangeClosure =
347
+ | { inclusive?: boolean; minInclusive?: never; maxInclusive?: never }
348
+ | { inclusive?: never; minInclusive?: boolean; maxInclusive?: boolean };
349
+ export type FilterRange = FilterRangeBounds & FilterRangeClosure;
347
350
  export type FilterModeOptions =
348
- | { oneOf: readonly unknown[]; predicate?: never; range?: never }
349
- | { oneOf?: never; predicate: FilterComparison; range?: never }
350
- | { oneOf?: never; predicate?: never; range: FilterRange };
351
+ | { oneOf: readonly [DatasetScalar, ...DatasetScalar[]]; noneOf?: never; predicate?: never; range?: never }
352
+ | { oneOf?: never; noneOf: readonly [DatasetScalar, ...DatasetScalar[]]; predicate?: never; range?: never }
353
+ | { oneOf?: never; noneOf?: never; predicate: FilterComparison; range?: never }
354
+ | { oneOf?: never; noneOf?: never; predicate?: never; range: FilterRange };
351
355
  export type FilterDataOptions = {
352
356
  id: string;
353
357
  source?: string;
354
358
  field: string;
359
+ nulls?: "include" | "exclude";
355
360
  } & FilterModeOptions;
356
361
  export type DatasetScalar = string | number | boolean | null;
362
+ export type RegressionPredictOptions =
363
+ | { values: readonly [number, ...number[]]; domain?: never; steps?: never }
364
+ | { values?: never; domain: readonly [number, number]; steps: number };
357
365
  export type DisplayLabelMap = ReadonlyArray<Readonly<{
358
366
  value: DatasetScalar;
359
367
  label: string;
@@ -364,10 +372,12 @@ export interface DisplayLabelOptions {
364
372
  export type DatasetFilterTransform = {
365
373
  type: "filter";
366
374
  field: string;
375
+ nulls?: "include" | "exclude";
367
376
  } & (
368
- | { oneOf: readonly DatasetScalar[]; predicate?: never; range?: never }
369
- | { oneOf?: never; predicate: FilterComparison; range?: never }
370
- | { oneOf?: never; predicate?: never; range: FilterRange }
377
+ | { oneOf: readonly DatasetScalar[]; noneOf?: never; predicate?: never; range?: never }
378
+ | { oneOf?: never; noneOf: readonly DatasetScalar[]; predicate?: never; range?: never }
379
+ | { oneOf?: never; noneOf?: never; predicate: FilterComparison; range?: never }
380
+ | { oneOf?: never; noneOf?: never; predicate?: never; range: FilterRange }
371
381
  );
372
382
  export type DatasetRegressionTransform = {
373
383
  type: "regression";
@@ -375,24 +385,22 @@ export type DatasetRegressionTransform = {
375
385
  y: string;
376
386
  groupBy?: string;
377
387
  } & (
378
- | {
388
+ | ({
379
389
  method: "linear";
380
- interval: "mean" | "prediction";
381
390
  degree?: never;
382
391
  span?: never;
383
- } & (
392
+ } & ({ interval: false; confidenceMethod?: never; level?: never; confidence?: never } | ({ interval: "mean" | "prediction" } & (
384
393
  | { confidenceMethod: ConfidenceIntervalMethod; level: number; confidence?: never }
385
394
  | { confidence: number; confidenceMethod?: never; level?: never }
386
- )
387
- | {
395
+ ))))
396
+ | ({
388
397
  method: "polynomial";
389
398
  degree: number;
390
- interval: "mean" | "prediction";
391
399
  span?: never;
392
- } & (
400
+ } & ({ interval: false; confidenceMethod?: never; level?: never; confidence?: never } | ({ interval: "mean" | "prediction" } & (
393
401
  | { confidenceMethod: ConfidenceIntervalMethod; level: number; confidence?: never }
394
402
  | { confidence: number; confidenceMethod?: never; level?: never }
395
- )
403
+ ))))
396
404
  | {
397
405
  method: "loess";
398
406
  span: number;
@@ -402,7 +410,7 @@ export type DatasetRegressionTransform = {
402
410
  confidence?: never;
403
411
  interval?: never;
404
412
  }
405
- );
413
+ ) & { readonly predict?: RegressionPredictOptions; readonly missing?: "error" | "drop" };
406
414
  export interface DatasetDensityTransform {
407
415
  type: "density";
408
416
  field: string;
@@ -413,6 +421,7 @@ export interface DatasetDensityTransform {
413
421
  kernel?: DensityKernel;
414
422
  normalization?: DensityNormalization;
415
423
  weight?: StatisticalWeight;
424
+ missing?: "error" | "drop";
416
425
  as: readonly [string, string];
417
426
  resolve: "shared";
418
427
  placement?: {
@@ -500,6 +509,7 @@ export type DatasetIntervalTransform = {
500
509
  field: string;
501
510
  groupBy: readonly string[];
502
511
  as: DatasetIntervalOutputFields;
512
+ missing?: "error" | "drop";
503
513
  } & (
504
514
  | {
505
515
  center: "mean";
@@ -603,6 +613,21 @@ export interface DatasetWindowTransform {
603
613
  readonly operations: readonly DatasetWindowOperation[];
604
614
  readonly temporalUnit?: TemporalInputUnit;
605
615
  }
616
+ export interface SortKey {
617
+ field: string;
618
+ order?: "ascending" | "descending";
619
+ nulls?: "first" | "last";
620
+ temporalUnit?: "year" | "timestamp";
621
+ }
622
+ export interface DatasetSortTransform {
623
+ readonly type: "sort";
624
+ readonly sortBy: readonly {
625
+ readonly field: string;
626
+ readonly order: "ascending" | "descending";
627
+ readonly nulls: "first" | "last";
628
+ readonly temporalUnit?: "year" | "timestamp";
629
+ }[];
630
+ }
606
631
  export interface ECDFOutputFields {
607
632
  value: string;
608
633
  cumulative: string;
@@ -715,6 +740,7 @@ export interface DatasetBinTransform {
715
740
  readonly includeEmpty: boolean;
716
741
  readonly members: boolean;
717
742
  readonly weight?: StatisticalWeight;
743
+ readonly missing?: "error" | "drop";
718
744
  readonly as: {
719
745
  readonly lower: string;
720
746
  readonly upper: string;
@@ -871,6 +897,7 @@ export type DatasetTransform =
871
897
  | DatasetFilterTransform
872
898
  | DatasetFoldTransform
873
899
  | DatasetRegressionTransform
900
+ | DatasetSortTransform
874
901
  | DatasetDensityTransform
875
902
  | DatasetECDFTransform
876
903
  | DatasetHorizonTransform
@@ -902,6 +929,8 @@ export interface DatasetSummaryTransform {
902
929
  aggregates: readonly SummaryAggregateOptions[];
903
930
  members?: string;
904
931
  weight?: StatisticalWeight;
932
+ missing?: "error" | "drop";
933
+ empty?: "null" | "identity";
905
934
  }
906
935
  export interface SummaryAggregateOptions {
907
936
  op: AggregateOperation;
@@ -915,6 +944,18 @@ export interface SummaryDataOptions {
915
944
  aggregates: readonly SummaryAggregateOptions[];
916
945
  members?: string;
917
946
  weight?: StatisticalWeight;
947
+ missing?: "error" | "drop";
948
+ empty?: "null" | "identity";
949
+ }
950
+ export interface SortedDataOptions {
951
+ id: string;
952
+ source?: string;
953
+ sortBy: readonly [SortKey, ...SortKey[]];
954
+ }
955
+ export interface EditSortedDataOptions {
956
+ target: string;
957
+ sortBy: readonly [SortKey, ...SortKey[]];
958
+ dependents?: DerivedDataDependents;
918
959
  }
919
960
  export interface BindMarkDataOptions {
920
961
  target: string;
@@ -1094,19 +1135,57 @@ export interface TraceNode {
1094
1135
 
1095
1136
  export interface SemanticDataset {
1096
1137
  readonly id: string;
1138
+ readonly schema: DatasetSchema;
1097
1139
  readonly values?: readonly Readonly<Record<string, unknown>>[];
1098
1140
  readonly source?: string;
1099
1141
  readonly transform?: readonly Readonly<Record<string, unknown>>[];
1100
1142
  readonly [key: string]: unknown;
1101
1143
  }
1102
1144
 
1145
+ export type DatasetStorageType =
1146
+ | "number"
1147
+ | "string"
1148
+ | "boolean"
1149
+ | "array"
1150
+ | "object"
1151
+ | "unknown"
1152
+ | "mixed";
1153
+ export interface DatasetFieldLineage {
1154
+ readonly inputs: readonly Readonly<{ data: string; field: string }>[];
1155
+ readonly owner: string;
1156
+ readonly role: string;
1157
+ }
1158
+ export interface DatasetSchemaField {
1159
+ readonly name: string;
1160
+ readonly storageType: DatasetStorageType;
1161
+ readonly nullable: boolean;
1162
+ readonly optional: boolean;
1163
+ readonly lineage?: DatasetFieldLineage;
1164
+ }
1165
+ export interface DatasetSchema {
1166
+ readonly version: 1;
1167
+ readonly completeness: "known" | "unknown";
1168
+ readonly origin: "declared" | "inferred" | "derived";
1169
+ readonly fields: readonly DatasetSchemaField[];
1170
+ }
1171
+ export interface SourceSchemaField {
1172
+ name: string;
1173
+ storageType: Exclude<DatasetStorageType, "unknown" | "mixed">;
1174
+ nullable?: boolean;
1175
+ optional?: boolean;
1176
+ }
1177
+ export interface SourceSchemaInput {
1178
+ fields: readonly SourceSchemaField[];
1179
+ }
1180
+
1103
1181
  export interface SemanticLayer {
1104
1182
  readonly id: string;
1105
1183
  readonly data?: string;
1106
1184
  readonly source?: string;
1107
1185
  readonly coordinate?: string;
1108
- readonly mark?: Readonly<{ type?: string; missing?: "error" | "break"; [key: string]: unknown }>;
1186
+ readonly mark?: Readonly<{ type?: string; missing?: "error" | "break" | "skip"; [key: string]: unknown }>;
1109
1187
  readonly layout?: Readonly<{ mode?: ColorLayout }>;
1188
+ readonly derivedBindings?: Readonly<{ regression?: Readonly<{ mode: "follow"; roles: readonly ["data", "x", "y"] }> }>;
1110
1189
  readonly encoding?: Readonly<Record<string, Readonly<Record<string, unknown>>>>;
1111
1190
  readonly [key: string]: unknown;
1112
1191
  }
@@ -1115,6 +1194,7 @@ export interface SemanticScale {
1115
1194
  readonly id: string;
1116
1195
  readonly type?: ScaleType;
1117
1196
  readonly domain?: "auto" | readonly unknown[];
1197
+ readonly emptyDomain?: "preserve" | "require-explicit";
1118
1198
  readonly range?: ScaleRange;
1119
1199
  readonly [key: string]: unknown;
1120
1200
  }
@@ -1557,6 +1637,7 @@ export interface ScaleOptions {
1557
1637
  id?: string;
1558
1638
  type?: ScaleType;
1559
1639
  domain?: "auto" | readonly unknown[];
1640
+ emptyDomain?: "preserve" | "require-explicit";
1560
1641
  range?: ScaleRange;
1561
1642
  nice?: boolean;
1562
1643
  zero?: boolean;
@@ -1702,6 +1783,7 @@ export interface EditScaleOptions {
1702
1783
  id?: string;
1703
1784
  type?: ScaleType;
1704
1785
  domain?: "auto" | readonly unknown[];
1786
+ emptyDomain?: "preserve" | "require-explicit";
1705
1787
  range?: ScaleRange;
1706
1788
  nice?: boolean;
1707
1789
  zero?: boolean;
@@ -2017,6 +2099,7 @@ export interface DensityDataOptions {
2017
2099
  kernel?: DensityKernel;
2018
2100
  normalization?: DensityNormalization;
2019
2101
  weight?: StatisticalWeight;
2102
+ missing?: "error" | "drop";
2020
2103
  as?: readonly [string, string];
2021
2104
  }
2022
2105
 
@@ -2112,6 +2195,7 @@ export interface IntervalDataOptions {
2112
2195
  extent?: IntervalExtent;
2113
2196
  method?: ConfidenceIntervalMethod;
2114
2197
  level?: number;
2198
+ missing?: "error" | "drop";
2115
2199
  as?: IntervalOutputFields;
2116
2200
  }
2117
2201
 
@@ -2184,6 +2268,7 @@ export type BinDataOptions = {
2184
2268
  includeEmpty?: boolean;
2185
2269
  members?: boolean;
2186
2270
  weight?: StatisticalWeight;
2271
+ missing?: "error" | "drop";
2187
2272
  as?: BinDataOutputFields;
2188
2273
  } & BinDataMode;
2189
2274
 
@@ -2326,7 +2411,22 @@ type FocusedWeightedDerivedDataEdit<T, Weight> = {
2326
2411
  : never);
2327
2412
 
2328
2413
  export type EditComputedDataOptions = FocusedDerivedDataEdit<ComputedDataOptions>;
2329
- export type EditFilteredDataOptions = FocusedDerivedDataEdit<FilterDataOptions>;
2414
+ export type FilterRangePatch = FilterRangeClosure & {
2415
+ min?: FilterBound | false;
2416
+ max?: FilterBound | false;
2417
+ };
2418
+ type FilterModePatch =
2419
+ | { oneOf: readonly [DatasetScalar, ...DatasetScalar[]]; noneOf?: never; predicate?: never; range?: never }
2420
+ | { oneOf?: never; noneOf: readonly [DatasetScalar, ...DatasetScalar[]]; predicate?: never; range?: never }
2421
+ | { oneOf?: never; noneOf?: never; predicate: FilterComparison; range?: never }
2422
+ | { oneOf?: never; noneOf?: never; predicate?: never; range: FilterRangePatch }
2423
+ | { oneOf?: never; noneOf?: never; predicate?: never; range?: never };
2424
+ export type EditFilteredDataOptions = FilterModePatch & {
2425
+ target: string;
2426
+ field?: string;
2427
+ nulls?: "include" | "exclude" | false;
2428
+ dependents?: DerivedDataDependents;
2429
+ };
2330
2430
  export type EditFoldDataOptions = FocusedDerivedDataEdit<FoldDataOptions>;
2331
2431
  export type EditSummaryDataOptions = FocusedWeightedDerivedDataEdit<
2332
2432
  SummaryDataOptions,
@@ -3690,6 +3790,7 @@ export interface TextMarkOptions {
3690
3790
  /** Explicit source mark. Mutually exclusive with data; may be incomplete. */
3691
3791
  source?: string;
3692
3792
  text?: unknown;
3793
+ missing?: "error" | "skip";
3693
3794
  fill?: string;
3694
3795
  opacity?: number;
3695
3796
  fontSize?: number;
@@ -3753,6 +3854,7 @@ export interface RemoveLabelLayoutOptions {
3753
3854
  export interface RectMarkOptions extends RectStyleDetails {
3754
3855
  id?: string;
3755
3856
  data?: string;
3857
+ missing?: "error" | "skip";
3756
3858
  fill?: string;
3757
3859
  opacity?: number;
3758
3860
  stroke?: string | false;
@@ -4160,7 +4262,8 @@ type RegressionParameterOptions =
4160
4262
  confidenceMethod?: ConfidenceIntervalMethod;
4161
4263
  level?: number;
4162
4264
  confidence?: number;
4163
- interval?: RegressionInterval;
4265
+ interval?: RegressionInterval | false;
4266
+ predict?: RegressionPredictOptions;
4164
4267
  }
4165
4268
  | {
4166
4269
  method: "polynomial";
@@ -4169,7 +4272,8 @@ type RegressionParameterOptions =
4169
4272
  confidenceMethod?: ConfidenceIntervalMethod;
4170
4273
  level?: number;
4171
4274
  confidence?: number;
4172
- interval?: RegressionInterval;
4275
+ interval?: RegressionInterval | false;
4276
+ predict?: RegressionPredictOptions;
4173
4277
  }
4174
4278
  | {
4175
4279
  method: "loess";
@@ -4179,6 +4283,7 @@ type RegressionParameterOptions =
4179
4283
  level?: never;
4180
4284
  confidence?: never;
4181
4285
  interval?: never;
4286
+ predict?: RegressionPredictOptions;
4182
4287
  };
4183
4288
 
4184
4289
  export type RegressionDataOptions = {
@@ -4187,6 +4292,7 @@ export type RegressionDataOptions = {
4187
4292
  x: string;
4188
4293
  y: string;
4189
4294
  groupBy?: string;
4295
+ missing?: "error" | "drop";
4190
4296
  } & RegressionParameterOptions;
4191
4297
 
4192
4298
  type RegressionCommonOptions = {
@@ -4195,6 +4301,8 @@ type RegressionCommonOptions = {
4195
4301
  y?: string;
4196
4302
  groupBy?: string | false;
4197
4303
  line?: StrokeStyleDetails & { strokeWidth?: number; curve?: CurveInterpolation };
4304
+ sourceBinding?: "fixed" | "follow";
4305
+ missing?: "error" | "drop";
4198
4306
  };
4199
4307
 
4200
4308
  export type RegressionOptions = RegressionCommonOptions & (
@@ -4221,9 +4329,12 @@ export interface EditRegressionOptions {
4221
4329
  confidenceMethod?: ConfidenceIntervalMethod;
4222
4330
  level?: number;
4223
4331
  confidence?: number;
4224
- interval?: RegressionInterval;
4332
+ interval?: RegressionInterval | false;
4333
+ missing?: "error" | "drop";
4334
+ predict?: RegressionPredictOptions | false;
4225
4335
  band?: false | RegressionBandOptions;
4226
4336
  line?: StrokeStyleDetails & { strokeWidth?: number; curve?: CurveInterpolation };
4337
+ sourceBinding?: "fixed" | "follow";
4227
4338
  }
4228
4339
 
4229
4340
  export interface RemoveAxisOptions {
@@ -4497,6 +4608,7 @@ type StoredCell<T> = T extends (...args: never[]) => unknown ? never
4497
4608
  export interface CreateDataOptions<Row extends object> {
4498
4609
  id?: string;
4499
4610
  values: readonly (Row extends readonly unknown[] ? never : Row & StoredCell<Row>)[];
4611
+ schema?: SourceSchemaInput;
4500
4612
  }
4501
4613
 
4502
4614
  export interface ReviseDataOptions<Row extends object> extends CreateDataOptions<Row> {
@@ -4552,6 +4664,7 @@ export class ChartProgram {
4552
4664
  createTimeUnitData(options: TimeUnitDataOptions): ChartProgram;
4553
4665
  createWindowData(options: WindowDataOptions): ChartProgram;
4554
4666
  createBin2DData(options: Bin2DDataOptions): ChartProgram;
4667
+ createSortedData(options: SortedDataOptions): ChartProgram;
4555
4668
  editDerivedData(options: EditDerivedDataOptions): ChartProgram;
4556
4669
  editComputedData(options: EditComputedDataOptions): ChartProgram;
4557
4670
  editFilteredData(options: EditFilteredDataOptions): ChartProgram;
@@ -4563,6 +4676,7 @@ export class ChartProgram {
4563
4676
  editDensityData(options: EditDensityDataOptions): ChartProgram;
4564
4677
  editStackData(options: EditStackDataOptions): ChartProgram;
4565
4678
  editRegressionData(options: EditRegressionDataOptions): ChartProgram;
4679
+ editSortedData(options: EditSortedDataOptions): ChartProgram;
4566
4680
  editIntervalData(options: EditIntervalDataOptions): ChartProgram;
4567
4681
  editECDFData(options: EditECDFDataOptions): ChartProgram;
4568
4682
  editNormalizedData(options: EditNormalizedDataOptions): ChartProgram;
@@ -4573,6 +4687,7 @@ export class ChartProgram {
4573
4687
  createPointMark(options?: StrokeStyleDetails & {
4574
4688
  id?: string;
4575
4689
  data?: string;
4690
+ missing?: "error" | "skip";
4576
4691
  shape?: PointShape;
4577
4692
  fill?: string;
4578
4693
  opacity?: number;
@@ -4581,6 +4696,7 @@ export class ChartProgram {
4581
4696
  }): ChartProgram;
4582
4697
  editPointMark(options: StrokeStyleDetails & {
4583
4698
  target?: string;
4699
+ missing?: "error" | "skip";
4584
4700
  shape?: PointShape;
4585
4701
  fill?: string;
4586
4702
  opacity?: number;
@@ -4590,6 +4706,7 @@ export class ChartProgram {
4590
4706
  createTickMark(options?: StrokeStyleDetails & {
4591
4707
  id?: string;
4592
4708
  data?: string;
4709
+ missing?: "error" | "skip";
4593
4710
  length?: number;
4594
4711
  stroke?: string;
4595
4712
  strokeWidth?: number;
@@ -4597,6 +4714,7 @@ export class ChartProgram {
4597
4714
  }): ChartProgram;
4598
4715
  editTickMark(options: StrokeStyleDetails & {
4599
4716
  target?: string;
4717
+ missing?: "error" | "skip";
4600
4718
  length?: number;
4601
4719
  stroke?: string;
4602
4720
  strokeWidth?: number;
@@ -4626,6 +4744,7 @@ export class ChartProgram {
4626
4744
  createBarMark(options?: RectStyleDetails & {
4627
4745
  id?: string;
4628
4746
  data?: string;
4747
+ missing?: "error" | "skip";
4629
4748
  fill?: string;
4630
4749
  opacity?: number;
4631
4750
  stroke?: FilledMarkStroke;
@@ -4633,6 +4752,7 @@ export class ChartProgram {
4633
4752
  }): ChartProgram;
4634
4753
  editBarMark(options: RectStyleDetails & {
4635
4754
  target?: string;
4755
+ missing?: "error" | "skip";
4636
4756
  fill?: string;
4637
4757
  opacity?: number;
4638
4758
  stroke?: FilledMarkStroke;
@@ -4669,8 +4789,8 @@ export class ChartProgram {
4669
4789
  }): ChartProgram;
4670
4790
  createRectMark(options?: RectMarkOptions): ChartProgram;
4671
4791
  editRectMark(options: EditRectMarkOptions): ChartProgram;
4672
- createRuleMark(options?: { id?: string; data?: string } & RuleStyleOptions): ChartProgram;
4673
- editRuleMark(options: { target?: string } & RuleStyleOptions): ChartProgram;
4792
+ createRuleMark(options?: { id?: string; data?: string; missing?: "error" | "skip" } & RuleStyleOptions): ChartProgram;
4793
+ editRuleMark(options: { target?: string; missing?: "error" | "skip" } & RuleStyleOptions): ChartProgram;
4674
4794
  createTextMark(options?: TextMarkOptions): ChartProgram;
4675
4795
  createMarkLabels(options?: CreateMarkLabelsOptions): ChartProgram;
4676
4796
  editMarkLabelSelection(options: EditMarkLabelSelectionOptions): ChartProgram;