@sdeverywhere/check-core 0.1.1 → 0.1.3

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/dist/index.js CHANGED
@@ -1,10 +1,54 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __async = (__this, __arguments, generator) => {
21
+ return new Promise((resolve, reject) => {
22
+ var fulfilled = (value) => {
23
+ try {
24
+ step(generator.next(value));
25
+ } catch (e) {
26
+ reject(e);
27
+ }
28
+ };
29
+ var rejected = (value) => {
30
+ try {
31
+ step(generator.throw(value));
32
+ } catch (e) {
33
+ reject(e);
34
+ }
35
+ };
36
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
37
+ step((generator = generator.apply(__this, __arguments)).next());
38
+ });
39
+ };
40
+
1
41
  // src/_shared/task-queue.ts
2
42
  var TaskQueue = class {
3
43
  constructor(processor) {
4
44
  this.processor = processor;
45
+ /** The queue of task keys, most recent at front. */
5
46
  this.taskKeyQueue = [];
47
+ /** The map of tasks. */
6
48
  this.taskMap = /* @__PURE__ */ new Map();
49
+ /** Whether tasks are being processed. */
7
50
  this.processing = false;
51
+ /** Whether `shutdown` has been called. */
8
52
  this.stopped = false;
9
53
  }
10
54
  addTask(key, input, onComplete) {
@@ -42,39 +86,41 @@ var TaskQueue = class {
42
86
  });
43
87
  }
44
88
  }
45
- async processNextTask() {
46
- var _a, _b;
47
- const taskKey = this.taskKeyQueue.shift();
48
- if (!taskKey) {
49
- return;
50
- }
51
- const task = this.taskMap.get(taskKey);
52
- if (task) {
53
- this.taskMap.delete(taskKey);
54
- } else {
55
- return;
56
- }
57
- let output;
58
- try {
59
- output = await this.processor.process(task.input);
60
- } catch (e) {
61
- if (!this.stopped) {
62
- this.shutdown();
63
- (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
89
+ processNextTask() {
90
+ return __async(this, null, function* () {
91
+ var _a, _b;
92
+ const taskKey = this.taskKeyQueue.shift();
93
+ if (!taskKey) {
94
+ return;
64
95
  }
65
- return;
66
- }
67
- task.onComplete(output);
68
- if (this.taskKeyQueue.length > 0) {
69
- setTimeout(() => {
70
- this.processNextTask();
71
- });
72
- } else {
73
- this.processing = false;
74
- if (!this.stopped) {
75
- (_b = this.onIdle) == null ? void 0 : _b.call(this);
96
+ const task = this.taskMap.get(taskKey);
97
+ if (task) {
98
+ this.taskMap.delete(taskKey);
99
+ } else {
100
+ return;
76
101
  }
77
- }
102
+ let output;
103
+ try {
104
+ output = yield this.processor.process(task.input);
105
+ } catch (e) {
106
+ if (!this.stopped) {
107
+ this.shutdown();
108
+ (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
109
+ }
110
+ return;
111
+ }
112
+ task.onComplete(output);
113
+ if (this.taskKeyQueue.length > 0) {
114
+ setTimeout(() => {
115
+ this.processNextTask();
116
+ });
117
+ } else {
118
+ this.processing = false;
119
+ if (!this.stopped) {
120
+ (_b = this.onIdle) == null ? void 0 : _b.call(this);
121
+ }
122
+ }
123
+ });
78
124
  }
79
125
  };
80
126
 
@@ -83,13 +129,13 @@ var CheckDataCoordinator = class {
83
129
  constructor(bundleModel) {
84
130
  this.bundleModel = bundleModel;
85
131
  this.taskQueue = new TaskQueue({
86
- process: async (request) => {
87
- const result = await this.bundleModel.getDatasetsForScenario(request.scenarioSpec, [request.datasetKey]);
132
+ process: (request) => __async(this, null, function* () {
133
+ const result = yield this.bundleModel.getDatasetsForScenario(request.scenarioSpec, [request.datasetKey]);
88
134
  const dataset = result.datasetMap.get(request.datasetKey);
89
135
  return {
90
136
  dataset
91
137
  };
92
- }
138
+ })
93
139
  });
94
140
  }
95
141
  requestDataset(requestKey, scenarioSpec, datasetKey, onResponse) {
@@ -1642,6 +1688,24 @@ var CheckPlanner = class {
1642
1688
  dataRefs: this.dataRefs
1643
1689
  };
1644
1690
  }
1691
+ /**
1692
+ * Record any references to additional datasets contained in the given predicate.
1693
+ * For example, if the predicate is:
1694
+ * ```
1695
+ * gt:
1696
+ * dataset:
1697
+ * name: 'XYZ'
1698
+ * scenario:
1699
+ * inputs: all
1700
+ * at: default
1701
+ * ```
1702
+ * this will add a reference to the scenario/dataset pair so that the data can
1703
+ * be fetched in a later stage.
1704
+ *
1705
+ * @param predicateSpec The predicate spec.
1706
+ * @param checkScenario The scenario in which the dataset is being checked.
1707
+ * @param checkDataset The dataset that is being checked.
1708
+ */
1645
1709
  addDataRefs(predicateSpec, checkScenario, checkDataset) {
1646
1710
  let dataRefs;
1647
1711
  const addDataRef = (op) => {
@@ -1770,7 +1834,7 @@ var ComparisonDataCoordinator = class {
1770
1834
  this.bundleModelL = bundleModelL;
1771
1835
  this.bundleModelR = bundleModelR;
1772
1836
  this.taskQueue = new TaskQueue({
1773
- process: async (request) => {
1837
+ process: (request) => __async(this, null, function* () {
1774
1838
  switch (request.kind) {
1775
1839
  case "dataset":
1776
1840
  return this.processDatasetRequest(request);
@@ -1779,44 +1843,52 @@ var ComparisonDataCoordinator = class {
1779
1843
  default:
1780
1844
  assertNever7(request);
1781
1845
  }
1782
- }
1846
+ })
1783
1847
  });
1784
1848
  }
1785
- async processDatasetRequest(request) {
1786
- async function fetchDatasets(bundleModel, scenarioSpec) {
1787
- if (scenarioSpec) {
1788
- return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys);
1789
- } else {
1790
- return void 0;
1849
+ processDatasetRequest(request) {
1850
+ return __async(this, null, function* () {
1851
+ function fetchDatasets(bundleModel, scenarioSpec) {
1852
+ return __async(this, null, function* () {
1853
+ if (scenarioSpec) {
1854
+ return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys);
1855
+ } else {
1856
+ return void 0;
1857
+ }
1858
+ });
1791
1859
  }
1792
- }
1793
- const [resultL, resultR] = await Promise.all([
1794
- fetchDatasets(this.bundleModelL, request.scenarioSpecL),
1795
- fetchDatasets(this.bundleModelR, request.scenarioSpecR)
1796
- ]);
1797
- return {
1798
- kind: "dataset",
1799
- datasetMapL: resultL == null ? void 0 : resultL.datasetMap,
1800
- datasetMapR: resultR == null ? void 0 : resultR.datasetMap
1801
- };
1860
+ const [resultL, resultR] = yield Promise.all([
1861
+ fetchDatasets(this.bundleModelL, request.scenarioSpecL),
1862
+ fetchDatasets(this.bundleModelR, request.scenarioSpecR)
1863
+ ]);
1864
+ return {
1865
+ kind: "dataset",
1866
+ datasetMapL: resultL == null ? void 0 : resultL.datasetMap,
1867
+ datasetMapR: resultR == null ? void 0 : resultR.datasetMap
1868
+ };
1869
+ });
1802
1870
  }
1803
- async processGraphDataRequest(request) {
1804
- async function fetchGraphData(bundleModel, scenarioSpec) {
1805
- if (scenarioSpec) {
1806
- return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId);
1807
- } else {
1808
- return void 0;
1871
+ processGraphDataRequest(request) {
1872
+ return __async(this, null, function* () {
1873
+ function fetchGraphData(bundleModel, scenarioSpec) {
1874
+ return __async(this, null, function* () {
1875
+ if (scenarioSpec) {
1876
+ return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId);
1877
+ } else {
1878
+ return void 0;
1879
+ }
1880
+ });
1809
1881
  }
1810
- }
1811
- const [graphDataL, graphDataR] = await Promise.all([
1812
- fetchGraphData(this.bundleModelL, request.scenarioSpecL),
1813
- fetchGraphData(this.bundleModelR, request.scenarioSpecR)
1814
- ]);
1815
- return {
1816
- kind: "graph-data",
1817
- graphDataL,
1818
- graphDataR
1819
- };
1882
+ const [graphDataL, graphDataR] = yield Promise.all([
1883
+ fetchGraphData(this.bundleModelL, request.scenarioSpecL),
1884
+ fetchGraphData(this.bundleModelR, request.scenarioSpecR)
1885
+ ]);
1886
+ return {
1887
+ kind: "graph-data",
1888
+ graphDataL,
1889
+ graphDataR
1890
+ };
1891
+ });
1820
1892
  }
1821
1893
  requestDatasetMaps(requestKey, scenarioSpecL, scenarioSpecR, datasetKeys, onResponse) {
1822
1894
  const request = {
@@ -1867,25 +1939,17 @@ function diffDatasets(datasetL, datasetR) {
1867
1939
  for (const t of times) {
1868
1940
  const valueL = datasetL.get(t);
1869
1941
  if (valueL !== void 0) {
1870
- if (valueL < minValueL)
1871
- minValueL = valueL;
1872
- if (valueL > maxValueL)
1873
- maxValueL = valueL;
1874
- if (valueL < minValue)
1875
- minValue = valueL;
1876
- if (valueL > maxValue)
1877
- maxValue = valueL;
1942
+ if (valueL < minValueL) minValueL = valueL;
1943
+ if (valueL > maxValueL) maxValueL = valueL;
1944
+ if (valueL < minValue) minValue = valueL;
1945
+ if (valueL > maxValue) maxValue = valueL;
1878
1946
  }
1879
1947
  const valueR = datasetR.get(t);
1880
1948
  if (valueR !== void 0) {
1881
- if (valueR < minValueR)
1882
- minValueR = valueR;
1883
- if (valueR > maxValueR)
1884
- maxValueR = valueR;
1885
- if (valueR < minValue)
1886
- minValue = valueR;
1887
- if (valueR > maxValue)
1888
- maxValue = valueR;
1949
+ if (valueR < minValueR) minValueR = valueR;
1950
+ if (valueR > maxValueR) maxValueR = valueR;
1951
+ if (valueR < minValue) minValue = valueR;
1952
+ if (valueR > maxValue) maxValue = valueR;
1889
1953
  }
1890
1954
  if (valueL === void 0 || valueR === void 0) {
1891
1955
  continue;
@@ -2044,9 +2108,6 @@ function restoreFromTerseSummaries(comparisonConfig, terseSummaries) {
2044
2108
  return allTestSummaries;
2045
2109
  }
2046
2110
 
2047
- // src/comparison/report/comparison-grouping.ts
2048
- import { assertNever as assertNever8 } from "assert-never";
2049
-
2050
2111
  // src/comparison/report/buckets.ts
2051
2112
  function getBucketIndex(diffPct, thresholds) {
2052
2113
  if (diffPct === 0) {
@@ -2060,7 +2121,33 @@ function getBucketIndex(diffPct, thresholds) {
2060
2121
  return thresholds.length + 1;
2061
2122
  }
2062
2123
 
2124
+ // src/comparison/report/comparison-group-scores.ts
2125
+ function getScoresForTestSummaries(testSummaries, thresholds) {
2126
+ const diffCountByBucket = Array(thresholds.length + 2).fill(0);
2127
+ const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0);
2128
+ let totalDiffCount = 0;
2129
+ for (const testSummary of testSummaries) {
2130
+ const bucketIndex = getBucketIndex(testSummary.md, thresholds);
2131
+ diffCountByBucket[bucketIndex]++;
2132
+ totalMaxDiffByBucket[bucketIndex] += testSummary.md;
2133
+ totalDiffCount++;
2134
+ }
2135
+ let diffPercentByBucket;
2136
+ if (totalDiffCount > 0) {
2137
+ diffPercentByBucket = diffCountByBucket.map((count) => count / totalDiffCount * 100);
2138
+ } else {
2139
+ diffPercentByBucket = [];
2140
+ }
2141
+ return {
2142
+ totalDiffCount,
2143
+ totalMaxDiffByBucket,
2144
+ diffCountByBucket,
2145
+ diffPercentByBucket
2146
+ };
2147
+ }
2148
+
2063
2149
  // src/comparison/report/comparison-grouping.ts
2150
+ import { assertNever as assertNever8 } from "assert-never";
2064
2151
  function categorizeComparisonTestSummaries(comparisonConfig, terseSummaries) {
2065
2152
  const allTestSummaries = restoreFromTerseSummaries(comparisonConfig, terseSummaries);
2066
2153
  const groupsByScenario = groupComparisonTestSummaries(allTestSummaries, "by-scenario");
@@ -2068,6 +2155,7 @@ function categorizeComparisonTestSummaries(comparisonConfig, terseSummaries) {
2068
2155
  const groupsByDataset = groupComparisonTestSummaries(allTestSummaries, "by-dataset");
2069
2156
  const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()]);
2070
2157
  return {
2158
+ allTestSummaries,
2071
2159
  byScenario,
2072
2160
  byDataset
2073
2161
  };
@@ -2109,7 +2197,7 @@ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2109
2197
  function addSummaryForGroup(group, root, validInL, validInR) {
2110
2198
  let scores;
2111
2199
  if (validInL && validInR) {
2112
- scores = getScoresForGroup(group, comparisonConfig.thresholds);
2200
+ scores = getScoresForTestSummaries(group.testSummaries, comparisonConfig.thresholds);
2113
2201
  }
2114
2202
  const groupSummary = {
2115
2203
  root,
@@ -2167,29 +2255,6 @@ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2167
2255
  withoutDiffs
2168
2256
  };
2169
2257
  }
2170
- function getScoresForGroup(group, thresholds) {
2171
- const diffCountByBucket = Array(thresholds.length + 2).fill(0);
2172
- const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0);
2173
- let totalDiffCount = 0;
2174
- for (const testSummary of group.testSummaries) {
2175
- const bucketIndex = getBucketIndex(testSummary.md, thresholds);
2176
- diffCountByBucket[bucketIndex]++;
2177
- totalMaxDiffByBucket[bucketIndex] += testSummary.md;
2178
- totalDiffCount++;
2179
- }
2180
- let diffPercentByBucket;
2181
- if (totalDiffCount > 0) {
2182
- diffPercentByBucket = diffCountByBucket.map((count) => count / totalDiffCount * 100);
2183
- } else {
2184
- diffPercentByBucket = [];
2185
- }
2186
- return {
2187
- totalDiffCount,
2188
- totalMaxDiffByBucket,
2189
- diffCountByBucket,
2190
- diffPercentByBucket
2191
- };
2192
- }
2193
2258
  function sortDatasetGroupSummaries(summaries) {
2194
2259
  return summaries.sort((a, b) => {
2195
2260
  var _a, _b;
@@ -2249,47 +2314,6 @@ function compareScores(a, b) {
2249
2314
  return 0;
2250
2315
  }
2251
2316
 
2252
- // src/bundle/model-inputs.ts
2253
- var ModelInputs = class {
2254
- constructor(modelSpec) {
2255
- this.inputsByLookupName = /* @__PURE__ */ new Map();
2256
- this.inputIdAliases = /* @__PURE__ */ new Set();
2257
- for (const inputVar of modelSpec.inputVars.values()) {
2258
- const varNameKey = inputVar.varName.toLowerCase();
2259
- this.inputsByLookupName.set(varNameKey, inputVar);
2260
- if (inputVar.inputId) {
2261
- const idAlias = `id ${inputVar.inputId}`;
2262
- this.inputIdAliases.add(idAlias);
2263
- const idKey = idAlias.toLowerCase();
2264
- this.inputsByLookupName.set(idKey, inputVar);
2265
- }
2266
- }
2267
- if (modelSpec.inputAliases) {
2268
- for (const [alias, varId] of modelSpec.inputAliases.entries()) {
2269
- const aliasKey = alias.toLowerCase();
2270
- if (this.inputsByLookupName.has(aliasKey)) {
2271
- console.warn(
2272
- `WARNING: Input variable already defined with a name that collides with alias '${alias}', skipping`
2273
- );
2274
- continue;
2275
- }
2276
- const inputVar = modelSpec.inputVars.get(varId);
2277
- if (inputVar === void 0) {
2278
- console.warn(`WARNING: No input variable found for '${varId}' associated with alias '${alias}', skipping`);
2279
- continue;
2280
- }
2281
- this.inputsByLookupName.set(aliasKey, inputVar);
2282
- }
2283
- }
2284
- }
2285
- getAllInputIdAliases() {
2286
- return [...this.inputIdAliases];
2287
- }
2288
- getInputVarForName(name) {
2289
- return this.inputsByLookupName.get(name.toLowerCase());
2290
- }
2291
- };
2292
-
2293
2317
  // src/comparison/config/parse/comparison-parser.ts
2294
2318
  import Ajv2 from "ajv";
2295
2319
  import assertNever9 from "assert-never";
@@ -2306,13 +2330,36 @@ var comparison_schema_default = {
2306
2330
  $ref: "#/$defs/top_level_array_item"
2307
2331
  },
2308
2332
  $defs: {
2333
+ //
2334
+ // TOP-LEVEL
2335
+ //
2309
2336
  top_level_array_item: {
2310
2337
  oneOf: [
2311
2338
  { $ref: "#/$defs/scenario_array_item" },
2312
2339
  { $ref: "#/$defs/scenario_group_array_item" },
2340
+ { $ref: "#/$defs/graph_group_array_item" },
2313
2341
  { $ref: "#/$defs/view_group_array_item" }
2314
2342
  ]
2315
2343
  },
2344
+ //
2345
+ // DATASETS
2346
+ //
2347
+ dataset: {
2348
+ type: "object",
2349
+ additionalProperties: false,
2350
+ properties: {
2351
+ name: {
2352
+ type: "string"
2353
+ },
2354
+ source: {
2355
+ type: "string"
2356
+ }
2357
+ },
2358
+ required: ["name"]
2359
+ },
2360
+ //
2361
+ // SCENARIOS
2362
+ //
2316
2363
  scenario_array_item: {
2317
2364
  type: "object",
2318
2365
  additionalProperties: false,
@@ -2329,7 +2376,9 @@ var comparison_schema_default = {
2329
2376
  { $ref: "#/$defs/scenario_with_input_at_value" },
2330
2377
  { $ref: "#/$defs/scenario_with_multiple_input_settings" },
2331
2378
  { $ref: "#/$defs/scenario_with_inputs_in_preset_at_position" },
2379
+ // { $ref: '#/$defs/scenario_with_inputs_in_group_at_position' }
2332
2380
  { $ref: "#/$defs/scenario_preset" }
2381
+ // { $ref: '#/$defs/scenario_expand_for_each_input_in_group' }
2333
2382
  ]
2334
2383
  },
2335
2384
  scenario_position: {
@@ -2469,6 +2518,9 @@ var comparison_schema_default = {
2469
2518
  },
2470
2519
  required: ["preset"]
2471
2520
  },
2521
+ //
2522
+ // SCENARIO GROUPS
2523
+ //
2472
2524
  scenario_group_array_item: {
2473
2525
  type: "object",
2474
2526
  additionalProperties: false,
@@ -2544,7 +2596,63 @@ var comparison_schema_default = {
2544
2596
  },
2545
2597
  required: ["scenario_group_ref"]
2546
2598
  },
2599
+ //
2600
+ // GRAPHS
2601
+ //
2602
+ graphs_preset: {
2603
+ type: "string",
2604
+ enum: ["all"]
2605
+ },
2606
+ graphs_array: {
2607
+ type: "array",
2608
+ items: {
2609
+ type: "string"
2610
+ },
2611
+ minItems: 1
2612
+ },
2613
+ graph_group_ref: {
2614
+ type: "object",
2615
+ additionalProperties: false,
2616
+ properties: {
2617
+ graph_group_ref: {
2618
+ type: "string"
2619
+ }
2620
+ },
2621
+ required: ["graph_group_ref"]
2622
+ },
2623
+ //
2624
+ // GRAPH GROUPS
2625
+ //
2626
+ graph_group_array_item: {
2627
+ type: "object",
2628
+ additionalProperties: false,
2629
+ properties: {
2630
+ graph_group: {
2631
+ $ref: "#/$defs/graph_group"
2632
+ }
2633
+ },
2634
+ required: ["graph_group"]
2635
+ },
2636
+ graph_group: {
2637
+ type: "object",
2638
+ additionalProperties: false,
2639
+ properties: {
2640
+ id: {
2641
+ type: "string"
2642
+ },
2643
+ graphs: {
2644
+ $ref: "#/$defs/graphs_array"
2645
+ }
2646
+ },
2647
+ required: ["id", "graphs"]
2648
+ },
2649
+ //
2650
+ // VIEWS
2651
+ //
2547
2652
  view: {
2653
+ oneOf: [{ $ref: "#/$defs/view_with_scenario" }, { $ref: "#/$defs/view_with_rows" }]
2654
+ },
2655
+ view_with_scenario: {
2548
2656
  type: "object",
2549
2657
  additionalProperties: false,
2550
2658
  properties: {
@@ -2559,24 +2667,106 @@ var comparison_schema_default = {
2559
2667
  },
2560
2668
  graphs: {
2561
2669
  $ref: "#/$defs/view_graphs"
2670
+ },
2671
+ graph_order: {
2672
+ $ref: "#/$defs/view_graph_order"
2562
2673
  }
2563
2674
  },
2564
- required: ["scenario_ref", "graphs"]
2675
+ required: ["scenario_ref"]
2565
2676
  },
2566
- view_graphs: {
2567
- oneOf: [{ $ref: "#/$defs/view_graphs_preset" }, { $ref: "#/$defs/view_graphs_array" }]
2677
+ view_with_rows: {
2678
+ type: "object",
2679
+ additionalProperties: false,
2680
+ properties: {
2681
+ title: {
2682
+ type: "string"
2683
+ },
2684
+ subtitle: {
2685
+ type: "string"
2686
+ },
2687
+ rows: {
2688
+ $ref: "#/$defs/view_rows_array"
2689
+ }
2690
+ },
2691
+ required: ["title", "rows"]
2568
2692
  },
2569
- view_graphs_preset: {
2570
- type: "string",
2571
- enum: ["all"]
2693
+ view_rows_array: {
2694
+ type: "array",
2695
+ items: {
2696
+ $ref: "#/$defs/view_rows_array_item"
2697
+ },
2698
+ minItems: 1
2572
2699
  },
2573
- view_graphs_array: {
2700
+ view_rows_array_item: {
2701
+ type: "object",
2702
+ additionalProperties: false,
2703
+ properties: {
2704
+ row: {
2705
+ $ref: "#/$defs/view_row"
2706
+ }
2707
+ }
2708
+ },
2709
+ view_row: {
2710
+ type: "object",
2711
+ additionalProperties: false,
2712
+ properties: {
2713
+ title: {
2714
+ type: "string"
2715
+ },
2716
+ subtitle: {
2717
+ type: "string"
2718
+ },
2719
+ boxes: {
2720
+ $ref: "#/$defs/view_boxes_array"
2721
+ }
2722
+ },
2723
+ required: ["title", "boxes"]
2724
+ },
2725
+ view_boxes_array: {
2574
2726
  type: "array",
2575
2727
  items: {
2576
- type: "string"
2728
+ $ref: "#/$defs/view_boxes_array_item"
2577
2729
  },
2578
2730
  minItems: 1
2579
2731
  },
2732
+ view_boxes_array_item: {
2733
+ type: "object",
2734
+ additionalProperties: false,
2735
+ properties: {
2736
+ box: {
2737
+ $ref: "#/$defs/view_box"
2738
+ }
2739
+ }
2740
+ },
2741
+ view_box: {
2742
+ type: "object",
2743
+ additionalProperties: false,
2744
+ properties: {
2745
+ title: {
2746
+ type: "string"
2747
+ },
2748
+ subtitle: {
2749
+ type: "string"
2750
+ },
2751
+ dataset: {
2752
+ $ref: "#/$defs/dataset"
2753
+ },
2754
+ scenario_ref: {
2755
+ $ref: "#/$defs/scenario_ref_id"
2756
+ }
2757
+ },
2758
+ required: ["title", "dataset", "scenario_ref"]
2759
+ },
2760
+ view_graphs: {
2761
+ oneOf: [{ $ref: "#/$defs/graphs_preset" }, { $ref: "#/$defs/graphs_array" }, { $ref: "#/$defs/graph_group_ref" }]
2762
+ },
2763
+ view_graph_order: {
2764
+ type: "string",
2765
+ enum: ["default", "grouped-by-diffs"]
2766
+ },
2767
+ //
2768
+ // VIEW GROUPS
2769
+ //
2580
2770
  view_group_array_item: {
2581
2771
  type: "object",
2582
2772
  additionalProperties: false,
@@ -2649,6 +2839,7 @@ var comparison_schema_default = {
2649
2839
  function parseComparisonSpecs(specSource) {
2650
2840
  const scenarios = [];
2651
2841
  const scenarioGroups = [];
2842
+ const graphGroups = [];
2652
2843
  const viewGroups = [];
2653
2844
  const ajv = new Ajv2();
2654
2845
  const validate = ajv.compile(comparison_schema_default);
@@ -2669,6 +2860,8 @@ function parseComparisonSpecs(specSource) {
2669
2860
  scenarios.push(scenarioSpecFromParsed(specItem.scenario));
2670
2861
  } else if ("scenario_group" in specItem) {
2671
2862
  scenarioGroups.push(scenarioGroupSpecFromParsed(specItem.scenario_group));
2863
+ } else if ("graph_group" in specItem) {
2864
+ graphGroups.push(graphGroupSpecFromParsed(specItem.graph_group));
2672
2865
  } else if ("view_group" in specItem) {
2673
2866
  viewGroups.push(viewGroupSpecFromParsed(specItem.view_group));
2674
2867
  }
@@ -2686,9 +2879,17 @@ ${error.message}`;
2686
2879
  return ok2({
2687
2880
  scenarios,
2688
2881
  scenarioGroups,
2882
+ graphGroups,
2689
2883
  viewGroups
2690
2884
  });
2691
2885
  }
2886
+ function datasetSpecFromParsed(parsedDataset) {
2887
+ return {
2888
+ kind: "dataset",
2889
+ name: parsedDataset.name,
2890
+ source: parsedDataset.source
2891
+ };
2892
+ }
2692
2893
  function scenarioSpecFromParsed(parsedScenario) {
2693
2894
  if (parsedScenario.preset === "matrix") {
2694
2895
  return {
@@ -2781,13 +2982,50 @@ function scenarioGroupRefSpecFromParsed(parsedGroupRef) {
2781
2982
  groupId: parsedGroupRef.scenario_group_ref
2782
2983
  };
2783
2984
  }
2985
+ function graphGroupSpecFromParsed(parsedGraphGroup) {
2986
+ return {
2987
+ kind: "graph-group",
2988
+ id: parsedGraphGroup.id,
2989
+ graphIds: parsedGraphGroup.graphs
2990
+ };
2991
+ }
2784
2992
  function viewSpecFromParsed(parsedView) {
2993
+ let scenarioId;
2994
+ let rows;
2995
+ if (parsedView.scenario_ref !== void 0) {
2996
+ scenarioId = parsedView.scenario_ref;
2997
+ } else if (parsedView.rows !== void 0) {
2998
+ rows = parsedView.rows.map((item) => viewRowSpecFromParsed(item.row));
2999
+ }
3000
+ let graphs;
3001
+ if (parsedView.graphs !== void 0) {
3002
+ graphs = viewGraphsSpecFromParsed(parsedView.graphs);
3003
+ }
2785
3004
  return {
2786
3005
  kind: "view",
2787
3006
  title: parsedView.title,
2788
3007
  subtitle: parsedView.subtitle,
2789
- scenarioId: parsedView.scenario_ref,
2790
- graphs: viewGraphsSpecFromParsed(parsedView.graphs)
3008
+ scenarioId,
3009
+ rows,
3010
+ graphs,
3011
+ graphOrder: parsedView.graph_order
3012
+ };
3013
+ }
3014
+ function viewRowSpecFromParsed(parsedRow) {
3015
+ return {
3016
+ kind: "view-row",
3017
+ title: parsedRow.title,
3018
+ subtitle: parsedRow.subtitle,
3019
+ boxes: parsedRow.boxes.map((item) => viewBoxSpecFromParsed(item.box))
3020
+ };
3021
+ }
3022
+ function viewBoxSpecFromParsed(parsedBox) {
3023
+ return {
3024
+ kind: "view-box",
3025
+ title: parsedBox.title,
3026
+ subtitle: parsedBox.subtitle,
3027
+ dataset: datasetSpecFromParsed(parsedBox.dataset),
3028
+ scenarioId: parsedBox.scenario_ref
2791
3029
  };
2792
3030
  }
2793
3031
  function viewGraphsSpecFromParsed(parsedGraphs) {
@@ -2796,11 +3034,18 @@ function viewGraphsSpecFromParsed(parsedGraphs) {
2796
3034
  kind: "graphs-preset",
2797
3035
  preset: "all"
2798
3036
  };
2799
- } else {
3037
+ } else if (Array.isArray(parsedGraphs)) {
2800
3038
  return {
2801
3039
  kind: "graphs-array",
2802
3040
  graphIds: parsedGraphs
2803
3041
  };
3042
+ } else if ("graph_group_ref" in parsedGraphs) {
3043
+ return {
3044
+ kind: "graph-group-ref",
3045
+ groupId: parsedGraphs.graph_group_ref
3046
+ };
3047
+ } else {
3048
+ throw new Error("Invalid graphs spec in comparison view");
2804
3049
  }
2805
3050
  }
2806
3051
  function viewGroupSpecFromParsed(parsedViewGroup) {
@@ -2836,6 +3081,57 @@ function viewGroupSpecFromParsed(parsedViewGroup) {
2836
3081
  // src/comparison/config/resolve/comparison-resolver.ts
2837
3082
  import { assertNever as assertNever11 } from "assert-never";
2838
3083
 
3084
+ // src/bundle/model-inputs.ts
3085
+ var ModelInputs = class {
3086
+ constructor(modelSpec) {
3087
+ /** All inputs keyed by lookup name (lowercase variable name or alias). */
3088
+ this.inputsByLookupName = /* @__PURE__ */ new Map();
3089
+ /** All input ID aliases. */
3090
+ this.inputIdAliases = /* @__PURE__ */ new Set();
3091
+ for (const inputVar of modelSpec.inputVars.values()) {
3092
+ const varNameKey = inputVar.varName.toLowerCase();
3093
+ this.inputsByLookupName.set(varNameKey, inputVar);
3094
+ if (inputVar.inputId) {
3095
+ const idAlias = `id ${inputVar.inputId}`;
3096
+ this.inputIdAliases.add(idAlias);
3097
+ const idKey = idAlias.toLowerCase();
3098
+ this.inputsByLookupName.set(idKey, inputVar);
3099
+ }
3100
+ }
3101
+ if (modelSpec.inputAliases) {
3102
+ for (const [alias, varId] of modelSpec.inputAliases.entries()) {
3103
+ const aliasKey = alias.toLowerCase();
3104
+ if (this.inputsByLookupName.has(aliasKey)) {
3105
+ console.warn(
3106
+ `WARNING: Input variable already defined with a name that collides with alias '${alias}', skipping`
3107
+ );
3108
+ continue;
3109
+ }
3110
+ const inputVar = modelSpec.inputVars.get(varId);
3111
+ if (inputVar === void 0) {
3112
+ console.warn(`WARNING: No input variable found for '${varId}' associated with alias '${alias}', skipping`);
3113
+ continue;
3114
+ }
3115
+ this.inputsByLookupName.set(aliasKey, inputVar);
3116
+ }
3117
+ }
3118
+ }
3119
+ /**
3120
+ * Return the set of `id XYZ` aliases for all input variables.
3121
+ */
3122
+ getAllInputIdAliases() {
3123
+ return [...this.inputIdAliases];
3124
+ }
3125
+ /**
3126
+ * Return the `InputVar` that matches the requested name (either by variable name or alias).
3127
+ *
3128
+ * @param name The variable name or alias to match.
3129
+ */
3130
+ getInputVarForName(name) {
3131
+ return this.inputsByLookupName.get(name.toLowerCase());
3132
+ }
3133
+ };
3134
+
2839
3135
  // src/comparison/config/resolve/comparison-scenario-specs.ts
2840
3136
  import { assertNever as assertNever10 } from "assert-never";
2841
3137
  function scenarioSpecsFromSettings(settings) {
@@ -2860,28 +3156,34 @@ function scenarioSpecFromInputs(inputs, side) {
2860
3156
  if (state.inputVar === void 0) {
2861
3157
  return void 0;
2862
3158
  }
2863
- const varId = state.inputVar.varId;
2864
- if (state.position) {
2865
- settings.push(positionSetting(varId, state.position));
2866
- } else {
2867
- settings.push(valueSetting(varId, state.value));
2868
- }
3159
+ settings.push(inputSettingFromResolvedInputState(state));
2869
3160
  }
2870
3161
  return inputSettingsSpec(settings);
2871
3162
  }
3163
+ function inputSettingFromResolvedInputState(state) {
3164
+ const varId = state.inputVar.varId;
3165
+ if (state.position) {
3166
+ return positionSetting(varId, state.position);
3167
+ } else {
3168
+ return valueSetting(varId, state.value);
3169
+ }
3170
+ }
2872
3171
 
2873
3172
  // src/comparison/config/resolve/comparison-resolver.ts
2874
- function resolveComparisonSpecs(modelInputsL, modelInputsR, specs) {
3173
+ function resolveComparisonSpecs(modelSpecL, modelSpecR, specs) {
2875
3174
  let key = 1;
2876
3175
  const genKey = () => {
2877
3176
  return `${key++}`;
2878
3177
  };
3178
+ const modelInputsL = new ModelInputs(modelSpecL);
3179
+ const modelInputsR = new ModelInputs(modelSpecR);
3180
+ const modelOutputs = new ModelOutputs(modelSpecL, modelSpecR);
2879
3181
  const resolvedScenarios = new ResolvedScenarios();
2880
- for (const scenarioSpec of specs.scenarios) {
3182
+ for (const scenarioSpec of specs.scenarios || []) {
2881
3183
  resolvedScenarios.add(resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genKey));
2882
3184
  }
2883
3185
  const partiallyResolvedScenarioGroups = [];
2884
- for (const scenarioGroupSpec of specs.scenarioGroups) {
3186
+ for (const scenarioGroupSpec of specs.scenarioGroups || []) {
2885
3187
  const scenariosForGroup = [];
2886
3188
  for (const scenarioItem of scenarioGroupSpec.scenarios) {
2887
3189
  if (scenarioItem.kind === "scenario-ref") {
@@ -2904,7 +3206,7 @@ function resolveComparisonSpecs(modelInputsL, modelInputsR, specs) {
2904
3206
  if (scenarioItem.kind === "scenario-ref") {
2905
3207
  const referencedScenario = resolvedScenarios.getScenarioForId(scenarioItem.scenarioId);
2906
3208
  if (referencedScenario) {
2907
- const resolvedScenario = { ...referencedScenario };
3209
+ const resolvedScenario = __spreadValues({}, referencedScenario);
2908
3210
  if (scenarioItem.title) {
2909
3211
  resolvedScenario.title = scenarioItem.title;
2910
3212
  }
@@ -2929,9 +3231,21 @@ function resolveComparisonSpecs(modelInputsL, modelInputsR, specs) {
2929
3231
  scenarios: scenariosForGroup
2930
3232
  });
2931
3233
  }
3234
+ const resolvedGraphGroups = new ResolvedGraphGroups();
3235
+ for (const graphGroupSpec of specs.graphGroups || []) {
3236
+ resolvedGraphGroups.add(graphGroupSpec);
3237
+ }
2932
3238
  const resolvedViewGroups = [];
2933
- for (const viewGroupSpec of specs.viewGroups) {
2934
- const resolvedViewGroup = resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, viewGroupSpec);
3239
+ for (const viewGroupSpec of specs.viewGroups || []) {
3240
+ const resolvedViewGroup = resolveViewGroupFromSpec(
3241
+ modelSpecL,
3242
+ modelSpecR,
3243
+ modelOutputs,
3244
+ resolvedScenarios,
3245
+ resolvedScenarioGroups,
3246
+ resolvedGraphGroups,
3247
+ viewGroupSpec
3248
+ );
2935
3249
  resolvedViewGroups.push(resolvedViewGroup);
2936
3250
  }
2937
3251
  return {
@@ -2940,9 +3254,40 @@ function resolveComparisonSpecs(modelInputsL, modelInputsR, specs) {
2940
3254
  viewGroups: resolvedViewGroups
2941
3255
  };
2942
3256
  }
3257
+ var ModelOutputs = class {
3258
+ constructor(modelSpecL, modelSpecR) {
3259
+ this.modelSpecL = modelSpecL;
3260
+ this.modelSpecR = modelSpecR;
3261
+ }
3262
+ getDatasetForName(name, source) {
3263
+ function findOutputVar(modelSpec) {
3264
+ for (const outputVar of modelSpec.outputVars.values()) {
3265
+ if (outputVar.varName === name && outputVar.sourceName === source) {
3266
+ return outputVar;
3267
+ }
3268
+ }
3269
+ return void 0;
3270
+ }
3271
+ const outputVarR = findOutputVar(this.modelSpecR);
3272
+ if (outputVarR) {
3273
+ const datasetKey = outputVarR.datasetKey;
3274
+ const outputVarL = this.modelSpecL.outputVars.get(datasetKey);
3275
+ return {
3276
+ kind: "dataset",
3277
+ key: datasetKey,
3278
+ outputVarL,
3279
+ outputVarR
3280
+ };
3281
+ } else {
3282
+ return void 0;
3283
+ }
3284
+ }
3285
+ };
2943
3286
  var ResolvedScenarios = class {
2944
3287
  constructor() {
3288
+ /** The array of all resolved scenarios. */
2945
3289
  this.resolvedScenarios = [];
3290
+ /** The set of resolved scenarios that include an ID, keyed by ID. */
2946
3291
  this.resolvedScenariosById = /* @__PURE__ */ new Map();
2947
3292
  }
2948
3293
  add(scenarios) {
@@ -2992,6 +3337,20 @@ function resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genK
2992
3337
  )
2993
3338
  ];
2994
3339
  }
3340
+ case "scenario-with-distinct-inputs": {
3341
+ return [
3342
+ resolveScenarioForDistinctInputSpecs(
3343
+ modelInputsL,
3344
+ modelInputsR,
3345
+ genKey(),
3346
+ scenarioSpec.id,
3347
+ scenarioSpec.title,
3348
+ scenarioSpec.subtitle,
3349
+ scenarioSpec.inputsL,
3350
+ scenarioSpec.inputsR
3351
+ )
3352
+ ];
3353
+ }
2995
3354
  default:
2996
3355
  assertNever11(scenarioSpec);
2997
3356
  }
@@ -3068,6 +3427,59 @@ function resolveScenarioForInputSpecs(modelInputsL, modelInputsR, key, id, title
3068
3427
  specR
3069
3428
  };
3070
3429
  }
3430
+ function resolveScenarioForDistinctInputSpecs(modelInputsL, modelInputsR, key, id, title, subtitle, inputSpecsL, inputSpecsR) {
3431
+ const inputsWithErrors = [];
3432
+ const settingsL = [];
3433
+ const settingsR = [];
3434
+ function resolveInputSpec(side, modelInputs, inputSpec) {
3435
+ let inputState;
3436
+ switch (inputSpec.kind) {
3437
+ case "input-at-position":
3438
+ inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.position);
3439
+ break;
3440
+ case "input-at-value":
3441
+ inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.value);
3442
+ break;
3443
+ default:
3444
+ assertNever11(inputSpec);
3445
+ }
3446
+ if (inputState.error !== void 0) {
3447
+ inputsWithErrors.push({
3448
+ requestedName: inputSpec.inputName,
3449
+ stateL: side === "left" ? inputState : {},
3450
+ stateR: side === "right" ? inputState : {}
3451
+ });
3452
+ } else {
3453
+ const inputSetting = inputSettingFromResolvedInputState(inputState);
3454
+ if (side === "left") {
3455
+ settingsL.push(inputSetting);
3456
+ } else {
3457
+ settingsR.push(inputSetting);
3458
+ }
3459
+ }
3460
+ }
3461
+ inputSpecsL.forEach((inputSpec) => resolveInputSpec("left", modelInputsL, inputSpec));
3462
+ inputSpecsR.forEach((inputSpec) => resolveInputSpec("right", modelInputsR, inputSpec));
3463
+ let specL;
3464
+ let specR;
3465
+ if (inputsWithErrors.length === 0) {
3466
+ specL = inputSettingsSpec(settingsL);
3467
+ specR = inputSettingsSpec(settingsR);
3468
+ }
3469
+ return {
3470
+ kind: "scenario",
3471
+ key,
3472
+ id,
3473
+ title,
3474
+ subtitle,
3475
+ settings: {
3476
+ kind: "input-settings",
3477
+ inputs: inputsWithErrors
3478
+ },
3479
+ specL,
3480
+ specR
3481
+ };
3482
+ }
3071
3483
  function resolveInputForName(modelInputsL, modelInputsR, inputName, at) {
3072
3484
  return {
3073
3485
  requestedName: inputName,
@@ -3143,7 +3555,9 @@ function inputValueAtPosition2(inputVar, position) {
3143
3555
  }
3144
3556
  var ResolvedScenarioGroups = class {
3145
3557
  constructor() {
3558
+ /** The array of all resolved scenario groups. */
3146
3559
  this.resolvedGroups = [];
3560
+ /** The set of resolved scenario groups that include an ID, keyed by ID. */
3147
3561
  this.resolvedGroupsById = /* @__PURE__ */ new Map();
3148
3562
  }
3149
3563
  add(group) {
@@ -3162,33 +3576,73 @@ var ResolvedScenarioGroups = class {
3162
3576
  return this.resolvedGroupsById.get(id);
3163
3577
  }
3164
3578
  };
3165
- function resolveGraphsFromSpec(graphsSpec) {
3579
+ var ResolvedGraphGroups = class {
3580
+ constructor() {
3581
+ /** The set of resolved graph groups, keyed by ID. */
3582
+ this.resolvedGroupsById = /* @__PURE__ */ new Map();
3583
+ }
3584
+ add(group) {
3585
+ if (this.resolvedGroupsById.has(group.id)) {
3586
+ throw new Error(`Multiple graph groups defined with the same id (${group.id})`);
3587
+ }
3588
+ this.resolvedGroupsById.set(group.id, group);
3589
+ }
3590
+ getGroupForId(id) {
3591
+ return this.resolvedGroupsById.get(id);
3592
+ }
3593
+ };
3594
+ function resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, graphsSpec) {
3166
3595
  switch (graphsSpec.kind) {
3167
- case "graphs-preset":
3168
- return "all";
3596
+ case "graphs-preset": {
3597
+ switch (graphsSpec.preset) {
3598
+ case "all": {
3599
+ const graphIds = /* @__PURE__ */ new Set();
3600
+ const addGraphIds = (modelSpec) => {
3601
+ if (modelSpec.graphSpecs) {
3602
+ for (const graphSpec of modelSpec.graphSpecs) {
3603
+ graphIds.add(graphSpec.id);
3604
+ }
3605
+ }
3606
+ };
3607
+ addGraphIds(modelSpecL);
3608
+ addGraphIds(modelSpecR);
3609
+ return [...graphIds];
3610
+ }
3611
+ default:
3612
+ assertNever11(graphsSpec.preset);
3613
+ }
3614
+ }
3615
+ // eslint-disable-next-line no-fallthrough
3169
3616
  case "graphs-array":
3170
3617
  return graphsSpec.graphIds;
3618
+ case "graph-group-ref": {
3619
+ const groupSpec = resolvedGraphGroups.getGroupForId(graphsSpec.groupId);
3620
+ if (groupSpec === void 0) {
3621
+ throw new Error(`No graph group found for id ${graphsSpec.groupId}`);
3622
+ }
3623
+ return groupSpec.graphIds;
3624
+ }
3171
3625
  default:
3172
3626
  assertNever11(graphsSpec);
3173
3627
  }
3174
3628
  }
3175
- function resolveViewForScenarioId(resolvedScenarios, viewTitle, viewSubtitle, scenarioId, graphs) {
3629
+ function resolveViewForScenarioId(resolvedScenarios, viewTitle, viewSubtitle, scenarioId, graphIds, graphOrder) {
3176
3630
  const resolvedScenario = resolvedScenarios.getScenarioForId(scenarioId);
3177
3631
  if (resolvedScenario) {
3178
- return resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphs);
3632
+ return resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphIds, graphOrder);
3179
3633
  } else {
3180
3634
  return unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId);
3181
3635
  }
3182
3636
  }
3183
- function resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphs) {
3637
+ function resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphIds, graphOrder) {
3184
3638
  const resolvedScenario = resolvedScenarios.getScenarioForId(refSpec.scenarioId);
3185
3639
  if (resolvedScenario) {
3186
- return resolveViewForScenario(refSpec.title, refSpec.subtitle, resolvedScenario, graphs);
3640
+ return resolveViewForScenario(refSpec.title, refSpec.subtitle, resolvedScenario, graphIds, graphOrder);
3187
3641
  } else {
3188
3642
  return unresolvedViewForScenarioId(void 0, void 0, refSpec.scenarioId);
3189
3643
  }
3190
3644
  }
3191
- function resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphs) {
3645
+ function resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphIds, graphOrder) {
3192
3646
  if (viewTitle === void 0) {
3193
3647
  viewTitle = resolvedScenario.title;
3194
3648
  if (viewTitle === void 0) {
@@ -3203,15 +3657,83 @@ function resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graph
3203
3657
  title: viewTitle,
3204
3658
  subtitle: viewSubtitle,
3205
3659
  scenario: resolvedScenario,
3206
- graphs
3660
+ graphIds,
3661
+ graphOrder
3662
+ };
3663
+ }
3664
+ function resolveViewBoxForSpec(modelOutputs, resolvedScenarios, boxSpec) {
3665
+ const resolvedDataset = modelOutputs.getDatasetForName(boxSpec.dataset.name, boxSpec.dataset.source);
3666
+ if (resolvedDataset === void 0) {
3667
+ return {
3668
+ kind: "unresolved-view-box",
3669
+ datasetName: boxSpec.dataset.name,
3670
+ datasetSource: boxSpec.dataset.source
3671
+ };
3672
+ }
3673
+ const resolvedScenario = resolvedScenarios.getScenarioForId(boxSpec.scenarioId);
3674
+ if (resolvedScenario === void 0) {
3675
+ return {
3676
+ kind: "unresolved-view-box",
3677
+ scenarioId: boxSpec.scenarioId
3678
+ };
3679
+ }
3680
+ return {
3681
+ kind: "view-box",
3682
+ title: boxSpec.title,
3683
+ subtitle: boxSpec.subtitle,
3684
+ dataset: resolvedDataset,
3685
+ scenario: resolvedScenario
3686
+ };
3687
+ }
3688
+ function resolveViewRowForSpec(modelOutputs, resolvedScenarios, rowSpec) {
3689
+ const resolvedBoxes = [];
3690
+ for (const boxSpec of rowSpec.boxes) {
3691
+ const box = resolveViewBoxForSpec(modelOutputs, resolvedScenarios, boxSpec);
3692
+ if (box.kind === "view-box") {
3693
+ resolvedBoxes.push(box);
3694
+ } else {
3695
+ return box;
3696
+ }
3697
+ }
3698
+ return {
3699
+ kind: "view-row",
3700
+ title: rowSpec.title,
3701
+ subtitle: rowSpec.subtitle,
3702
+ boxes: resolvedBoxes
3207
3703
  };
3208
3704
  }
3209
- function unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId) {
3705
+ function resolveViewWithRowSpecs(modelOutputs, resolvedScenarios, viewTitle, viewSubtitle, rowSpecs) {
3706
+ const resolvedRows = [];
3707
+ for (const rowSpec of rowSpecs) {
3708
+ const row = resolveViewRowForSpec(modelOutputs, resolvedScenarios, rowSpec);
3709
+ if (row.kind === "view-row") {
3710
+ resolvedRows.push(row);
3711
+ } else {
3712
+ return unresolvedViewForScenarioId(viewTitle, viewSubtitle, row.scenarioId, row.datasetName, row.datasetSource);
3713
+ }
3714
+ }
3715
+ if (viewTitle === void 0) {
3716
+ viewTitle = "Untitled view";
3717
+ }
3718
+ return {
3719
+ kind: "view",
3720
+ title: viewTitle,
3721
+ subtitle: viewSubtitle,
3722
+ rows: resolvedRows,
3723
+ // TODO: The schema doesn't currently allow for graphs in a view that is defined
3724
+ // with rows. Probably we should have different view types to make this more clear.
3725
+ graphIds: [],
3726
+ graphOrder: "default"
3727
+ };
3728
+ }
3729
+ function unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId, datasetName, datasetSource) {
3210
3730
  return {
3211
3731
  kind: "unresolved-view",
3212
3732
  title: viewTitle,
3213
3733
  subtitle: viewSubtitle,
3214
- scenarioId
3734
+ scenarioId,
3735
+ datasetName,
3736
+ datasetSource
3215
3737
  };
3216
3738
  }
3217
3739
  function unresolvedViewForScenarioGroupId(viewTitle, viewSubtitle, scenarioGroupId) {
@@ -3222,29 +3744,46 @@ function unresolvedViewForScenarioGroupId(viewTitle, viewSubtitle, scenarioGroup
3222
3744
  scenarioGroupId
3223
3745
  };
3224
3746
  }
3225
- function resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, viewGroupSpec) {
3747
+ function resolveViewGroupFromSpec(modelSpecL, modelSpecR, modelOutputs, resolvedScenarios, resolvedScenarioGroups, resolvedGraphGroups, viewGroupSpec) {
3226
3748
  let views;
3227
3749
  switch (viewGroupSpec.kind) {
3228
3750
  case "view-group-with-views": {
3229
3751
  views = viewGroupSpec.views.map((viewSpec) => {
3230
- const graphs = resolveGraphsFromSpec(viewSpec.graphs);
3231
- return resolveViewForScenarioId(
3232
- resolvedScenarios,
3233
- viewSpec.title,
3234
- viewSpec.subtitle,
3235
- viewSpec.scenarioId,
3236
- graphs
3237
- );
3752
+ if (viewSpec.scenarioId) {
3753
+ let graphIds;
3754
+ if (viewSpec.graphs) {
3755
+ graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewSpec.graphs);
3756
+ } else {
3757
+ graphIds = [];
3758
+ }
3759
+ return resolveViewForScenarioId(
3760
+ resolvedScenarios,
3761
+ viewSpec.title,
3762
+ viewSpec.subtitle,
3763
+ viewSpec.scenarioId,
3764
+ graphIds,
3765
+ viewSpec.graphOrder || "default"
3766
+ );
3767
+ } else {
3768
+ return resolveViewWithRowSpecs(
3769
+ modelOutputs,
3770
+ resolvedScenarios,
3771
+ viewSpec.title,
3772
+ viewSpec.subtitle,
3773
+ viewSpec.rows
3774
+ );
3775
+ }
3238
3776
  });
3239
3777
  break;
3240
3778
  }
3241
3779
  case "view-group-with-scenarios": {
3242
- const graphs = resolveGraphsFromSpec(viewGroupSpec.graphs);
3780
+ const graphIds = resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, viewGroupSpec.graphs);
3781
+ const graphOrder = viewGroupSpec.graphOrder || "default";
3243
3782
  views = [];
3244
3783
  for (const refSpec of viewGroupSpec.scenarios) {
3245
3784
  switch (refSpec.kind) {
3246
3785
  case "scenario-ref":
3247
- views.push(resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphs));
3786
+ views.push(resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphIds, graphOrder));
3248
3787
  break;
3249
3788
  case "scenario-group-ref": {
3250
3789
  const resolvedGroup = resolvedScenarioGroups.getGroupForId(refSpec.groupId);
@@ -3255,7 +3794,7 @@ function resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, vie
3255
3794
  views.push(unresolvedViewForScenarioId(void 0, void 0, scenario.scenarioId));
3256
3795
  break;
3257
3796
  case "scenario":
3258
- views.push(resolveViewForScenario(void 0, void 0, scenario, graphs));
3797
+ views.push(resolveViewForScenario(void 0, void 0, scenario, graphIds, graphOrder));
3259
3798
  break;
3260
3799
  default:
3261
3800
  assertNever11(scenario);
@@ -3283,10 +3822,11 @@ function resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, vie
3283
3822
  }
3284
3823
 
3285
3824
  // src/comparison/config/comparison-config.ts
3286
- function resolveComparisonSpecsFromSources(modelInputsL, modelInputsR, specSources) {
3825
+ function resolveComparisonSpecsFromSources(modelSpecL, modelSpecR, specSources) {
3287
3826
  const combinedSpecs = {
3288
3827
  scenarios: [],
3289
3828
  scenarioGroups: [],
3829
+ graphGroups: [],
3290
3830
  viewGroups: []
3291
3831
  };
3292
3832
  for (const specSource of specSources) {
@@ -3303,11 +3843,12 @@ function resolveComparisonSpecsFromSources(modelInputsL, modelInputsR, specSourc
3303
3843
  } else {
3304
3844
  specs = specSource;
3305
3845
  }
3306
- combinedSpecs.scenarios.push(...specs.scenarios);
3307
- combinedSpecs.scenarioGroups.push(...specs.scenarioGroups);
3308
- combinedSpecs.viewGroups.push(...specs.viewGroups);
3846
+ combinedSpecs.scenarios.push(...specs.scenarios || []);
3847
+ combinedSpecs.scenarioGroups.push(...specs.scenarioGroups || []);
3848
+ combinedSpecs.graphGroups.push(...specs.graphGroups || []);
3849
+ combinedSpecs.viewGroups.push(...specs.viewGroups || []);
3309
3850
  }
3310
- return resolveComparisonSpecs(modelInputsL, modelInputsR, combinedSpecs);
3851
+ return resolveComparisonSpecs(modelSpecL, modelSpecR, combinedSpecs);
3311
3852
  }
3312
3853
 
3313
3854
  // src/comparison/config/comparison-datasets.ts
@@ -3315,7 +3856,14 @@ function getComparisonDatasets(modelSpecL, modelSpecR, datasetOptions) {
3315
3856
  return new ComparisonDatasetsImpl(modelSpecL, modelSpecR, datasetOptions);
3316
3857
  }
3317
3858
  var ComparisonDatasetsImpl = class {
3859
+ /**
3860
+ * @param modelSpecL The model spec for the "left" bundle being compared.
3861
+ * @param modelSpecR The model spec for the "right" bundle being compared.
3862
+ * @param datasetOptions The custom configuration for the datasets to be compared.
3863
+ */
3318
3864
  constructor(modelSpecL, modelSpecR, datasetOptions) {
3865
+ this.modelSpecL = modelSpecL;
3866
+ this.modelSpecR = modelSpecR;
3319
3867
  this.datasetOptions = datasetOptions;
3320
3868
  const renamedDatasetKeys = datasetOptions == null ? void 0 : datasetOptions.renamedDatasetKeys;
3321
3869
  const invertedRenamedKeys = /* @__PURE__ */ new Map();
@@ -3353,12 +3901,15 @@ var ComparisonDatasetsImpl = class {
3353
3901
  });
3354
3902
  }
3355
3903
  }
3904
+ // from ComparisonDatasets interface
3356
3905
  getAllDatasets() {
3357
3906
  return this.allDatasets.values();
3358
3907
  }
3908
+ // from ComparisonDatasets interface
3359
3909
  getDataset(datasetKey) {
3360
3910
  return this.allDatasets.get(datasetKey);
3361
3911
  }
3912
+ // from ComparisonDatasets interface
3362
3913
  getDatasetKeysForScenario(scenario) {
3363
3914
  var _a;
3364
3915
  if (((_a = this.datasetOptions) == null ? void 0 : _a.datasetKeysForScenario) !== void 0) {
@@ -3371,7 +3922,47 @@ var ComparisonDatasetsImpl = class {
3371
3922
  }
3372
3923
  }
3373
3924
  }
3925
+ // from ComparisonDatasets interface
3926
+ getReferencePlotsForDataset(datasetKey, scenario) {
3927
+ var _a;
3928
+ if (((_a = this.datasetOptions) == null ? void 0 : _a.referencePlotsForDataset) !== void 0) {
3929
+ const dataset = this.getDataset(datasetKey);
3930
+ if (dataset !== void 0) {
3931
+ return this.datasetOptions.referencePlotsForDataset(dataset, scenario);
3932
+ }
3933
+ }
3934
+ return [];
3935
+ }
3936
+ // from ComparisonDatasets interface
3937
+ getContextGraphIdsForDataset(datasetKey, scenario) {
3938
+ var _a;
3939
+ const dataset = this.getDataset(datasetKey);
3940
+ if (dataset === void 0) {
3941
+ return [];
3942
+ }
3943
+ if (((_a = this.datasetOptions) == null ? void 0 : _a.contextGraphIdsForDataset) !== void 0) {
3944
+ return this.datasetOptions.contextGraphIdsForDataset(dataset, scenario);
3945
+ } else {
3946
+ return getContextGraphIdsForDataset(this.modelSpecL, this.modelSpecR, dataset);
3947
+ }
3948
+ }
3374
3949
  };
3950
+ function getContextGraphIdsForDataset(modelSpecL, modelSpecR, dataset) {
3951
+ const contextGraphIds = /* @__PURE__ */ new Set();
3952
+ function addGraphs(modelSpec, outputVar) {
3953
+ for (const graphSpec of modelSpec.graphSpecs || []) {
3954
+ for (const graphDatasetSpec of graphSpec.datasets) {
3955
+ if (graphDatasetSpec.datasetKey === (outputVar == null ? void 0 : outputVar.datasetKey)) {
3956
+ contextGraphIds.add(graphSpec.id);
3957
+ break;
3958
+ }
3959
+ }
3960
+ }
3961
+ }
3962
+ addGraphs(modelSpecL, dataset.outputVarL);
3963
+ addGraphs(modelSpecR, dataset.outputVarR);
3964
+ return [...contextGraphIds];
3965
+ }
3375
3966
 
3376
3967
  // src/comparison/config/comparison-scenarios.ts
3377
3968
  function getComparisonScenarios(scenarios) {
@@ -3384,9 +3975,17 @@ var ComparisonScenariosImpl = class {
3384
3975
  this.scenarioDefs.set(scenario.key, scenario);
3385
3976
  }
3386
3977
  }
3978
+ /**
3979
+ * Return all `ComparisonScenario` instances that are available for comparisons.
3980
+ */
3387
3981
  getAllScenarios() {
3388
3982
  return this.scenarioDefs.values();
3389
3983
  }
3984
+ /**
3985
+ * Return the scenario definition for the given key.
3986
+ *
3987
+ * @param key The key for the scenario.
3988
+ */
3390
3989
  getScenario(key) {
3391
3990
  return this.scenarioDefs.get(key);
3392
3991
  }
@@ -3413,11 +4012,11 @@ var PromiseQueue = class {
3413
4012
  }
3414
4013
  add(f) {
3415
4014
  return new Promise((resolve, reject) => {
3416
- const run = async () => {
4015
+ const run = () => __async(this, null, function* () {
3417
4016
  this.runningCount++;
3418
4017
  const promise = f();
3419
4018
  try {
3420
- const result = await promise;
4019
+ const result = yield promise;
3421
4020
  resolve(result);
3422
4021
  } catch (e) {
3423
4022
  reject(e);
@@ -3425,7 +4024,7 @@ var PromiseQueue = class {
3425
4024
  this.runningCount--;
3426
4025
  this.runNext();
3427
4026
  }
3428
- };
4027
+ });
3429
4028
  if (this.runningCount < 1) {
3430
4029
  run();
3431
4030
  } else {
@@ -3444,81 +4043,82 @@ var PromiseQueue = class {
3444
4043
  };
3445
4044
 
3446
4045
  // src/config/config.ts
3447
- async function createConfig(options) {
3448
- var _a;
3449
- const origCurrentBundle = await loadSynchronized(options.current);
3450
- let currentBundle;
3451
- let comparisonConfig;
3452
- if (options.comparison === void 0) {
3453
- currentBundle = origCurrentBundle;
3454
- } else {
3455
- const baselineBundle = await loadSynchronized(options.comparison.baseline);
3456
- const renamedDatasetKeys = (_a = options.comparison.datasets) == null ? void 0 : _a.renamedDatasetKeys;
3457
- const invertedRenamedKeys = /* @__PURE__ */ new Map();
3458
- renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
3459
- invertedRenamedKeys.set(newKey, oldKey);
3460
- });
3461
- const rightKeyForLeftKey = (leftKey) => {
3462
- return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
3463
- };
3464
- const leftKeyForRightKey = (rightKey) => {
3465
- return invertedRenamedKeys.get(rightKey) || rightKey;
3466
- };
3467
- const origBundleModelR = origCurrentBundle.model;
3468
- const adjBundleModelR = {
3469
- modelSpec: origBundleModelR.modelSpec,
3470
- getDatasetsForScenario: async (scenarioSpec, datasetKeys) => {
3471
- const rightKeys = datasetKeys.map(rightKeyForLeftKey);
3472
- const result = await origBundleModelR.getDatasetsForScenario(scenarioSpec, rightKeys);
3473
- const mapWithRightKeys = result.datasetMap;
3474
- const mapWithLeftKeys = /* @__PURE__ */ new Map();
3475
- for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
3476
- const leftKey = leftKeyForRightKey(rightKey);
3477
- mapWithLeftKeys.set(leftKey, dataset);
3478
- }
3479
- return {
3480
- datasetMap: mapWithLeftKeys,
3481
- modelRunTime: result.modelRunTime
3482
- };
3483
- },
3484
- getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
3485
- getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
3486
- };
3487
- currentBundle = {
3488
- ...origCurrentBundle,
3489
- model: adjBundleModelR
4046
+ function createConfig(options) {
4047
+ return __async(this, null, function* () {
4048
+ var _a;
4049
+ const origCurrentBundle = yield loadSynchronized(options.current);
4050
+ let currentBundle;
4051
+ let comparisonConfig;
4052
+ if (options.comparison === void 0) {
4053
+ currentBundle = origCurrentBundle;
4054
+ } else {
4055
+ const baselineBundle = yield loadSynchronized(options.comparison.baseline);
4056
+ const renamedDatasetKeys = (_a = options.comparison.datasets) == null ? void 0 : _a.renamedDatasetKeys;
4057
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
4058
+ renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
4059
+ invertedRenamedKeys.set(newKey, oldKey);
4060
+ });
4061
+ const rightKeyForLeftKey = (leftKey) => {
4062
+ return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
4063
+ };
4064
+ const leftKeyForRightKey = (rightKey) => {
4065
+ return invertedRenamedKeys.get(rightKey) || rightKey;
4066
+ };
4067
+ const origBundleModelR = origCurrentBundle.model;
4068
+ const adjBundleModelR = {
4069
+ modelSpec: origBundleModelR.modelSpec,
4070
+ getDatasetsForScenario: (scenarioSpec, datasetKeys) => __async(this, null, function* () {
4071
+ const rightKeys = datasetKeys.map(rightKeyForLeftKey);
4072
+ const result = yield origBundleModelR.getDatasetsForScenario(scenarioSpec, rightKeys);
4073
+ const mapWithRightKeys = result.datasetMap;
4074
+ const mapWithLeftKeys = /* @__PURE__ */ new Map();
4075
+ for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
4076
+ const leftKey = leftKeyForRightKey(rightKey);
4077
+ mapWithLeftKeys.set(leftKey, dataset);
4078
+ }
4079
+ return {
4080
+ datasetMap: mapWithLeftKeys,
4081
+ modelRunTime: result.modelRunTime
4082
+ };
4083
+ }),
4084
+ getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
4085
+ getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
4086
+ };
4087
+ currentBundle = __spreadProps(__spreadValues({}, origCurrentBundle), {
4088
+ model: adjBundleModelR
4089
+ });
4090
+ const modelSpecL = baselineBundle.model.modelSpec;
4091
+ const modelSpecR = currentBundle.model.modelSpec;
4092
+ const comparisonDefs = resolveComparisonSpecsFromSources(modelSpecL, modelSpecR, options.comparison.specs);
4093
+ comparisonConfig = {
4094
+ bundleL: baselineBundle,
4095
+ bundleR: currentBundle,
4096
+ thresholds: options.comparison.thresholds,
4097
+ scenarios: getComparisonScenarios(comparisonDefs.scenarios),
4098
+ datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),
4099
+ viewGroups: comparisonDefs.viewGroups
4100
+ };
4101
+ }
4102
+ const checkConfig = {
4103
+ bundle: currentBundle,
4104
+ tests: options.check.tests
3490
4105
  };
3491
- const modelSpecL = baselineBundle.model.modelSpec;
3492
- const modelSpecR = currentBundle.model.modelSpec;
3493
- const modelInputsL = new ModelInputs(modelSpecL);
3494
- const modelInputsR = new ModelInputs(modelSpecR);
3495
- const comparisonDefs = resolveComparisonSpecsFromSources(modelInputsL, modelInputsR, options.comparison.specs);
3496
- comparisonConfig = {
3497
- bundleL: baselineBundle,
3498
- bundleR: currentBundle,
3499
- thresholds: options.comparison.thresholds,
3500
- scenarios: getComparisonScenarios(comparisonDefs.scenarios),
3501
- datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),
3502
- viewGroups: comparisonDefs.viewGroups
4106
+ return {
4107
+ check: checkConfig,
4108
+ comparison: comparisonConfig
3503
4109
  };
3504
- }
3505
- const checkConfig = {
3506
- bundle: currentBundle,
3507
- tests: options.check.tests
3508
- };
3509
- return {
3510
- check: checkConfig,
3511
- comparison: comparisonConfig
3512
- };
4110
+ });
3513
4111
  }
3514
- async function loadSynchronized(sourceBundle) {
3515
- const sourceModel = await sourceBundle.bundle.initModel();
3516
- const synchronizedModel = synchronizedBundleModel(sourceModel);
3517
- return {
3518
- name: sourceBundle.name,
3519
- version: sourceBundle.bundle.version,
3520
- model: synchronizedModel
3521
- };
4112
+ function loadSynchronized(sourceBundle) {
4113
+ return __async(this, null, function* () {
4114
+ const sourceModel = yield sourceBundle.bundle.initModel();
4115
+ const synchronizedModel = synchronizedBundleModel(sourceModel);
4116
+ return {
4117
+ name: sourceBundle.name,
4118
+ version: sourceBundle.bundle.version,
4119
+ model: synchronizedModel
4120
+ };
4121
+ });
3522
4122
  }
3523
4123
 
3524
4124
  // src/perf/perf-runner.ts
@@ -3568,22 +4168,22 @@ var PerfRunner = class {
3568
4168
  this.mode = mode;
3569
4169
  const scenarioSpec = allInputsAtPositionSpec("at-default");
3570
4170
  this.taskQueue = new TaskQueue({
3571
- process: async (request) => {
4171
+ process: (request) => __async(this, null, function* () {
3572
4172
  switch (request.kind) {
3573
4173
  case "left": {
3574
- const result = await bundleModelL.getDatasetsForScenario(scenarioSpec, []);
4174
+ const result = yield bundleModelL.getDatasetsForScenario(scenarioSpec, []);
3575
4175
  return {
3576
4176
  runTimeL: result.modelRunTime
3577
4177
  };
3578
4178
  }
3579
4179
  case "right": {
3580
- const result = await bundleModelR.getDatasetsForScenario(scenarioSpec, []);
4180
+ const result = yield bundleModelR.getDatasetsForScenario(scenarioSpec, []);
3581
4181
  return {
3582
4182
  runTimeR: result.modelRunTime
3583
4183
  };
3584
4184
  }
3585
4185
  case "both": {
3586
- const [resultL, resultR] = await Promise.all([
4186
+ const [resultL, resultR] = yield Promise.all([
3587
4187
  bundleModelL.getDatasetsForScenario(scenarioSpec, []),
3588
4188
  bundleModelR.getDatasetsForScenario(scenarioSpec, [])
3589
4189
  ]);
@@ -3595,7 +4195,7 @@ var PerfRunner = class {
3595
4195
  default:
3596
4196
  assertNever12(request.kind);
3597
4197
  }
3598
- }
4198
+ })
3599
4199
  });
3600
4200
  }
3601
4201
  start() {
@@ -3643,13 +4243,31 @@ var PerfRunner = class {
3643
4243
 
3644
4244
  // src/data/data-planner.ts
3645
4245
  var DataPlanner = class {
4246
+ /**
4247
+ * @param batchSize The maximum number of impl vars that can be fetched
4248
+ * with a single request; this is usually the same as the number of
4249
+ * normal model outputs.
4250
+ */
3646
4251
  constructor(batchSize) {
3647
4252
  this.batchSize = batchSize;
4253
+ /** Tasks that need to access both "left" and "right" models in parallel. */
3648
4254
  this.taskSetsLR = /* @__PURE__ */ new Map();
4255
+ /** Tasks that only need to access the "left" model. */
3649
4256
  this.taskSetsL = /* @__PURE__ */ new Map();
4257
+ /** Tasks that only need to access the "right" model. */
3650
4258
  this.taskSetsR = /* @__PURE__ */ new Map();
3651
4259
  this.complete = false;
3652
4260
  }
4261
+ /**
4262
+ * Add a request to the plan for the given scenario(s) and data task.
4263
+ *
4264
+ * @param scenarioSpecL The input scenario used to configure the "left" model, or undefined if no data
4265
+ * is needed from the left model.
4266
+ * @param scenarioSpecR The input scenario used to configure the "right" model, or undefined if no data
4267
+ * is needed from the right model.
4268
+ * @param datasetKey The key for the dataset to be fetched from each model for the given scenario.
4269
+ * @param dataAction The action to be performed with the fetched datasets.
4270
+ */
3653
4271
  addRequest(scenarioSpecL, scenarioSpecR, datasetKey, dataAction) {
3654
4272
  if (scenarioSpecL === void 0 && scenarioSpecR === void 0) {
3655
4273
  console.warn("WARNING: Both scenario specs are undefined for DataPlanner request, skipping");
@@ -3677,6 +4295,9 @@ var DataPlanner = class {
3677
4295
  dataAction
3678
4296
  });
3679
4297
  }
4298
+ /**
4299
+ * Build a plan that minimizes the number of data fetches needed.
4300
+ */
3680
4301
  buildPlan() {
3681
4302
  if (this.complete) {
3682
4303
  throw new Error("DataPlanner.buildPlan() can only be called once");
@@ -3727,6 +4348,12 @@ var DataTaskSet = class {
3727
4348
  this.modelTasks = /* @__PURE__ */ new Map();
3728
4349
  this.modelImplTasks = /* @__PURE__ */ new Map();
3729
4350
  }
4351
+ /**
4352
+ * Add a task that will be performed when the data is fetched for the scenario(s)
4353
+ * associated with this task set.
4354
+ *
4355
+ * @param dataTask A task that performs an action using the fetched dataset(s).
4356
+ */
3730
4357
  addTask(dataTask) {
3731
4358
  let taskMap;
3732
4359
  if (dataTask.datasetKey.startsWith("ModelImpl")) {
@@ -3741,6 +4368,11 @@ var DataTaskSet = class {
3741
4368
  }
3742
4369
  tasks.push(dataTask);
3743
4370
  }
4371
+ /**
4372
+ * Add all tasks from the given set into this set.
4373
+ *
4374
+ * @param otherTaskSet The other task set that will be merged into this one.
4375
+ */
3744
4376
  merge(otherTaskSet) {
3745
4377
  for (const tasks of otherTaskSet.modelTasks.values()) {
3746
4378
  for (const task of tasks) {
@@ -3753,6 +4385,12 @@ var DataTaskSet = class {
3753
4385
  }
3754
4386
  }
3755
4387
  }
4388
+ /**
4389
+ * Create one or more data requests that can be used to fetch data for the configured scenario(s).
4390
+ *
4391
+ * @param batchSize The maximum number of impl vars that can be fetched with a single request.
4392
+ * This is usually the same as the number of normal model outputs.
4393
+ */
3756
4394
  buildRequests(batchSize) {
3757
4395
  const dataRequests = [];
3758
4396
  if (this.modelTasks.size > 0) {
@@ -3988,42 +4626,46 @@ var SuiteRunner = class {
3988
4626
  });
3989
4627
  }
3990
4628
  }
3991
- async processRequest(request) {
3992
- var _a, _b;
3993
- const datasetKeySet = /* @__PURE__ */ new Set();
3994
- for (const dataTask of request.dataTasks) {
3995
- datasetKeySet.add(dataTask.datasetKey);
3996
- }
3997
- const datasetKeys = [...datasetKeySet];
3998
- async function getDatasets(bundleModel, scenarioSpec) {
3999
- if (bundleModel && scenarioSpec) {
4000
- return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys);
4001
- } else {
4002
- return void 0;
4003
- }
4004
- }
4005
- const bundleModelL = (_a = this.config.comparison) == null ? void 0 : _a.bundleL.model;
4006
- const bundleModelR = ((_b = this.config.comparison) == null ? void 0 : _b.bundleR.model) || this.config.check.bundle.model;
4007
- const [datasetsResultL, datasetsResultR] = await Promise.all([
4008
- getDatasets(bundleModelL, request.scenarioSpecL),
4009
- getDatasets(bundleModelR, request.scenarioSpecR)
4010
- ]);
4011
- if (datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime) {
4012
- this.perfStatsL.addRun(datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime);
4013
- }
4014
- if (datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime) {
4015
- this.perfStatsR.addRun(datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime);
4016
- }
4017
- const datasetMapL = datasetsResultL == null ? void 0 : datasetsResultL.datasetMap;
4018
- const datasetMapR = datasetsResultR == null ? void 0 : datasetsResultR.datasetMap;
4019
- for (const dataTask of request.dataTasks) {
4020
- const datasetL = datasetMapL == null ? void 0 : datasetMapL.get(dataTask.datasetKey);
4021
- const datasetR = datasetMapR == null ? void 0 : datasetMapR.get(dataTask.datasetKey);
4022
- dataTask.dataAction({
4023
- datasetL,
4024
- datasetR
4025
- });
4026
- }
4629
+ processRequest(request) {
4630
+ return __async(this, null, function* () {
4631
+ var _a, _b;
4632
+ const datasetKeySet = /* @__PURE__ */ new Set();
4633
+ for (const dataTask of request.dataTasks) {
4634
+ datasetKeySet.add(dataTask.datasetKey);
4635
+ }
4636
+ const datasetKeys = [...datasetKeySet];
4637
+ function getDatasets(bundleModel, scenarioSpec) {
4638
+ return __async(this, null, function* () {
4639
+ if (bundleModel && scenarioSpec) {
4640
+ return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys);
4641
+ } else {
4642
+ return void 0;
4643
+ }
4644
+ });
4645
+ }
4646
+ const bundleModelL = (_a = this.config.comparison) == null ? void 0 : _a.bundleL.model;
4647
+ const bundleModelR = ((_b = this.config.comparison) == null ? void 0 : _b.bundleR.model) || this.config.check.bundle.model;
4648
+ const [datasetsResultL, datasetsResultR] = yield Promise.all([
4649
+ getDatasets(bundleModelL, request.scenarioSpecL),
4650
+ getDatasets(bundleModelR, request.scenarioSpecR)
4651
+ ]);
4652
+ if (datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime) {
4653
+ this.perfStatsL.addRun(datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime);
4654
+ }
4655
+ if (datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime) {
4656
+ this.perfStatsR.addRun(datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime);
4657
+ }
4658
+ const datasetMapL = datasetsResultL == null ? void 0 : datasetsResultL.datasetMap;
4659
+ const datasetMapR = datasetsResultR == null ? void 0 : datasetsResultR.datasetMap;
4660
+ for (const dataTask of request.dataTasks) {
4661
+ const datasetL = datasetMapL == null ? void 0 : datasetMapL.get(dataTask.datasetKey);
4662
+ const datasetR = datasetMapR == null ? void 0 : datasetMapR.get(dataTask.datasetKey);
4663
+ dataTask.dataAction({
4664
+ datasetL,
4665
+ datasetR
4666
+ });
4667
+ }
4668
+ });
4027
4669
  }
4028
4670
  };
4029
4671
  function runSuite(config, callbacks, options) {
@@ -4059,6 +4701,7 @@ export {
4059
4701
  datasetMessage,
4060
4702
  diffDatasets,
4061
4703
  diffGraphs,
4704
+ getScoresForTestSummaries,
4062
4705
  predicateMessage,
4063
4706
  runSuite,
4064
4707
  scenarioMessage,