@sdeverywhere/check-core 0.1.0 → 0.1.2

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.cjs CHANGED
@@ -33,117 +33,69 @@ var __copyProps = (to, from, except, desc) => {
33
33
  }
34
34
  return to;
35
35
  };
36
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
36
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
37
+ // If the importer is in node compatibility mode or this is not an ESM
38
+ // file that has been converted to a CommonJS file using a Babel-
39
+ // compatible transform (i.e. "__esModule" has not been set), then set
40
+ // "default" to the CommonJS "module.exports" for node compatibility.
41
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
42
+ mod
43
+ ));
37
44
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
45
+ var __async = (__this, __arguments, generator) => {
46
+ return new Promise((resolve, reject) => {
47
+ var fulfilled = (value) => {
48
+ try {
49
+ step(generator.next(value));
50
+ } catch (e) {
51
+ reject(e);
52
+ }
53
+ };
54
+ var rejected = (value) => {
55
+ try {
56
+ step(generator.throw(value));
57
+ } catch (e) {
58
+ reject(e);
59
+ }
60
+ };
61
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
62
+ step((generator = generator.apply(__this, __arguments)).next());
63
+ });
64
+ };
38
65
 
39
66
  // src/index.ts
40
67
  var src_exports = {};
41
68
  __export(src_exports, {
42
69
  CheckDataCoordinator: () => CheckDataCoordinator,
43
- CompareDataCoordinator: () => CompareDataCoordinator,
44
- DatasetManager: () => DatasetManager,
70
+ ComparisonDataCoordinator: () => ComparisonDataCoordinator,
45
71
  PerfRunner: () => PerfRunner,
46
72
  PerfStats: () => PerfStats,
47
- ScenarioManager: () => ScenarioManager,
48
- allInputsAtPositionScenario: () => allInputsAtPositionScenario,
73
+ categorizeComparisonTestSummaries: () => categorizeComparisonTestSummaries,
49
74
  checkReportFromSummary: () => checkReportFromSummary,
50
75
  checkSummaryFromReport: () => checkSummaryFromReport,
51
- compareDatasets: () => compareDatasets,
52
- compareSummaryFromReport: () => compareSummaryFromReport,
76
+ comparisonSummaryFromReport: () => comparisonSummaryFromReport,
53
77
  createConfig: () => createConfig,
54
78
  datasetMessage: () => datasetMessage,
55
79
  diffDatasets: () => diffDatasets,
56
80
  diffGraphs: () => diffGraphs,
57
- inputAtPositionScenario: () => inputAtPositionScenario,
58
- inputAtValueScenario: () => inputAtValueScenario,
59
- matrixScenarios: () => matrixScenarios,
60
- positionSetting: () => positionSetting,
61
81
  predicateMessage: () => predicateMessage,
62
82
  runSuite: () => runSuite,
63
83
  scenarioMessage: () => scenarioMessage,
64
- settingsScenario: () => settingsScenario,
65
- suiteSummaryFromReport: () => suiteSummaryFromReport,
66
- valueSetting: () => valueSetting
84
+ suiteSummaryFromReport: () => suiteSummaryFromReport
67
85
  });
68
86
  module.exports = __toCommonJS(src_exports);
69
87
 
70
- // src/_shared/scenario.ts
71
- var import_assert_never = require("assert-never");
72
- function positionSetting(inputVarId, position) {
73
- return {
74
- kind: "position",
75
- inputVarId,
76
- position
77
- };
78
- }
79
- function valueSetting(inputVarId, value) {
80
- return {
81
- kind: "value",
82
- inputVarId,
83
- value
84
- };
85
- }
86
- function settingsScenario(key, groupKey, settings) {
87
- return {
88
- kind: "settings",
89
- key,
90
- groupKey,
91
- settings
92
- };
93
- }
94
- function inputAtPositionScenario(inputVarId, groupKey, position) {
95
- const key = keyForInputAtPosition(`input${inputVarId}`, position);
96
- return settingsScenario(key, groupKey, [positionSetting(inputVarId, position)]);
97
- }
98
- function inputAtValueScenario(inputVarId, groupKey, value) {
99
- const key = keyForInputAtValue(`input${inputVarId}`, value);
100
- return settingsScenario(key, groupKey, [valueSetting(inputVarId, value)]);
101
- }
102
- function allInputsAtPositionScenario(position) {
103
- return {
104
- kind: "all-inputs",
105
- key: keyForInputAtPosition("all_inputs", position),
106
- groupKey: "all_inputs",
107
- position
108
- };
109
- }
110
- function matrixScenarios(inputVarIds) {
111
- const scenarios = [];
112
- scenarios.push(allInputsAtPositionScenario("at-default"));
113
- scenarios.push(allInputsAtPositionScenario("at-minimum"));
114
- scenarios.push(allInputsAtPositionScenario("at-maximum"));
115
- for (const inputVarId of inputVarIds) {
116
- scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, "at-minimum"));
117
- scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, "at-maximum"));
118
- }
119
- return scenarios;
120
- }
121
- function keyForInputPosition(position) {
122
- switch (position) {
123
- case "at-default":
124
- return "default";
125
- case "at-minimum":
126
- return "min";
127
- case "at-maximum":
128
- return "max";
129
- default:
130
- (0, import_assert_never.assertNever)(position);
131
- }
132
- }
133
- function keyForInputAtPosition(inputKey, position) {
134
- return `${inputKey}_at_${keyForInputPosition(position)}`;
135
- }
136
- function keyForInputAtValue(inputKey, value) {
137
- return `${inputKey}_at_${value}`;
138
- }
139
-
140
88
  // src/_shared/task-queue.ts
141
89
  var TaskQueue = class {
142
90
  constructor(processor) {
143
91
  this.processor = processor;
92
+ /** The queue of task keys, most recent at front. */
144
93
  this.taskKeyQueue = [];
94
+ /** The map of tasks. */
145
95
  this.taskMap = /* @__PURE__ */ new Map();
96
+ /** Whether tasks are being processed. */
146
97
  this.processing = false;
98
+ /** Whether `shutdown` has been called. */
147
99
  this.stopped = false;
148
100
  }
149
101
  addTask(key, input, onComplete) {
@@ -181,39 +133,41 @@ var TaskQueue = class {
181
133
  });
182
134
  }
183
135
  }
184
- async processNextTask() {
185
- var _a, _b;
186
- const taskKey = this.taskKeyQueue.shift();
187
- if (!taskKey) {
188
- return;
189
- }
190
- const task = this.taskMap.get(taskKey);
191
- if (task) {
192
- this.taskMap.delete(taskKey);
193
- } else {
194
- return;
195
- }
196
- let output;
197
- try {
198
- output = await this.processor.process(task.input);
199
- } catch (e) {
200
- if (!this.stopped) {
201
- this.shutdown();
202
- (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
136
+ processNextTask() {
137
+ return __async(this, null, function* () {
138
+ var _a, _b;
139
+ const taskKey = this.taskKeyQueue.shift();
140
+ if (!taskKey) {
141
+ return;
203
142
  }
204
- return;
205
- }
206
- task.onComplete(output);
207
- if (this.taskKeyQueue.length > 0) {
208
- setTimeout(() => {
209
- this.processNextTask();
210
- });
211
- } else {
212
- this.processing = false;
213
- if (!this.stopped) {
214
- (_b = this.onIdle) == null ? void 0 : _b.call(this);
143
+ const task = this.taskMap.get(taskKey);
144
+ if (task) {
145
+ this.taskMap.delete(taskKey);
146
+ } else {
147
+ return;
215
148
  }
216
- }
149
+ let output;
150
+ try {
151
+ output = yield this.processor.process(task.input);
152
+ } catch (e) {
153
+ if (!this.stopped) {
154
+ this.shutdown();
155
+ (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
156
+ }
157
+ return;
158
+ }
159
+ task.onComplete(output);
160
+ if (this.taskKeyQueue.length > 0) {
161
+ setTimeout(() => {
162
+ this.processNextTask();
163
+ });
164
+ } else {
165
+ this.processing = false;
166
+ if (!this.stopped) {
167
+ (_b = this.onIdle) == null ? void 0 : _b.call(this);
168
+ }
169
+ }
170
+ });
217
171
  }
218
172
  };
219
173
 
@@ -222,18 +176,18 @@ var CheckDataCoordinator = class {
222
176
  constructor(bundleModel) {
223
177
  this.bundleModel = bundleModel;
224
178
  this.taskQueue = new TaskQueue({
225
- process: async (request) => {
226
- const result = await this.bundleModel.getDatasetsForScenario(request.scenario, [request.datasetKey]);
179
+ process: (request) => __async(this, null, function* () {
180
+ const result = yield this.bundleModel.getDatasetsForScenario(request.scenarioSpec, [request.datasetKey]);
227
181
  const dataset = result.datasetMap.get(request.datasetKey);
228
182
  return {
229
183
  dataset
230
184
  };
231
- }
185
+ })
232
186
  });
233
187
  }
234
- requestDataset(requestKey, scenario, datasetKey, onResponse) {
188
+ requestDataset(requestKey, scenarioSpec, datasetKey, onResponse) {
235
189
  const request = {
236
- scenario,
190
+ scenarioSpec,
237
191
  datasetKey
238
192
  };
239
193
  this.taskQueue.addTask(requestKey, request, (response) => {
@@ -246,10 +200,10 @@ var CheckDataCoordinator = class {
246
200
  };
247
201
 
248
202
  // src/check/check-report.ts
249
- var import_assert_never3 = __toESM(require("assert-never"), 1);
203
+ var import_assert_never2 = __toESM(require("assert-never"), 1);
250
204
 
251
205
  // src/check/check-predicate.ts
252
- var import_assert_never2 = __toESM(require("assert-never"), 1);
206
+ var import_assert_never = __toESM(require("assert-never"), 1);
253
207
  function symbolForPredicateOp(op) {
254
208
  switch (op) {
255
209
  case "gt":
@@ -265,7 +219,7 @@ function symbolForPredicateOp(op) {
265
219
  case "approx":
266
220
  return "\u2248";
267
221
  default:
268
- (0, import_assert_never2.default)(op);
222
+ (0, import_assert_never.default)(op);
269
223
  }
270
224
  }
271
225
 
@@ -279,7 +233,7 @@ function buildCheckReport(checkPlan, checkResults) {
279
233
  const scenarioReports = [];
280
234
  for (const scenarioPlan of testPlan.scenarios) {
281
235
  let scenarioStatus = "passed";
282
- if (scenarioPlan.checkScenario.scenario === void 0) {
236
+ if (scenarioPlan.checkScenario.spec === void 0) {
283
237
  testStatus = "error";
284
238
  scenarioStatus = "error";
285
239
  }
@@ -376,14 +330,14 @@ function predicateReport(predicatePlan, checkKey, result) {
376
330
  };
377
331
  opRef = opDataRef;
378
332
  opValue = `${sym} '${dataRef.dataset.name}'`;
379
- const refScenario = (_b = dataRef.scenario) == null ? void 0 : _b.scenario;
380
- if (!refScenario) {
333
+ const refScenarioSpec = (_b = dataRef.scenario) == null ? void 0 : _b.spec;
334
+ if (!refScenarioSpec) {
381
335
  return;
382
336
  }
383
337
  if (predOp.scenario === "inherit") {
384
338
  opValue += ` (w/ same scenario)`;
385
339
  } else {
386
- if (refScenario.kind === "all-inputs" && refScenario.position === "at-default") {
340
+ if (refScenarioSpec.kind === "all-inputs" && refScenarioSpec.position === "at-default") {
387
341
  opValue += ` (w/ default scenario)`;
388
342
  } else {
389
343
  opValue += ` (w/ configured scenario)`;
@@ -418,7 +372,7 @@ function predicateReport(predicatePlan, checkKey, result) {
418
372
  }
419
373
  function scenarioMessage(scenario, bold) {
420
374
  const checkScenario = scenario.checkScenario;
421
- if (checkScenario.scenario === void 0) {
375
+ if (checkScenario.spec === void 0) {
422
376
  if (checkScenario.error) {
423
377
  switch (checkScenario.error.kind) {
424
378
  case "unknown-input-group":
@@ -426,7 +380,7 @@ function scenarioMessage(scenario, bold) {
426
380
  case "empty-input-group":
427
381
  return `error: input group ${bold(checkScenario.error.name)} is empty`;
428
382
  default:
429
- (0, import_assert_never3.default)(checkScenario.error.kind);
383
+ (0, import_assert_never2.default)(checkScenario.error.kind);
430
384
  }
431
385
  } else {
432
386
  const badInputNames = checkScenario.inputDescs.filter((d) => d.inputVar === void 0).map((d) => bold(d.name));
@@ -443,7 +397,7 @@ function scenarioMessage(scenario, bold) {
443
397
  case "at-maximum":
444
398
  return "maximum";
445
399
  default:
446
- (0, import_assert_never3.default)(position);
400
+ (0, import_assert_never2.default)(position);
447
401
  }
448
402
  }
449
403
  function inputMessage(inputDesc) {
@@ -458,13 +412,13 @@ function scenarioMessage(scenario, bold) {
458
412
  }
459
413
  return msg;
460
414
  }
461
- if (checkScenario.scenario.kind === "all-inputs") {
462
- const position = checkScenario.scenario.position;
415
+ if (checkScenario.spec.kind === "all-inputs") {
416
+ const position = checkScenario.spec.position;
463
417
  return `when ${bold("all inputs")} are at ${bold(positionName(position))}...`;
464
418
  } else if (checkScenario.inputGroupName) {
465
419
  let position = "at-default";
466
- if (checkScenario.scenario.settings[0].kind === "position") {
467
- position = checkScenario.scenario.settings[0].position;
420
+ if (checkScenario.spec.settings[0].kind === "position") {
421
+ position = checkScenario.spec.settings[0].position;
468
422
  }
469
423
  const groupName = checkScenario.inputGroupName;
470
424
  return `when all inputs in ${bold(groupName)} are at ${bold(positionName(position))}...`;
@@ -497,7 +451,7 @@ function predicateMessage(predicate, bold) {
497
451
  case "empty-input-group":
498
452
  return `error: referenced input group ${bold(result.errorInfo.name)} is empty`;
499
453
  default:
500
- (0, import_assert_never3.default)(result.errorInfo.kind);
454
+ (0, import_assert_never2.default)(result.errorInfo.kind);
501
455
  }
502
456
  } else {
503
457
  return `unknown error`;
@@ -1068,7 +1022,7 @@ function parseTestYaml(yamlStrings) {
1068
1022
  groups.push(group);
1069
1023
  }
1070
1024
  } else {
1071
- let msg = "Failed to parse YAML tests";
1025
+ let msg = "Failed to parse YAML check definitions";
1072
1026
  for (const error of validate.errors || []) {
1073
1027
  if (error.message) {
1074
1028
  msg += `
@@ -1242,9 +1196,12 @@ function actionForPredicate(predicateSpec) {
1242
1196
 
1243
1197
  // src/_shared/combo.ts
1244
1198
  function cartesianProductOf(arr) {
1245
- return arr.reduce((a, b) => {
1246
- return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
1247
- }, [[]]);
1199
+ return arr.reduce(
1200
+ (a, b) => {
1201
+ return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
1202
+ },
1203
+ [[]]
1204
+ );
1248
1205
  }
1249
1206
 
1250
1207
  // src/check/check-dataset.ts
@@ -1335,14 +1292,16 @@ function matchByName(modelSpec, datasetName, datasetSource) {
1335
1292
  };
1336
1293
  }
1337
1294
  function matchByGroup(modelSpec, groupName) {
1338
- const groupToMatch = groupName.toLowerCase();
1339
1295
  let matchedGroupName;
1340
1296
  let matchedGroupDatasetKeys;
1341
- for (const [group, datasetKeys] of modelSpec.datasetGroups) {
1342
- if (group.toLowerCase() === groupToMatch) {
1343
- matchedGroupName = group;
1344
- matchedGroupDatasetKeys = datasetKeys;
1345
- break;
1297
+ if (modelSpec.datasetGroups) {
1298
+ const groupToMatch = groupName.toLowerCase();
1299
+ for (const [group, datasetKeys] of modelSpec.datasetGroups) {
1300
+ if (group.toLowerCase() === groupToMatch) {
1301
+ matchedGroupName = group;
1302
+ matchedGroupDatasetKeys = datasetKeys;
1303
+ break;
1304
+ }
1346
1305
  }
1347
1306
  }
1348
1307
  if (matchedGroupName === void 0) {
@@ -1419,6 +1378,65 @@ function matchByType(modelSpec, varTypeToMatch) {
1419
1378
 
1420
1379
  // src/check/check-scenario.ts
1421
1380
  var import_assert_never4 = __toESM(require("assert-never"), 1);
1381
+
1382
+ // src/_shared/scenario-specs.ts
1383
+ var import_assert_never3 = require("assert-never");
1384
+ function positionSetting(inputVarId, position) {
1385
+ return {
1386
+ kind: "position",
1387
+ inputVarId,
1388
+ position
1389
+ };
1390
+ }
1391
+ function valueSetting(inputVarId, value) {
1392
+ return {
1393
+ kind: "value",
1394
+ inputVarId,
1395
+ value
1396
+ };
1397
+ }
1398
+ function inputSettingsSpec(settings) {
1399
+ const uidParts = settings.map((setting) => {
1400
+ switch (setting.kind) {
1401
+ case "position":
1402
+ return `${setting.inputVarId}_at_${keyForInputPosition(setting.position)}`;
1403
+ case "value":
1404
+ return `${setting.inputVarId}_at_${setting.value}`;
1405
+ default:
1406
+ (0, import_assert_never3.assertNever)(setting);
1407
+ }
1408
+ });
1409
+ const uid = `inputs_${uidParts.sort().join("_")}`;
1410
+ return {
1411
+ kind: "input-settings",
1412
+ uid,
1413
+ settings
1414
+ };
1415
+ }
1416
+ function inputAtPositionSpec(inputVarId, position) {
1417
+ return inputSettingsSpec([positionSetting(inputVarId, position)]);
1418
+ }
1419
+ function allInputsAtPositionSpec(position) {
1420
+ return {
1421
+ kind: "all-inputs",
1422
+ uid: `all_inputs_at_${keyForInputPosition(position)}`,
1423
+ position
1424
+ };
1425
+ }
1426
+ function keyForInputPosition(position) {
1427
+ switch (position) {
1428
+ case "at-default":
1429
+ return "default";
1430
+ case "at-minimum":
1431
+ return "min";
1432
+ case "at-maximum":
1433
+ return "max";
1434
+ default:
1435
+ (0, import_assert_never3.assertNever)(position);
1436
+ }
1437
+ }
1438
+
1439
+ // src/check/check-scenario.ts
1422
1440
  function expandScenarios(modelSpec, scenarioSpecs, simplify) {
1423
1441
  if (scenarioSpecs.length === 0) {
1424
1442
  const scenarioSpec = {
@@ -1495,10 +1513,12 @@ function inputDescForName(modelSpec, inputName, at) {
1495
1513
  }
1496
1514
  }
1497
1515
  function groupForName(modelSpec, groupName) {
1498
- const groupToMatch = groupName.toLowerCase();
1499
- for (const [group, inputVars] of modelSpec.inputGroups) {
1500
- if (group.toLowerCase() === groupToMatch) {
1501
- return [group, inputVars];
1516
+ if (modelSpec.inputGroups) {
1517
+ const groupToMatch = groupName.toLowerCase();
1518
+ for (const [group, inputVars] of modelSpec.inputGroups) {
1519
+ if (group.toLowerCase() === groupToMatch) {
1520
+ return [group, inputVars];
1521
+ }
1502
1522
  }
1503
1523
  }
1504
1524
  return void 0;
@@ -1513,56 +1533,35 @@ function errorScenarioForInputGroup(kind, groupName) {
1513
1533
  };
1514
1534
  }
1515
1535
  function checkScenarioWithAllInputsAtPosition(position) {
1516
- const scenario = allInputsAtPositionScenario(position);
1517
1536
  return {
1518
- scenario,
1537
+ spec: allInputsAtPositionSpec(position),
1519
1538
  inputDescs: []
1520
1539
  };
1521
1540
  }
1522
1541
  function checkScenarioWithInputAtPosition(inputVar, position) {
1523
1542
  const varId = inputVar.varId;
1524
- const scenario = inputAtPositionScenario(varId, varId, position);
1525
1543
  return {
1526
- scenario,
1544
+ spec: inputAtPositionSpec(varId, position),
1527
1545
  inputDescs: [inputDescAtPosition(inputVar, position)]
1528
1546
  };
1529
1547
  }
1530
1548
  function checkScenarioForInputDescs(groupName, inputDescs) {
1531
- let scenario;
1549
+ let spec;
1532
1550
  if (inputDescs.every((desc) => desc.inputVar !== void 0)) {
1533
- const settings = [];
1534
- const keyParts = [];
1535
- for (const inputDesc of inputDescs) {
1551
+ const settings = inputDescs.map((inputDesc) => {
1536
1552
  const varId = inputDesc.inputVar.varId;
1537
1553
  if (inputDesc.position) {
1538
- settings.push(positionSetting(varId, inputDesc.position));
1539
- keyParts.push(keyForInputAtPosition(varId, inputDesc.position));
1540
- } else {
1541
- settings.push(valueSetting(varId, inputDesc.value));
1542
- keyParts.push(keyForInputAtValue(varId, inputDesc.value));
1543
- }
1544
- }
1545
- if (settings.length === 1) {
1546
- const scenarioKey = `input${keyParts[0]}`;
1547
- const groupKey = settings[0].inputVarId;
1548
- scenario = settingsScenario(scenarioKey, groupKey, settings);
1549
- } else if (settings.length > 1) {
1550
- let scenarioKey;
1551
- let groupKey;
1552
- if (groupName) {
1553
- scenarioKey = `group_${groupName.toLowerCase().replace(/ /g, "_")}`;
1554
- groupKey = scenarioKey;
1554
+ return positionSetting(varId, inputDesc.position);
1555
1555
  } else {
1556
- scenarioKey = "multi" + keyParts.join("_");
1557
- groupKey = scenarioKey;
1556
+ return valueSetting(varId, inputDesc.value);
1558
1557
  }
1559
- scenario = settingsScenario(scenarioKey, groupKey, settings);
1560
- }
1558
+ });
1559
+ spec = inputSettingsSpec(settings);
1561
1560
  } else {
1562
- scenario = void 0;
1561
+ spec = void 0;
1563
1562
  }
1564
1563
  return {
1565
- scenario,
1564
+ spec,
1566
1565
  inputGroupName: groupName,
1567
1566
  inputDescs
1568
1567
  };
@@ -1676,7 +1675,7 @@ var CheckPlanner = class {
1676
1675
  }
1677
1676
  const planScenarios = [];
1678
1677
  for (const checkScenario of checkScenarios) {
1679
- if (checkScenario.scenario === void 0) {
1678
+ if (checkScenario.spec === void 0) {
1680
1679
  planScenarios.push({
1681
1680
  checkScenario,
1682
1681
  datasets: []
@@ -1736,6 +1735,24 @@ var CheckPlanner = class {
1736
1735
  dataRefs: this.dataRefs
1737
1736
  };
1738
1737
  }
1738
+ /**
1739
+ * Record any references to additional datasets contained in the given predicate.
1740
+ * For example, if the predicate is:
1741
+ * ```
1742
+ * gt:
1743
+ * dataset:
1744
+ * name: 'XYZ'
1745
+ * scenario:
1746
+ * inputs: all
1747
+ * at: default
1748
+ * ```
1749
+ * this will add a reference to the scenario/dataset pair so that the data can
1750
+ * be fetched in a later stage.
1751
+ *
1752
+ * @param predicateSpec The predicate spec.
1753
+ * @param checkScenario The scenario in which the dataset is being checked.
1754
+ * @param checkDataset The dataset that is being checked.
1755
+ */
1739
1756
  addDataRefs(predicateSpec, checkScenario, checkDataset) {
1740
1757
  let dataRefs;
1741
1758
  const addDataRef = (op) => {
@@ -1785,8 +1802,8 @@ var CheckPlanner = class {
1785
1802
  }
1786
1803
  }
1787
1804
  let dataRefKey;
1788
- if (refScenario.scenario && refDataset.datasetKey) {
1789
- dataRefKey = `${refScenario.scenario.key}::${refDataset.datasetKey}`;
1805
+ if (refScenario.spec && refDataset.datasetKey) {
1806
+ dataRefKey = `${refScenario.spec.uid}::${refDataset.datasetKey}`;
1790
1807
  }
1791
1808
  const dataRef = {
1792
1809
  key: dataRefKey,
@@ -1841,14 +1858,14 @@ function checkSummaryFromReport(checkReport) {
1841
1858
  predicateSummaries
1842
1859
  };
1843
1860
  }
1844
- function checkReportFromSummary(checkConfig, checkSummary, simplifyScenarios) {
1861
+ function checkReportFromSummary(checkConfig, checkSummary) {
1845
1862
  const checkSpecResult = parseTestYaml(checkConfig.tests);
1846
1863
  if (checkSpecResult.isErr()) {
1847
1864
  return void 0;
1848
1865
  }
1849
1866
  const checkSpec = checkSpecResult.value;
1850
1867
  const checkPlanner = new CheckPlanner(checkConfig.bundle.model.modelSpec);
1851
- checkPlanner.addAllChecks(checkSpec, simplifyScenarios);
1868
+ checkPlanner.addAllChecks(checkSpec, false);
1852
1869
  const checkPlan = checkPlanner.buildPlan();
1853
1870
  const checkResults = /* @__PURE__ */ new Map();
1854
1871
  for (const predicateSummary of checkSummary.predicateSummaries) {
@@ -1857,44 +1874,74 @@ function checkReportFromSummary(checkConfig, checkSummary, simplifyScenarios) {
1857
1874
  return buildCheckReport(checkPlan, checkResults);
1858
1875
  }
1859
1876
 
1860
- // src/compare/compare-data-coordinator.ts
1877
+ // src/comparison/run/comparison-data-coordinator.ts
1861
1878
  var import_assert_never7 = require("assert-never");
1862
- var CompareDataCoordinator = class {
1879
+ var ComparisonDataCoordinator = class {
1863
1880
  constructor(bundleModelL, bundleModelR) {
1864
1881
  this.bundleModelL = bundleModelL;
1865
1882
  this.bundleModelR = bundleModelR;
1866
1883
  this.taskQueue = new TaskQueue({
1867
- process: async (request) => {
1884
+ process: (request) => __async(this, null, function* () {
1868
1885
  switch (request.kind) {
1869
- case "dataset": {
1870
- const [resultL, resultR] = await Promise.all([
1871
- this.bundleModelL.getDatasetsForScenario(request.scenario, request.datasetKeys),
1872
- this.bundleModelR.getDatasetsForScenario(request.scenario, request.datasetKeys)
1873
- ]);
1874
- return {
1875
- kind: "dataset",
1876
- datasetMapL: resultL.datasetMap,
1877
- datasetMapR: resultR.datasetMap
1878
- };
1879
- }
1880
- case "graph-data": {
1881
- const bundleModel = request.bundle === "right" ? this.bundleModelR : this.bundleModelL;
1882
- const graphData = await bundleModel.getGraphDataForScenario(request.scenario, request.graphId);
1883
- return {
1884
- kind: "graph-data",
1885
- graphData
1886
- };
1887
- }
1886
+ case "dataset":
1887
+ return this.processDatasetRequest(request);
1888
+ case "graph-data":
1889
+ return this.processGraphDataRequest(request);
1888
1890
  default:
1889
1891
  (0, import_assert_never7.assertNever)(request);
1890
1892
  }
1893
+ })
1894
+ });
1895
+ }
1896
+ processDatasetRequest(request) {
1897
+ return __async(this, null, function* () {
1898
+ function fetchDatasets(bundleModel, scenarioSpec) {
1899
+ return __async(this, null, function* () {
1900
+ if (scenarioSpec) {
1901
+ return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys);
1902
+ } else {
1903
+ return void 0;
1904
+ }
1905
+ });
1906
+ }
1907
+ const [resultL, resultR] = yield Promise.all([
1908
+ fetchDatasets(this.bundleModelL, request.scenarioSpecL),
1909
+ fetchDatasets(this.bundleModelR, request.scenarioSpecR)
1910
+ ]);
1911
+ return {
1912
+ kind: "dataset",
1913
+ datasetMapL: resultL == null ? void 0 : resultL.datasetMap,
1914
+ datasetMapR: resultR == null ? void 0 : resultR.datasetMap
1915
+ };
1916
+ });
1917
+ }
1918
+ processGraphDataRequest(request) {
1919
+ return __async(this, null, function* () {
1920
+ function fetchGraphData(bundleModel, scenarioSpec) {
1921
+ return __async(this, null, function* () {
1922
+ if (scenarioSpec) {
1923
+ return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId);
1924
+ } else {
1925
+ return void 0;
1926
+ }
1927
+ });
1891
1928
  }
1929
+ const [graphDataL, graphDataR] = yield Promise.all([
1930
+ fetchGraphData(this.bundleModelL, request.scenarioSpecL),
1931
+ fetchGraphData(this.bundleModelR, request.scenarioSpecR)
1932
+ ]);
1933
+ return {
1934
+ kind: "graph-data",
1935
+ graphDataL,
1936
+ graphDataR
1937
+ };
1892
1938
  });
1893
1939
  }
1894
- requestDatasetMaps(requestKey, scenario, datasetKeys, onResponse) {
1940
+ requestDatasetMaps(requestKey, scenarioSpecL, scenarioSpecR, datasetKeys, onResponse) {
1895
1941
  const request = {
1896
1942
  kind: "dataset",
1897
- scenario,
1943
+ scenarioSpecL,
1944
+ scenarioSpecR,
1898
1945
  datasetKeys
1899
1946
  };
1900
1947
  this.taskQueue.addTask(requestKey, request, (response) => {
@@ -1903,16 +1950,16 @@ var CompareDataCoordinator = class {
1903
1950
  }
1904
1951
  });
1905
1952
  }
1906
- requestGraphData(requestKey, bundle, scenario, graphId, onResponse) {
1953
+ requestGraphData(requestKey, scenarioSpecL, scenarioSpecR, graphId, onResponse) {
1907
1954
  const request = {
1908
1955
  kind: "graph-data",
1909
- bundle,
1910
- scenario,
1956
+ scenarioSpecL,
1957
+ scenarioSpecR,
1911
1958
  graphId
1912
1959
  };
1913
1960
  this.taskQueue.addTask(requestKey, request, (response) => {
1914
1961
  if (response.kind === "graph-data") {
1915
- onResponse(response.graphData);
1962
+ onResponse(response.graphDataL, response.graphDataR);
1916
1963
  }
1917
1964
  });
1918
1965
  }
@@ -1921,7 +1968,7 @@ var CompareDataCoordinator = class {
1921
1968
  }
1922
1969
  };
1923
1970
 
1924
- // src/compare/compare-datasets.ts
1971
+ // src/comparison/diff-datasets/diff-datasets.ts
1925
1972
  function diffDatasets(datasetL, datasetR) {
1926
1973
  let minValueL = Number.MAX_VALUE;
1927
1974
  let maxValueL = Number.MIN_VALUE;
@@ -2016,19 +2063,9 @@ function diffDatasets(datasetL, datasetR) {
2016
2063
  maxDiffPoint
2017
2064
  };
2018
2065
  }
2019
- function compareDatasets(scenarioKey, datasetKey, datasetMapL, datasetMapR) {
2020
- const datasetL = datasetMapL.get(datasetKey);
2021
- const datasetR = datasetMapR.get(datasetKey);
2022
- const diffReport = diffDatasets(datasetL, datasetR);
2023
- return {
2024
- scenarioKey,
2025
- datasetKey,
2026
- diffReport
2027
- };
2028
- }
2029
2066
 
2030
- // src/compare/compare-graphs.ts
2031
- function diffGraphs(graphL, graphR, scenarioKey, datasetSummaries) {
2067
+ // src/comparison/diff-graphs/diff-graphs.ts
2068
+ function diffGraphs(graphL, graphR, scenarioKey, testSummaries) {
2032
2069
  let inclusion;
2033
2070
  if (graphL && graphR) {
2034
2071
  inclusion = "both";
@@ -2070,8 +2107,8 @@ function diffGraphs(graphL, graphR, scenarioKey, datasetSummaries) {
2070
2107
  datasetKeys.add(dataset.datasetKey);
2071
2108
  }
2072
2109
  for (const datasetKey of datasetKeys) {
2073
- const summary = datasetSummaries.find((summary2) => summary2.d === datasetKey && summary2.s === scenarioKey);
2074
- const maxDiff = summary !== void 0 ? summary.md : void 0;
2110
+ const testSummary = testSummaries.find((summary) => summary.d === datasetKey && summary.s === scenarioKey);
2111
+ const maxDiff = testSummary == null ? void 0 : testSummary.md;
2075
2112
  datasetReports.push({
2076
2113
  datasetKey,
2077
2114
  maxDiff
@@ -2085,12 +2122,12 @@ function diffGraphs(graphL, graphR, scenarioKey, datasetSummaries) {
2085
2122
  };
2086
2123
  }
2087
2124
 
2088
- // src/compare/compare-summary.ts
2089
- function compareSummaryFromReport(compareReport) {
2090
- const datasetSummaries = [];
2091
- for (const r of compareReport.datasetReports) {
2125
+ // src/comparison/report/comparison-reporting.ts
2126
+ function comparisonSummaryFromReport(comparisonReport) {
2127
+ const terseSummaries = [];
2128
+ for (const r of comparisonReport.testReports) {
2092
2129
  if (r.diffReport.validity === "both" && r.diffReport.maxDiff > 0) {
2093
- datasetSummaries.push({
2130
+ terseSummaries.push({
2094
2131
  s: r.scenarioKey,
2095
2132
  d: r.datasetKey,
2096
2133
  md: r.diffReport.maxDiff
@@ -2098,394 +2135,1633 @@ function compareSummaryFromReport(compareReport) {
2098
2135
  }
2099
2136
  }
2100
2137
  return {
2101
- datasetSummaries,
2102
- perfReportL: compareReport.perfReportL,
2103
- perfReportR: compareReport.perfReportR
2138
+ testSummaries: terseSummaries,
2139
+ perfReportL: comparisonReport.perfReportL,
2140
+ perfReportR: comparisonReport.perfReportR
2104
2141
  };
2105
2142
  }
2143
+ function restoreFromTerseSummaries(comparisonConfig, terseSummaries) {
2144
+ const existingSummaries = /* @__PURE__ */ new Map();
2145
+ for (const summary of terseSummaries) {
2146
+ const key = `${summary.s}::${summary.d}`;
2147
+ existingSummaries.set(key, summary);
2148
+ }
2149
+ const allTestSummaries = [];
2150
+ for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {
2151
+ const datasetKeys = comparisonConfig.datasets.getDatasetKeysForScenario(scenario);
2152
+ for (const datasetKey of datasetKeys) {
2153
+ const key = `${scenario.key}::${datasetKey}`;
2154
+ const existingSummary = existingSummaries.get(key);
2155
+ const maxDiff = (existingSummary == null ? void 0 : existingSummary.md) || 0;
2156
+ allTestSummaries.push({
2157
+ s: scenario.key,
2158
+ d: datasetKey,
2159
+ md: maxDiff
2160
+ });
2161
+ }
2162
+ }
2163
+ return allTestSummaries;
2164
+ }
2106
2165
 
2107
- // src/config/synchronized-model.ts
2108
- function synchronizedBundleModel(sourceModel) {
2109
- var _a;
2110
- const promiseQueue = new PromiseQueue();
2166
+ // src/comparison/report/comparison-grouping.ts
2167
+ var import_assert_never8 = require("assert-never");
2168
+
2169
+ // src/comparison/report/buckets.ts
2170
+ function getBucketIndex(diffPct, thresholds) {
2171
+ if (diffPct === 0) {
2172
+ return 0;
2173
+ }
2174
+ for (let i = 0; i < thresholds.length; i++) {
2175
+ if (diffPct < thresholds[i]) {
2176
+ return i + 1;
2177
+ }
2178
+ }
2179
+ return thresholds.length + 1;
2180
+ }
2181
+
2182
+ // src/comparison/report/comparison-grouping.ts
2183
+ function categorizeComparisonTestSummaries(comparisonConfig, terseSummaries) {
2184
+ const allTestSummaries = restoreFromTerseSummaries(comparisonConfig, terseSummaries);
2185
+ const groupsByScenario = groupComparisonTestSummaries(allTestSummaries, "by-scenario");
2186
+ const byScenario = categorizeComparisonGroups(comparisonConfig, [...groupsByScenario.values()]);
2187
+ const groupsByDataset = groupComparisonTestSummaries(allTestSummaries, "by-dataset");
2188
+ const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()]);
2111
2189
  return {
2112
- modelSpec: sourceModel.modelSpec,
2113
- getDatasetsForScenario: (scenario, datasetKeys) => {
2114
- return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenario, datasetKeys));
2115
- },
2116
- getGraphsForDataset: (_a = sourceModel.getGraphsForDataset) == null ? void 0 : _a.bind(sourceModel),
2117
- getGraphDataForScenario: (scenario, graphId) => {
2118
- return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenario, graphId));
2119
- },
2120
- getGraphLinksForScenario: sourceModel.getGraphLinksForScenario.bind(sourceModel)
2190
+ byScenario,
2191
+ byDataset
2121
2192
  };
2122
2193
  }
2123
- var PromiseQueue = class {
2124
- constructor() {
2125
- this.tasks = [];
2126
- this.runningCount = 0;
2194
+ function groupComparisonTestSummaries(testSummaries, groupKind) {
2195
+ const groups = /* @__PURE__ */ new Map();
2196
+ for (const testSummary of testSummaries) {
2197
+ let groupKey;
2198
+ switch (groupKind) {
2199
+ case "by-dataset":
2200
+ groupKey = testSummary.d;
2201
+ break;
2202
+ case "by-scenario":
2203
+ groupKey = testSummary.s;
2204
+ break;
2205
+ default:
2206
+ (0, import_assert_never8.assertNever)(groupKind);
2207
+ }
2208
+ const group = groups.get(groupKey);
2209
+ if (group) {
2210
+ group.testSummaries.push(testSummary);
2211
+ } else {
2212
+ groups.set(groupKey, {
2213
+ kind: groupKind,
2214
+ key: groupKey,
2215
+ testSummaries: [testSummary]
2216
+ });
2217
+ }
2127
2218
  }
2128
- add(f) {
2129
- return new Promise((resolve, reject) => {
2130
- const run = async () => {
2131
- this.runningCount++;
2132
- const promise = f();
2133
- try {
2134
- const result = await promise;
2135
- resolve(result);
2136
- } catch (e) {
2137
- reject(e);
2138
- } finally {
2139
- this.runningCount--;
2140
- this.runNext();
2141
- }
2142
- };
2143
- if (this.runningCount < 1) {
2144
- run();
2219
+ return groups;
2220
+ }
2221
+ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2222
+ const allGroupSummaries = /* @__PURE__ */ new Map();
2223
+ const withErrors = [];
2224
+ const onlyInLeft = [];
2225
+ const onlyInRight = [];
2226
+ let withDiffs = [];
2227
+ const withoutDiffs = [];
2228
+ function addSummaryForGroup(group, root, validInL, validInR) {
2229
+ let scores;
2230
+ if (validInL && validInR) {
2231
+ scores = getScoresForGroup(group, comparisonConfig.thresholds);
2232
+ }
2233
+ const groupSummary = {
2234
+ root,
2235
+ group,
2236
+ scores
2237
+ };
2238
+ allGroupSummaries.set(group.key, groupSummary);
2239
+ if (validInL && validInR) {
2240
+ if (scores.totalDiffCount !== scores.diffCountByBucket[0]) {
2241
+ withDiffs.push(groupSummary);
2145
2242
  } else {
2146
- this.tasks.push(run);
2243
+ withoutDiffs.push(groupSummary);
2147
2244
  }
2148
- });
2245
+ } else if (validInL) {
2246
+ onlyInLeft.push(groupSummary);
2247
+ } else if (validInR) {
2248
+ onlyInRight.push(groupSummary);
2249
+ } else {
2250
+ withErrors.push(groupSummary);
2251
+ }
2149
2252
  }
2150
- runNext() {
2151
- if (this.tasks.length > 0) {
2152
- const task = this.tasks.shift();
2153
- if (task) {
2154
- task();
2253
+ for (const group of allGroups.values()) {
2254
+ switch (group.kind) {
2255
+ case "by-dataset": {
2256
+ const dataset = comparisonConfig.datasets.getDataset(group.key);
2257
+ const validInL = (dataset == null ? void 0 : dataset.outputVarL) !== void 0;
2258
+ const validInR = (dataset == null ? void 0 : dataset.outputVarR) !== void 0;
2259
+ addSummaryForGroup(group, dataset, validInL, validInR);
2260
+ break;
2155
2261
  }
2262
+ case "by-scenario": {
2263
+ const scenario = comparisonConfig.scenarios.getScenario(group.key);
2264
+ const validInL = (scenario == null ? void 0 : scenario.specL) !== void 0;
2265
+ const validInR = (scenario == null ? void 0 : scenario.specR) !== void 0;
2266
+ addSummaryForGroup(group, scenario, validInL, validInR);
2267
+ break;
2268
+ }
2269
+ default:
2270
+ (0, import_assert_never8.assertNever)(group.kind);
2156
2271
  }
2157
2272
  }
2158
- };
2159
-
2160
- // src/config/config.ts
2161
- async function createConfig(options) {
2162
- var _a;
2163
- const origCurrentBundle = await loadSynchronized(options.current);
2164
- let currentBundle;
2165
- let compareConfig;
2166
- if (options.compare === void 0) {
2167
- currentBundle = origCurrentBundle;
2168
- } else {
2169
- const baselineBundle = await loadSynchronized(options.compare.baseline);
2170
- const renamedDatasetKeys = options.compare.datasets.renamedDatasetKeys;
2171
- const invertedRenamedKeys = /* @__PURE__ */ new Map();
2172
- renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
2173
- invertedRenamedKeys.set(newKey, oldKey);
2174
- });
2175
- const rightKeyForLeftKey = (leftKey) => {
2176
- return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
2177
- };
2178
- const leftKeyForRightKey = (rightKey) => {
2179
- return invertedRenamedKeys.get(rightKey) || rightKey;
2180
- };
2181
- const origBundleModelR = origCurrentBundle.model;
2182
- const adjBundleModelR = {
2183
- modelSpec: origBundleModelR.modelSpec,
2184
- getDatasetsForScenario: async (scenario, datasetKeys) => {
2185
- const rightKeys = datasetKeys.map(rightKeyForLeftKey);
2186
- const result = await origBundleModelR.getDatasetsForScenario(scenario, rightKeys);
2187
- const mapWithRightKeys = result.datasetMap;
2188
- const mapWithLeftKeys = /* @__PURE__ */ new Map();
2189
- for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
2190
- const leftKey = leftKeyForRightKey(rightKey);
2191
- mapWithLeftKeys.set(leftKey, dataset);
2192
- }
2193
- return {
2194
- datasetMap: mapWithLeftKeys,
2195
- modelRunTime: result.modelRunTime
2196
- };
2197
- },
2198
- getGraphsForDataset: (_a = origBundleModelR.getGraphsForDataset) == null ? void 0 : _a.bind(origBundleModelR),
2199
- getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
2200
- getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
2201
- };
2202
- currentBundle = __spreadProps(__spreadValues({}, origCurrentBundle), {
2203
- model: adjBundleModelR
2204
- });
2205
- compareConfig = {
2206
- bundleL: baselineBundle,
2207
- bundleR: currentBundle,
2208
- thresholds: options.compare.thresholds,
2209
- scenarios: options.compare.scenarios,
2210
- datasets: options.compare.datasets
2211
- };
2273
+ if (withDiffs.length > 1) {
2274
+ if (withDiffs[0].group.kind === "by-dataset") {
2275
+ withDiffs = sortDatasetGroupSummaries(withDiffs);
2276
+ } else if (withDiffs[0].group.kind === "by-scenario") {
2277
+ withDiffs = sortScenarioGroupSummaries(withDiffs);
2278
+ }
2212
2279
  }
2213
- const checkConfig = {
2214
- bundle: currentBundle,
2215
- tests: options.check.tests
2216
- };
2217
2280
  return {
2218
- check: checkConfig,
2219
- compare: compareConfig
2281
+ allGroupSummaries,
2282
+ withErrors,
2283
+ onlyInLeft,
2284
+ onlyInRight,
2285
+ withDiffs,
2286
+ withoutDiffs
2220
2287
  };
2221
2288
  }
2222
- async function loadSynchronized(sourceBundle) {
2223
- const sourceModel = await sourceBundle.bundle.initModel();
2224
- const synchronizedModel = synchronizedBundleModel(sourceModel);
2289
+ function getScoresForGroup(group, thresholds) {
2290
+ const diffCountByBucket = Array(thresholds.length + 2).fill(0);
2291
+ const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0);
2292
+ let totalDiffCount = 0;
2293
+ for (const testSummary of group.testSummaries) {
2294
+ const bucketIndex = getBucketIndex(testSummary.md, thresholds);
2295
+ diffCountByBucket[bucketIndex]++;
2296
+ totalMaxDiffByBucket[bucketIndex] += testSummary.md;
2297
+ totalDiffCount++;
2298
+ }
2299
+ let diffPercentByBucket;
2300
+ if (totalDiffCount > 0) {
2301
+ diffPercentByBucket = diffCountByBucket.map((count) => count / totalDiffCount * 100);
2302
+ } else {
2303
+ diffPercentByBucket = [];
2304
+ }
2225
2305
  return {
2226
- name: sourceBundle.name,
2227
- version: sourceBundle.bundle.version,
2228
- model: synchronizedModel
2306
+ totalDiffCount,
2307
+ totalMaxDiffByBucket,
2308
+ diffCountByBucket,
2309
+ diffPercentByBucket
2229
2310
  };
2230
2311
  }
2231
-
2232
- // src/config/dataset-manager.ts
2233
- var DatasetManager = class {
2234
- constructor(bundleL, bundleR, renamedDatasetKeys) {
2235
- this.bundleL = bundleL;
2236
- this.bundleR = bundleR;
2237
- this.renamedDatasetKeys = renamedDatasetKeys;
2238
- const invertedRenamedKeys = /* @__PURE__ */ new Map();
2239
- renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
2240
- invertedRenamedKeys.set(newKey, oldKey);
2241
- });
2242
- function leftKeyForRightKey(rightKey) {
2243
- return invertedRenamedKeys.get(rightKey) || rightKey;
2312
+ function sortDatasetGroupSummaries(summaries) {
2313
+ return summaries.sort((a, b) => {
2314
+ var _a, _b;
2315
+ const scoreResult = compareScores(a.scores, b.scores);
2316
+ if (scoreResult !== 0) {
2317
+ return -scoreResult;
2318
+ } else {
2319
+ const aVar = a.root.outputVarL;
2320
+ const bVar = b.root.outputVarR;
2321
+ const aSource = ((_a = aVar.sourceName) == null ? void 0 : _a.toLowerCase()) || "";
2322
+ const bSource = ((_b = bVar.sourceName) == null ? void 0 : _b.toLowerCase()) || "";
2323
+ if (aSource !== bSource) {
2324
+ return aSource.localeCompare(bSource);
2325
+ } else {
2326
+ const aName = aVar.varName.toLowerCase();
2327
+ const bName = bVar.varName.toLowerCase();
2328
+ return aName.localeCompare(bName);
2329
+ }
2244
2330
  }
2245
- const allOutputVarKeysSet = /* @__PURE__ */ new Set();
2246
- const modelOutputVarKeysSet = /* @__PURE__ */ new Set();
2247
- function addOutputVars(outputVars, handleRenames) {
2248
- outputVars.forEach((outputVar, key) => {
2249
- const remappedKey = handleRenames ? leftKeyForRightKey(key) : key;
2250
- allOutputVarKeysSet.add(remappedKey);
2331
+ });
2332
+ }
2333
+ function sortScenarioGroupSummaries(summaries) {
2334
+ return summaries.sort((a, b) => {
2335
+ var _a, _b;
2336
+ const scoreResult = compareScores(a.scores, b.scores);
2337
+ if (scoreResult !== 0) {
2338
+ return -scoreResult;
2339
+ } else {
2340
+ const aScenario = a.root;
2341
+ const bScenario = b.root;
2342
+ const aTitle = aScenario.title.toLowerCase();
2343
+ const bTitle = bScenario.title.toLowerCase();
2344
+ if (aTitle !== bTitle) {
2345
+ return aTitle.localeCompare(bTitle);
2346
+ } else {
2347
+ const aSubtitle = ((_a = aScenario.subtitle) == null ? void 0 : _a.toLowerCase()) || "";
2348
+ const bSubtitle = ((_b = bScenario.subtitle) == null ? void 0 : _b.toLowerCase()) || "";
2349
+ return aSubtitle.localeCompare(bSubtitle);
2350
+ }
2351
+ }
2352
+ });
2353
+ }
2354
+ function compareScores(a, b) {
2355
+ if (a.totalMaxDiffByBucket.length !== b.totalMaxDiffByBucket.length) {
2356
+ return 0;
2357
+ }
2358
+ const len = a.totalMaxDiffByBucket.length;
2359
+ for (let i = len - 1; i >= 0; i--) {
2360
+ const aTotal = a.totalMaxDiffByBucket[i];
2361
+ const bTotal = b.totalMaxDiffByBucket[i];
2362
+ if (aTotal > bTotal) {
2363
+ return 1;
2364
+ } else if (aTotal < bTotal) {
2365
+ return -1;
2366
+ }
2367
+ }
2368
+ return 0;
2369
+ }
2370
+
2371
+ // src/bundle/model-inputs.ts
2372
+ var ModelInputs = class {
2373
+ constructor(modelSpec) {
2374
+ /** All inputs keyed by lookup name (lowercase variable name or alias). */
2375
+ this.inputsByLookupName = /* @__PURE__ */ new Map();
2376
+ /** All input ID aliases. */
2377
+ this.inputIdAliases = /* @__PURE__ */ new Set();
2378
+ for (const inputVar of modelSpec.inputVars.values()) {
2379
+ const varNameKey = inputVar.varName.toLowerCase();
2380
+ this.inputsByLookupName.set(varNameKey, inputVar);
2381
+ if (inputVar.inputId) {
2382
+ const idAlias = `id ${inputVar.inputId}`;
2383
+ this.inputIdAliases.add(idAlias);
2384
+ const idKey = idAlias.toLowerCase();
2385
+ this.inputsByLookupName.set(idKey, inputVar);
2386
+ }
2387
+ }
2388
+ if (modelSpec.inputAliases) {
2389
+ for (const [alias, varId] of modelSpec.inputAliases.entries()) {
2390
+ const aliasKey = alias.toLowerCase();
2391
+ if (this.inputsByLookupName.has(aliasKey)) {
2392
+ console.warn(
2393
+ `WARNING: Input variable already defined with a name that collides with alias '${alias}', skipping`
2394
+ );
2395
+ continue;
2396
+ }
2397
+ const inputVar = modelSpec.inputVars.get(varId);
2398
+ if (inputVar === void 0) {
2399
+ console.warn(`WARNING: No input variable found for '${varId}' associated with alias '${alias}', skipping`);
2400
+ continue;
2401
+ }
2402
+ this.inputsByLookupName.set(aliasKey, inputVar);
2403
+ }
2404
+ }
2405
+ }
2406
+ /**
2407
+ * Return the set of `id XYZ` aliases for all input variables.
2408
+ */
2409
+ getAllInputIdAliases() {
2410
+ return [...this.inputIdAliases];
2411
+ }
2412
+ /**
2413
+ * Return the `InputVar` that matches the requested name (either by variable name or alias).
2414
+ *
2415
+ * @param name The variable name or alias to match.
2416
+ */
2417
+ getInputVarForName(name) {
2418
+ return this.inputsByLookupName.get(name.toLowerCase());
2419
+ }
2420
+ };
2421
+
2422
+ // src/comparison/config/parse/comparison-parser.ts
2423
+ var import_ajv2 = __toESM(require("ajv"), 1);
2424
+ var import_assert_never9 = __toESM(require("assert-never"), 1);
2425
+ var import_neverthrow2 = require("neverthrow");
2426
+ var import_yaml2 = __toESM(require("yaml"), 1);
2427
+
2428
+ // src/comparison/config/parse/comparison.schema.js
2429
+ var comparison_schema_default = {
2430
+ $schema: "http://json-schema.org/draft-07/schema#",
2431
+ title: "Model Comparison Test",
2432
+ type: "array",
2433
+ description: "A group of model comparison scenarios and views.",
2434
+ items: {
2435
+ $ref: "#/$defs/top_level_array_item"
2436
+ },
2437
+ $defs: {
2438
+ //
2439
+ // TOP-LEVEL
2440
+ //
2441
+ top_level_array_item: {
2442
+ oneOf: [
2443
+ { $ref: "#/$defs/scenario_array_item" },
2444
+ { $ref: "#/$defs/scenario_group_array_item" },
2445
+ { $ref: "#/$defs/view_group_array_item" }
2446
+ ]
2447
+ },
2448
+ //
2449
+ // SCENARIOS
2450
+ //
2451
+ scenario_array_item: {
2452
+ type: "object",
2453
+ additionalProperties: false,
2454
+ properties: {
2455
+ scenario: {
2456
+ $ref: "#/$defs/scenario"
2457
+ }
2458
+ },
2459
+ required: ["scenario"]
2460
+ },
2461
+ scenario: {
2462
+ oneOf: [
2463
+ { $ref: "#/$defs/scenario_with_input_at_position" },
2464
+ { $ref: "#/$defs/scenario_with_input_at_value" },
2465
+ { $ref: "#/$defs/scenario_with_multiple_input_settings" },
2466
+ { $ref: "#/$defs/scenario_with_inputs_in_preset_at_position" },
2467
+ // { $ref: '#/$defs/scenario_with_inputs_in_group_at_position' }
2468
+ { $ref: "#/$defs/scenario_preset" }
2469
+ // { $ref: '#/$defs/scenario_expand_for_each_input_in_group' }
2470
+ ]
2471
+ },
2472
+ scenario_position: {
2473
+ type: "string",
2474
+ enum: ["min", "max", "default"]
2475
+ },
2476
+ scenario_with_input_at_position: {
2477
+ type: "object",
2478
+ additionalProperties: false,
2479
+ properties: {
2480
+ id: {
2481
+ type: "string"
2482
+ },
2483
+ title: {
2484
+ type: "string"
2485
+ },
2486
+ subtitle: {
2487
+ type: "string"
2488
+ },
2489
+ with: {
2490
+ type: "string"
2491
+ },
2492
+ at: {
2493
+ $ref: "#/$defs/scenario_position"
2494
+ }
2495
+ },
2496
+ required: ["with", "at"]
2497
+ },
2498
+ scenario_with_input_at_value: {
2499
+ type: "object",
2500
+ additionalProperties: false,
2501
+ properties: {
2502
+ id: {
2503
+ type: "string"
2504
+ },
2505
+ title: {
2506
+ type: "string"
2507
+ },
2508
+ subtitle: {
2509
+ type: "string"
2510
+ },
2511
+ with: {
2512
+ type: "string"
2513
+ },
2514
+ at: {
2515
+ type: "number"
2516
+ }
2517
+ },
2518
+ required: ["with", "at"]
2519
+ },
2520
+ scenario_input_at_position: {
2521
+ type: "object",
2522
+ additionalProperties: false,
2523
+ properties: {
2524
+ input: {
2525
+ type: "string"
2526
+ },
2527
+ at: {
2528
+ $ref: "#/$defs/scenario_position"
2529
+ }
2530
+ },
2531
+ required: ["input", "at"]
2532
+ },
2533
+ scenario_input_at_value: {
2534
+ type: "object",
2535
+ additionalProperties: false,
2536
+ properties: {
2537
+ input: {
2538
+ type: "string"
2539
+ },
2540
+ at: {
2541
+ type: "number"
2542
+ }
2543
+ },
2544
+ required: ["input", "at"]
2545
+ },
2546
+ scenario_input_setting: {
2547
+ oneOf: [{ $ref: "#/$defs/scenario_input_at_position" }, { $ref: "#/$defs/scenario_input_at_value" }]
2548
+ },
2549
+ scenario_input_setting_array: {
2550
+ type: "array",
2551
+ items: {
2552
+ $ref: "#/$defs/scenario_input_setting"
2553
+ },
2554
+ minItems: 1
2555
+ },
2556
+ scenario_with_multiple_input_settings: {
2557
+ type: "object",
2558
+ additionalProperties: false,
2559
+ properties: {
2560
+ id: {
2561
+ type: "string"
2562
+ },
2563
+ title: {
2564
+ type: "string"
2565
+ },
2566
+ subtitle: {
2567
+ type: "string"
2568
+ },
2569
+ with: {
2570
+ $ref: "#/$defs/scenario_input_setting_array"
2571
+ }
2572
+ },
2573
+ required: ["with"]
2574
+ },
2575
+ scenario_with_inputs_in_preset_at_position: {
2576
+ type: "object",
2577
+ additionalProperties: false,
2578
+ properties: {
2579
+ id: {
2580
+ type: "string"
2581
+ },
2582
+ title: {
2583
+ type: "string"
2584
+ },
2585
+ subtitle: {
2586
+ type: "string"
2587
+ },
2588
+ with_inputs: {
2589
+ type: "string",
2590
+ enum: ["all"]
2591
+ },
2592
+ at: {
2593
+ $ref: "#/$defs/scenario_position"
2594
+ }
2595
+ },
2596
+ required: ["with_inputs", "at"]
2597
+ },
2598
+ scenario_preset: {
2599
+ type: "object",
2600
+ additionalProperties: false,
2601
+ properties: {
2602
+ preset: {
2603
+ type: "string",
2604
+ enum: ["matrix"]
2605
+ }
2606
+ },
2607
+ required: ["preset"]
2608
+ },
2609
+ //
2610
+ // SCENARIO GROUPS
2611
+ //
2612
+ scenario_group_array_item: {
2613
+ type: "object",
2614
+ additionalProperties: false,
2615
+ properties: {
2616
+ scenario_group: {
2617
+ $ref: "#/$defs/scenario_group"
2618
+ }
2619
+ },
2620
+ required: ["scenario_group"]
2621
+ },
2622
+ scenario_group: {
2623
+ type: "object",
2624
+ additionalProperties: false,
2625
+ properties: {
2626
+ id: {
2627
+ type: "string"
2628
+ },
2629
+ title: {
2630
+ type: "string"
2631
+ },
2632
+ scenarios: {
2633
+ type: "array",
2634
+ items: {
2635
+ $ref: "#/$defs/scenario_group_scenarios_array_item"
2636
+ },
2637
+ minItems: 1
2638
+ }
2639
+ },
2640
+ required: ["title", "scenarios"]
2641
+ },
2642
+ scenario_group_scenarios_array_item: {
2643
+ oneOf: [{ $ref: "#/$defs/scenario_array_item" }, { $ref: "#/$defs/scenario_ref_array_item" }]
2644
+ },
2645
+ scenario_ref_id: {
2646
+ type: "string"
2647
+ },
2648
+ scenario_ref_object: {
2649
+ type: "object",
2650
+ additionalProperties: false,
2651
+ properties: {
2652
+ id: {
2653
+ type: "string"
2654
+ },
2655
+ title: {
2656
+ type: "string"
2657
+ },
2658
+ subtitle: {
2659
+ type: "string"
2660
+ }
2661
+ },
2662
+ required: ["id"]
2663
+ },
2664
+ scenario_ref: {
2665
+ oneOf: [{ $ref: "#/$defs/scenario_ref_id" }, { $ref: "#/$defs/scenario_ref_object" }]
2666
+ },
2667
+ scenario_ref_array_item: {
2668
+ type: "object",
2669
+ additionalProperties: false,
2670
+ properties: {
2671
+ scenario_ref: {
2672
+ $ref: "#/$defs/scenario_ref"
2673
+ }
2674
+ },
2675
+ required: ["scenario_ref"]
2676
+ },
2677
+ scenario_group_ref: {
2678
+ type: "object",
2679
+ additionalProperties: false,
2680
+ properties: {
2681
+ scenario_group_ref: {
2682
+ type: "string"
2683
+ }
2684
+ },
2685
+ required: ["scenario_group_ref"]
2686
+ },
2687
+ //
2688
+ // VIEWS
2689
+ //
2690
+ view: {
2691
+ type: "object",
2692
+ additionalProperties: false,
2693
+ properties: {
2694
+ title: {
2695
+ type: "string"
2696
+ },
2697
+ subtitle: {
2698
+ type: "string"
2699
+ },
2700
+ scenario_ref: {
2701
+ type: "string"
2702
+ },
2703
+ graphs: {
2704
+ $ref: "#/$defs/view_graphs"
2705
+ }
2706
+ },
2707
+ required: ["scenario_ref", "graphs"]
2708
+ },
2709
+ view_graphs: {
2710
+ oneOf: [{ $ref: "#/$defs/view_graphs_preset" }, { $ref: "#/$defs/view_graphs_array" }]
2711
+ },
2712
+ view_graphs_preset: {
2713
+ type: "string",
2714
+ enum: ["all"]
2715
+ },
2716
+ view_graphs_array: {
2717
+ type: "array",
2718
+ items: {
2719
+ type: "string"
2720
+ },
2721
+ minItems: 1
2722
+ },
2723
+ //
2724
+ // VIEW GROUPS
2725
+ //
2726
+ view_group_array_item: {
2727
+ type: "object",
2728
+ additionalProperties: false,
2729
+ properties: {
2730
+ view_group: {
2731
+ $ref: "#/$defs/view_group"
2732
+ }
2733
+ },
2734
+ required: ["view_group"]
2735
+ },
2736
+ view_group: {
2737
+ oneOf: [
2738
+ { $ref: "#/$defs/view_group_with_views_array" },
2739
+ { $ref: "#/$defs/view_group_shorthand_with_scenarios_array" }
2740
+ ]
2741
+ },
2742
+ view_group_with_views_array: {
2743
+ type: "object",
2744
+ additionalProperties: false,
2745
+ properties: {
2746
+ title: {
2747
+ type: "string"
2748
+ },
2749
+ views: {
2750
+ type: "array",
2751
+ items: {
2752
+ $ref: "#/$defs/view_group_views_array_item"
2753
+ },
2754
+ minItems: 1
2755
+ }
2756
+ },
2757
+ required: ["title", "views"]
2758
+ },
2759
+ view_group_views_array_item: {
2760
+ type: "object",
2761
+ additionalProperties: false,
2762
+ properties: {
2763
+ view: {
2764
+ $ref: "#/$defs/view"
2765
+ }
2766
+ }
2767
+ },
2768
+ view_group_shorthand_with_scenarios_array: {
2769
+ type: "object",
2770
+ additionalProperties: false,
2771
+ properties: {
2772
+ title: {
2773
+ type: "string"
2774
+ },
2775
+ scenarios: {
2776
+ type: "array",
2777
+ items: {
2778
+ $ref: "#/$defs/view_group_scenarios_array_item"
2779
+ },
2780
+ minItems: 1
2781
+ },
2782
+ graphs: {
2783
+ $ref: "#/$defs/view_graphs"
2784
+ }
2785
+ },
2786
+ required: ["title", "scenarios", "graphs"]
2787
+ },
2788
+ view_group_scenarios_array_item: {
2789
+ oneOf: [{ $ref: "#/$defs/scenario_ref_array_item" }, { $ref: "#/$defs/scenario_group_ref" }]
2790
+ }
2791
+ }
2792
+ };
2793
+
2794
+ // src/comparison/config/parse/comparison-parser.ts
2795
+ function parseComparisonSpecs(specSource) {
2796
+ const scenarios = [];
2797
+ const scenarioGroups = [];
2798
+ const viewGroups = [];
2799
+ const ajv = new import_ajv2.default();
2800
+ const validate = ajv.compile(comparison_schema_default);
2801
+ let parsed;
2802
+ switch (specSource.kind) {
2803
+ case "json":
2804
+ parsed = JSON.parse(specSource.content);
2805
+ break;
2806
+ case "yaml":
2807
+ parsed = import_yaml2.default.parse(specSource.content);
2808
+ break;
2809
+ default:
2810
+ (0, import_assert_never9.default)(specSource.kind);
2811
+ }
2812
+ if (validate(parsed)) {
2813
+ for (const specItem of parsed) {
2814
+ if ("scenario" in specItem) {
2815
+ scenarios.push(scenarioSpecFromParsed(specItem.scenario));
2816
+ } else if ("scenario_group" in specItem) {
2817
+ scenarioGroups.push(scenarioGroupSpecFromParsed(specItem.scenario_group));
2818
+ } else if ("view_group" in specItem) {
2819
+ viewGroups.push(viewGroupSpecFromParsed(specItem.view_group));
2820
+ }
2821
+ }
2822
+ } else {
2823
+ let msg = "Failed to parse YAML comparison definitions";
2824
+ for (const error of validate.errors || []) {
2825
+ if (error.message) {
2826
+ msg += `
2827
+ ${error.message}`;
2828
+ }
2829
+ }
2830
+ return (0, import_neverthrow2.err)(new Error(msg));
2831
+ }
2832
+ return (0, import_neverthrow2.ok)({
2833
+ scenarios,
2834
+ scenarioGroups,
2835
+ viewGroups
2836
+ });
2837
+ }
2838
+ function scenarioSpecFromParsed(parsedScenario) {
2839
+ if (parsedScenario.preset === "matrix") {
2840
+ return {
2841
+ kind: "scenario-matrix"
2842
+ };
2843
+ }
2844
+ if (parsedScenario.with !== void 0) {
2845
+ let inputSpecs;
2846
+ if (Array.isArray(parsedScenario.with)) {
2847
+ const parsedInputs = parsedScenario.with;
2848
+ inputSpecs = parsedInputs.map(inputSpecFromParsed);
2849
+ } else {
2850
+ inputSpecs = [
2851
+ inputSpecFromParsed({
2852
+ input: parsedScenario.with,
2853
+ at: parsedScenario.at
2854
+ })
2855
+ ];
2856
+ }
2857
+ return {
2858
+ kind: "scenario-with-inputs",
2859
+ id: parsedScenario.id,
2860
+ title: parsedScenario.title,
2861
+ subtitle: parsedScenario.subtitle,
2862
+ inputs: inputSpecs
2863
+ };
2864
+ }
2865
+ if (parsedScenario.with_inputs === "all") {
2866
+ return {
2867
+ kind: "scenario-with-all-inputs",
2868
+ id: parsedScenario.id,
2869
+ title: parsedScenario.title,
2870
+ subtitle: parsedScenario.subtitle,
2871
+ position: parsedScenario.at
2872
+ };
2873
+ }
2874
+ throw new Error(`Unable to convert parsed scenario: ${JSON.stringify(parsedScenario)}`);
2875
+ }
2876
+ function inputSpecFromParsed(parsedInput) {
2877
+ if (typeof parsedInput.at === "number") {
2878
+ const value = parsedInput.at;
2879
+ return {
2880
+ kind: "input-at-value",
2881
+ inputName: parsedInput.input,
2882
+ value
2883
+ };
2884
+ } else {
2885
+ return {
2886
+ kind: "input-at-position",
2887
+ inputName: parsedInput.input,
2888
+ position: parsedInput.at
2889
+ };
2890
+ }
2891
+ }
2892
+ function scenarioGroupSpecFromParsed(parsedScenarioGroup) {
2893
+ const scenarioSpecs = parsedScenarioGroup.scenarios.map(
2894
+ (parsedScenarioOrRef) => {
2895
+ if ("scenario_ref" in parsedScenarioOrRef) {
2896
+ return scenarioRefSpecFromParsed(parsedScenarioOrRef);
2897
+ } else {
2898
+ return scenarioSpecFromParsed(parsedScenarioOrRef.scenario);
2899
+ }
2900
+ }
2901
+ );
2902
+ return {
2903
+ kind: "scenario-group",
2904
+ id: parsedScenarioGroup.id,
2905
+ title: parsedScenarioGroup.title,
2906
+ scenarios: scenarioSpecs
2907
+ };
2908
+ }
2909
+ function scenarioRefSpecFromParsed(parsedScenarioRef) {
2910
+ if (typeof parsedScenarioRef.scenario_ref === "string") {
2911
+ return {
2912
+ kind: "scenario-ref",
2913
+ scenarioId: parsedScenarioRef.scenario_ref
2914
+ };
2915
+ } else {
2916
+ return {
2917
+ kind: "scenario-ref",
2918
+ scenarioId: parsedScenarioRef.scenario_ref.id,
2919
+ title: parsedScenarioRef.scenario_ref.title,
2920
+ subtitle: parsedScenarioRef.scenario_ref.subtitle
2921
+ };
2922
+ }
2923
+ }
2924
+ function scenarioGroupRefSpecFromParsed(parsedGroupRef) {
2925
+ return {
2926
+ kind: "scenario-group-ref",
2927
+ groupId: parsedGroupRef.scenario_group_ref
2928
+ };
2929
+ }
2930
+ function viewSpecFromParsed(parsedView) {
2931
+ return {
2932
+ kind: "view",
2933
+ title: parsedView.title,
2934
+ subtitle: parsedView.subtitle,
2935
+ scenarioId: parsedView.scenario_ref,
2936
+ graphs: viewGraphsSpecFromParsed(parsedView.graphs)
2937
+ };
2938
+ }
2939
+ function viewGraphsSpecFromParsed(parsedGraphs) {
2940
+ if (parsedGraphs === "all") {
2941
+ return {
2942
+ kind: "graphs-preset",
2943
+ preset: "all"
2944
+ };
2945
+ } else {
2946
+ return {
2947
+ kind: "graphs-array",
2948
+ graphIds: parsedGraphs
2949
+ };
2950
+ }
2951
+ }
2952
+ function viewGroupSpecFromParsed(parsedViewGroup) {
2953
+ if (parsedViewGroup.views !== void 0) {
2954
+ return {
2955
+ kind: "view-group-with-views",
2956
+ title: parsedViewGroup.title,
2957
+ views: parsedViewGroup.views.map((item) => viewSpecFromParsed(item.view))
2958
+ };
2959
+ } else if (parsedViewGroup.scenarios !== void 0) {
2960
+ const scenarios = parsedViewGroup.scenarios.map(
2961
+ (parsedScenarioOrGroupRef) => {
2962
+ if ("scenario_ref" in parsedScenarioOrGroupRef) {
2963
+ return scenarioRefSpecFromParsed(parsedScenarioOrGroupRef);
2964
+ } else if ("scenario_group_ref" in parsedScenarioOrGroupRef) {
2965
+ return scenarioGroupRefSpecFromParsed(parsedScenarioOrGroupRef);
2966
+ } else {
2967
+ throw new Error("Invalid view group");
2968
+ }
2969
+ }
2970
+ );
2971
+ return {
2972
+ kind: "view-group-with-scenarios",
2973
+ title: parsedViewGroup.title,
2974
+ scenarios,
2975
+ graphs: viewGraphsSpecFromParsed(parsedViewGroup.graphs)
2976
+ };
2977
+ } else {
2978
+ throw new Error("Invalid view group");
2979
+ }
2980
+ }
2981
+
2982
+ // src/comparison/config/resolve/comparison-resolver.ts
2983
+ var import_assert_never11 = require("assert-never");
2984
+
2985
+ // src/comparison/config/resolve/comparison-scenario-specs.ts
2986
+ var import_assert_never10 = require("assert-never");
2987
+ function scenarioSpecsFromSettings(settings) {
2988
+ switch (settings.kind) {
2989
+ case "all-inputs-settings": {
2990
+ const scenario = allInputsAtPositionSpec(settings.position);
2991
+ return [scenario, scenario];
2992
+ }
2993
+ case "input-settings": {
2994
+ const specL = scenarioSpecFromInputs(settings.inputs, "left");
2995
+ const specR = scenarioSpecFromInputs(settings.inputs, "right");
2996
+ return [specL, specR];
2997
+ }
2998
+ default:
2999
+ (0, import_assert_never10.assertNever)(settings);
3000
+ }
3001
+ }
3002
+ function scenarioSpecFromInputs(inputs, side) {
3003
+ const settings = [];
3004
+ for (const input of inputs) {
3005
+ const state = side === "left" ? input.stateL : input.stateR;
3006
+ if (state.inputVar === void 0) {
3007
+ return void 0;
3008
+ }
3009
+ settings.push(inputSettingFromResolvedInputState(state));
3010
+ }
3011
+ return inputSettingsSpec(settings);
3012
+ }
3013
+ function inputSettingFromResolvedInputState(state) {
3014
+ const varId = state.inputVar.varId;
3015
+ if (state.position) {
3016
+ return positionSetting(varId, state.position);
3017
+ } else {
3018
+ return valueSetting(varId, state.value);
3019
+ }
3020
+ }
3021
+
3022
+ // src/comparison/config/resolve/comparison-resolver.ts
3023
+ function resolveComparisonSpecs(modelInputsL, modelInputsR, specs) {
3024
+ let key = 1;
3025
+ const genKey = () => {
3026
+ return `${key++}`;
3027
+ };
3028
+ const resolvedScenarios = new ResolvedScenarios();
3029
+ for (const scenarioSpec of specs.scenarios) {
3030
+ resolvedScenarios.add(resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genKey));
3031
+ }
3032
+ const partiallyResolvedScenarioGroups = [];
3033
+ for (const scenarioGroupSpec of specs.scenarioGroups) {
3034
+ const scenariosForGroup = [];
3035
+ for (const scenarioItem of scenarioGroupSpec.scenarios) {
3036
+ if (scenarioItem.kind === "scenario-ref") {
3037
+ scenariosForGroup.push(scenarioItem);
3038
+ } else {
3039
+ const scenarios = resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioItem, genKey);
3040
+ resolvedScenarios.add(scenarios);
3041
+ scenariosForGroup.push(...scenarios);
3042
+ }
3043
+ }
3044
+ partiallyResolvedScenarioGroups.push({
3045
+ spec: scenarioGroupSpec,
3046
+ scenarios: scenariosForGroup
3047
+ });
3048
+ }
3049
+ const resolvedScenarioGroups = new ResolvedScenarioGroups();
3050
+ for (const partiallyResolvedGroup of partiallyResolvedScenarioGroups) {
3051
+ const scenariosForGroup = [];
3052
+ for (const scenarioItem of partiallyResolvedGroup.scenarios) {
3053
+ if (scenarioItem.kind === "scenario-ref") {
3054
+ const referencedScenario = resolvedScenarios.getScenarioForId(scenarioItem.scenarioId);
3055
+ if (referencedScenario) {
3056
+ const resolvedScenario = __spreadValues({}, referencedScenario);
3057
+ if (scenarioItem.title) {
3058
+ resolvedScenario.title = scenarioItem.title;
3059
+ }
3060
+ if (scenarioItem.subtitle) {
3061
+ resolvedScenario.subtitle = scenarioItem.subtitle;
3062
+ }
3063
+ scenariosForGroup.push(resolvedScenario);
3064
+ } else {
3065
+ scenariosForGroup.push({
3066
+ kind: "unresolved-scenario-ref",
3067
+ scenarioId: scenarioItem.scenarioId
3068
+ });
3069
+ }
3070
+ } else {
3071
+ scenariosForGroup.push(scenarioItem);
3072
+ }
3073
+ }
3074
+ resolvedScenarioGroups.add({
3075
+ kind: "scenario-group",
3076
+ id: partiallyResolvedGroup.spec.id,
3077
+ title: partiallyResolvedGroup.spec.title,
3078
+ scenarios: scenariosForGroup
3079
+ });
3080
+ }
3081
+ const resolvedViewGroups = [];
3082
+ for (const viewGroupSpec of specs.viewGroups) {
3083
+ const resolvedViewGroup = resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, viewGroupSpec);
3084
+ resolvedViewGroups.push(resolvedViewGroup);
3085
+ }
3086
+ return {
3087
+ scenarios: resolvedScenarios.getAll(),
3088
+ scenarioGroups: resolvedScenarioGroups.getAll(),
3089
+ viewGroups: resolvedViewGroups
3090
+ };
3091
+ }
3092
+ var ResolvedScenarios = class {
3093
+ constructor() {
3094
+ /** The array of all resolved scenarios. */
3095
+ this.resolvedScenarios = [];
3096
+ /** The set of resolved scenarios that include an ID, keyed by ID. */
3097
+ this.resolvedScenariosById = /* @__PURE__ */ new Map();
3098
+ }
3099
+ add(scenarios) {
3100
+ for (const resolvedScenario of scenarios) {
3101
+ this.resolvedScenarios.push(resolvedScenario);
3102
+ if (resolvedScenario.id !== void 0) {
3103
+ if (this.resolvedScenariosById.has(resolvedScenario.id)) {
3104
+ throw new Error(`Multiple scenarios defined with the same id (${resolvedScenario.id})`);
3105
+ }
3106
+ this.resolvedScenariosById.set(resolvedScenario.id, resolvedScenario);
3107
+ }
3108
+ }
3109
+ }
3110
+ getAll() {
3111
+ return this.resolvedScenarios;
3112
+ }
3113
+ getScenarioForId(id) {
3114
+ return this.resolvedScenariosById.get(id);
3115
+ }
3116
+ };
3117
+ function resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genKey) {
3118
+ switch (scenarioSpec.kind) {
3119
+ case "scenario-matrix":
3120
+ return resolveScenarioMatrix(modelInputsL, modelInputsR, genKey);
3121
+ case "scenario-with-all-inputs": {
3122
+ const position = inputPosition2(scenarioSpec.position);
3123
+ return [
3124
+ resolveScenarioWithAllInputsAtPosition(
3125
+ genKey(),
3126
+ scenarioSpec.id,
3127
+ scenarioSpec.title,
3128
+ scenarioSpec.subtitle,
3129
+ position
3130
+ )
3131
+ ];
3132
+ }
3133
+ case "scenario-with-inputs": {
3134
+ return [
3135
+ resolveScenarioForInputSpecs(
3136
+ modelInputsL,
3137
+ modelInputsR,
3138
+ genKey(),
3139
+ scenarioSpec.id,
3140
+ scenarioSpec.title,
3141
+ scenarioSpec.subtitle,
3142
+ scenarioSpec.inputs
3143
+ )
3144
+ ];
3145
+ }
3146
+ case "scenario-with-distinct-inputs": {
3147
+ return [
3148
+ resolveScenarioForDistinctInputSpecs(
3149
+ modelInputsL,
3150
+ modelInputsR,
3151
+ genKey(),
3152
+ scenarioSpec.id,
3153
+ scenarioSpec.title,
3154
+ scenarioSpec.subtitle,
3155
+ scenarioSpec.inputsL,
3156
+ scenarioSpec.inputsR
3157
+ )
3158
+ ];
3159
+ }
3160
+ default:
3161
+ (0, import_assert_never11.assertNever)(scenarioSpec);
3162
+ }
3163
+ }
3164
+ function resolveScenarioMatrix(modelInputsL, modelInputsR, genKey) {
3165
+ const resolvedScenarios = [];
3166
+ resolvedScenarios.push(
3167
+ resolveScenarioWithAllInputsAtPosition(genKey(), void 0, void 0, void 0, "at-default")
3168
+ );
3169
+ const inputIdAliases = /* @__PURE__ */ new Set();
3170
+ modelInputsL.getAllInputIdAliases().forEach((alias) => inputIdAliases.add(alias));
3171
+ modelInputsR.getAllInputIdAliases().forEach((alias) => inputIdAliases.add(alias));
3172
+ for (const inputIdAlias of inputIdAliases) {
3173
+ const inputAtMin = {
3174
+ kind: "input-at-position",
3175
+ inputName: inputIdAlias,
3176
+ position: "min"
3177
+ };
3178
+ const inputAtMax = {
3179
+ kind: "input-at-position",
3180
+ inputName: inputIdAlias,
3181
+ position: "max"
3182
+ };
3183
+ resolvedScenarios.push(
3184
+ resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), void 0, void 0, void 0, [inputAtMin])
3185
+ );
3186
+ resolvedScenarios.push(
3187
+ resolveScenarioForInputSpecs(modelInputsL, modelInputsR, genKey(), void 0, void 0, void 0, [inputAtMax])
3188
+ );
3189
+ }
3190
+ return resolvedScenarios;
3191
+ }
3192
+ function resolveScenarioWithAllInputsAtPosition(key, id, title, subtitle, position) {
3193
+ const settings = {
3194
+ kind: "all-inputs-settings",
3195
+ position
3196
+ };
3197
+ const [specL, specR] = scenarioSpecsFromSettings(settings);
3198
+ return {
3199
+ kind: "scenario",
3200
+ key,
3201
+ id,
3202
+ title,
3203
+ subtitle,
3204
+ settings,
3205
+ specL,
3206
+ specR
3207
+ };
3208
+ }
3209
+ function resolveScenarioForInputSpecs(modelInputsL, modelInputsR, key, id, title, subtitle, inputSpecs) {
3210
+ const resolvedInputs = inputSpecs.map((inputSpec) => {
3211
+ switch (inputSpec.kind) {
3212
+ case "input-at-position":
3213
+ return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.position);
3214
+ case "input-at-value":
3215
+ return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.value);
3216
+ default:
3217
+ (0, import_assert_never11.assertNever)(inputSpec);
3218
+ }
3219
+ });
3220
+ const settings = {
3221
+ kind: "input-settings",
3222
+ inputs: resolvedInputs
3223
+ };
3224
+ const [specL, specR] = scenarioSpecsFromSettings(settings);
3225
+ return {
3226
+ kind: "scenario",
3227
+ key,
3228
+ id,
3229
+ title,
3230
+ subtitle,
3231
+ settings,
3232
+ specL,
3233
+ specR
3234
+ };
3235
+ }
3236
+ function resolveScenarioForDistinctInputSpecs(modelInputsL, modelInputsR, key, id, title, subtitle, inputSpecsL, inputSpecsR) {
3237
+ const inputsWithErrors = [];
3238
+ const settingsL = [];
3239
+ const settingsR = [];
3240
+ function resolveInputSpec(side, modelInputs, inputSpec) {
3241
+ let inputState;
3242
+ switch (inputSpec.kind) {
3243
+ case "input-at-position":
3244
+ inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.position);
3245
+ break;
3246
+ case "input-at-value":
3247
+ inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.value);
3248
+ break;
3249
+ default:
3250
+ (0, import_assert_never11.assertNever)(inputSpec);
3251
+ }
3252
+ if (inputState.error !== void 0) {
3253
+ inputsWithErrors.push({
3254
+ requestedName: inputSpec.inputName,
3255
+ stateL: side === "left" ? inputState : {},
3256
+ stateR: side === "right" ? inputState : {}
3257
+ });
3258
+ } else {
3259
+ const inputSetting = inputSettingFromResolvedInputState(inputState);
3260
+ if (side === "left") {
3261
+ settingsL.push(inputSetting);
3262
+ } else {
3263
+ settingsR.push(inputSetting);
3264
+ }
3265
+ }
3266
+ }
3267
+ inputSpecsL.forEach((inputSpec) => resolveInputSpec("left", modelInputsL, inputSpec));
3268
+ inputSpecsR.forEach((inputSpec) => resolveInputSpec("right", modelInputsR, inputSpec));
3269
+ let specL;
3270
+ let specR;
3271
+ if (inputsWithErrors.length === 0) {
3272
+ specL = inputSettingsSpec(settingsL);
3273
+ specR = inputSettingsSpec(settingsR);
3274
+ }
3275
+ return {
3276
+ kind: "scenario",
3277
+ key,
3278
+ id,
3279
+ title,
3280
+ subtitle,
3281
+ settings: {
3282
+ kind: "input-settings",
3283
+ inputs: inputsWithErrors
3284
+ },
3285
+ specL,
3286
+ specR
3287
+ };
3288
+ }
3289
+ function resolveInputForName(modelInputsL, modelInputsR, inputName, at) {
3290
+ return {
3291
+ requestedName: inputName,
3292
+ stateL: resolveInputForNameInModel(modelInputsL, inputName, at),
3293
+ stateR: resolveInputForNameInModel(modelInputsR, inputName, at)
3294
+ };
3295
+ }
3296
+ function resolveInputForNameInModel(modelInputs, inputName, at) {
3297
+ const inputVar = modelInputs.getInputVarForName(inputName);
3298
+ if (inputVar) {
3299
+ return resolveInputVar(inputVar, at);
3300
+ } else {
3301
+ return {
3302
+ error: {
3303
+ kind: "unknown-input"
3304
+ }
3305
+ };
3306
+ }
3307
+ }
3308
+ function resolveInputVar(inputVar, at) {
3309
+ if (typeof at === "number") {
3310
+ const value = at;
3311
+ return resolveInputVarAtValue(inputVar, value);
3312
+ } else {
3313
+ const position = inputPosition2(at);
3314
+ return resolveInputVarAtPosition(inputVar, position);
3315
+ }
3316
+ }
3317
+ function resolveInputVarAtPosition(inputVar, position) {
3318
+ return {
3319
+ inputVar,
3320
+ position,
3321
+ value: inputValueAtPosition2(inputVar, position)
3322
+ };
3323
+ }
3324
+ function resolveInputVarAtValue(inputVar, value) {
3325
+ if (value >= inputVar.minValue && value <= inputVar.maxValue) {
3326
+ return {
3327
+ inputVar,
3328
+ value
3329
+ };
3330
+ } else {
3331
+ return {
3332
+ error: {
3333
+ kind: "invalid-value"
3334
+ }
3335
+ };
3336
+ }
3337
+ }
3338
+ function inputPosition2(position) {
3339
+ switch (position) {
3340
+ case "default":
3341
+ return "at-default";
3342
+ case "min":
3343
+ return "at-minimum";
3344
+ case "max":
3345
+ return "at-maximum";
3346
+ default:
3347
+ return void 0;
3348
+ }
3349
+ }
3350
+ function inputValueAtPosition2(inputVar, position) {
3351
+ switch (position) {
3352
+ case "at-default":
3353
+ return inputVar.defaultValue;
3354
+ case "at-minimum":
3355
+ return inputVar.minValue;
3356
+ case "at-maximum":
3357
+ return inputVar.maxValue;
3358
+ default:
3359
+ (0, import_assert_never11.assertNever)(position);
3360
+ }
3361
+ }
3362
+ var ResolvedScenarioGroups = class {
3363
+ constructor() {
3364
+ /** The array of all resolved scenario groups. */
3365
+ this.resolvedGroups = [];
3366
+ /** The set of resolved scenario groups that include an ID, keyed by ID. */
3367
+ this.resolvedGroupsById = /* @__PURE__ */ new Map();
3368
+ }
3369
+ add(group) {
3370
+ this.resolvedGroups.push(group);
3371
+ if (group.id !== void 0) {
3372
+ if (this.resolvedGroupsById.has(group.id)) {
3373
+ throw new Error(`Multiple scenario groups defined with the same id (${group.id})`);
3374
+ }
3375
+ this.resolvedGroupsById.set(group.id, group);
3376
+ }
3377
+ }
3378
+ getAll() {
3379
+ return this.resolvedGroups;
3380
+ }
3381
+ getGroupForId(id) {
3382
+ return this.resolvedGroupsById.get(id);
3383
+ }
3384
+ };
3385
+ function resolveGraphsFromSpec(graphsSpec) {
3386
+ switch (graphsSpec.kind) {
3387
+ case "graphs-preset":
3388
+ return "all";
3389
+ case "graphs-array":
3390
+ return graphsSpec.graphIds;
3391
+ default:
3392
+ (0, import_assert_never11.assertNever)(graphsSpec);
3393
+ }
3394
+ }
3395
+ function resolveViewForScenarioId(resolvedScenarios, viewTitle, viewSubtitle, scenarioId, graphs) {
3396
+ const resolvedScenario = resolvedScenarios.getScenarioForId(scenarioId);
3397
+ if (resolvedScenario) {
3398
+ return resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphs);
3399
+ } else {
3400
+ return unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId);
3401
+ }
3402
+ }
3403
+ function resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphs) {
3404
+ const resolvedScenario = resolvedScenarios.getScenarioForId(refSpec.scenarioId);
3405
+ if (resolvedScenario) {
3406
+ return resolveViewForScenario(refSpec.title, refSpec.subtitle, resolvedScenario, graphs);
3407
+ } else {
3408
+ return unresolvedViewForScenarioId(void 0, void 0, refSpec.scenarioId);
3409
+ }
3410
+ }
3411
+ function resolveViewForScenario(viewTitle, viewSubtitle, resolvedScenario, graphs) {
3412
+ if (viewTitle === void 0) {
3413
+ viewTitle = resolvedScenario.title;
3414
+ if (viewTitle === void 0) {
3415
+ viewTitle = "Untitled view";
3416
+ }
3417
+ if (viewSubtitle === void 0) {
3418
+ viewSubtitle = resolvedScenario.subtitle;
3419
+ }
3420
+ }
3421
+ return {
3422
+ kind: "view",
3423
+ title: viewTitle,
3424
+ subtitle: viewSubtitle,
3425
+ scenario: resolvedScenario,
3426
+ graphs
3427
+ };
3428
+ }
3429
+ function unresolvedViewForScenarioId(viewTitle, viewSubtitle, scenarioId) {
3430
+ return {
3431
+ kind: "unresolved-view",
3432
+ title: viewTitle,
3433
+ subtitle: viewSubtitle,
3434
+ scenarioId
3435
+ };
3436
+ }
3437
+ function unresolvedViewForScenarioGroupId(viewTitle, viewSubtitle, scenarioGroupId) {
3438
+ return {
3439
+ kind: "unresolved-view",
3440
+ title: viewTitle,
3441
+ subtitle: viewSubtitle,
3442
+ scenarioGroupId
3443
+ };
3444
+ }
3445
+ function resolveViewGroupFromSpec(resolvedScenarios, resolvedScenarioGroups, viewGroupSpec) {
3446
+ let views;
3447
+ switch (viewGroupSpec.kind) {
3448
+ case "view-group-with-views": {
3449
+ views = viewGroupSpec.views.map((viewSpec) => {
3450
+ const graphs = resolveGraphsFromSpec(viewSpec.graphs);
3451
+ return resolveViewForScenarioId(
3452
+ resolvedScenarios,
3453
+ viewSpec.title,
3454
+ viewSpec.subtitle,
3455
+ viewSpec.scenarioId,
3456
+ graphs
3457
+ );
3458
+ });
3459
+ break;
3460
+ }
3461
+ case "view-group-with-scenarios": {
3462
+ const graphs = resolveGraphsFromSpec(viewGroupSpec.graphs);
3463
+ views = [];
3464
+ for (const refSpec of viewGroupSpec.scenarios) {
3465
+ switch (refSpec.kind) {
3466
+ case "scenario-ref":
3467
+ views.push(resolveViewForScenarioRefSpec(resolvedScenarios, refSpec, graphs));
3468
+ break;
3469
+ case "scenario-group-ref": {
3470
+ const resolvedGroup = resolvedScenarioGroups.getGroupForId(refSpec.groupId);
3471
+ if (resolvedGroup) {
3472
+ for (const scenario of resolvedGroup.scenarios) {
3473
+ switch (scenario.kind) {
3474
+ case "unresolved-scenario-ref":
3475
+ views.push(unresolvedViewForScenarioId(void 0, void 0, scenario.scenarioId));
3476
+ break;
3477
+ case "scenario":
3478
+ views.push(resolveViewForScenario(void 0, void 0, scenario, graphs));
3479
+ break;
3480
+ default:
3481
+ (0, import_assert_never11.assertNever)(scenario);
3482
+ }
3483
+ }
3484
+ } else {
3485
+ views.push(unresolvedViewForScenarioGroupId(void 0, void 0, refSpec.groupId));
3486
+ }
3487
+ break;
3488
+ }
3489
+ default:
3490
+ (0, import_assert_never11.assertNever)(refSpec);
3491
+ }
3492
+ }
3493
+ break;
3494
+ }
3495
+ default:
3496
+ (0, import_assert_never11.assertNever)(viewGroupSpec);
3497
+ }
3498
+ return {
3499
+ kind: "view-group",
3500
+ title: viewGroupSpec.title,
3501
+ views
3502
+ };
3503
+ }
3504
+
3505
+ // src/comparison/config/comparison-config.ts
3506
+ function resolveComparisonSpecsFromSources(modelInputsL, modelInputsR, specSources) {
3507
+ const combinedSpecs = {
3508
+ scenarios: [],
3509
+ scenarioGroups: [],
3510
+ viewGroups: []
3511
+ };
3512
+ for (const specSource of specSources) {
3513
+ let specs;
3514
+ if ("kind" in specSource) {
3515
+ const parseResult = parseComparisonSpecs(specSource);
3516
+ if (parseResult.isOk()) {
3517
+ specs = parseResult.value;
3518
+ } else {
3519
+ const filenamePart = specSource.filename ? ` in ${specSource.filename}` : "";
3520
+ console.error(`ERROR: Failed to parse comparison spec${filenamePart}, skipping`);
3521
+ continue;
3522
+ }
3523
+ } else {
3524
+ specs = specSource;
3525
+ }
3526
+ combinedSpecs.scenarios.push(...specs.scenarios);
3527
+ combinedSpecs.scenarioGroups.push(...specs.scenarioGroups);
3528
+ combinedSpecs.viewGroups.push(...specs.viewGroups);
3529
+ }
3530
+ return resolveComparisonSpecs(modelInputsL, modelInputsR, combinedSpecs);
3531
+ }
3532
+
3533
+ // src/comparison/config/comparison-datasets.ts
3534
+ function getComparisonDatasets(modelSpecL, modelSpecR, datasetOptions) {
3535
+ return new ComparisonDatasetsImpl(modelSpecL, modelSpecR, datasetOptions);
3536
+ }
3537
+ var ComparisonDatasetsImpl = class {
3538
+ /**
3539
+ * @param modelSpecL The model spec for the "left" bundle being compared.
3540
+ * @param modelSpecR The model spec for the "right" bundle being compared.
3541
+ * @param datasetOptions The custom configuration for the datasets to be compared.
3542
+ */
3543
+ constructor(modelSpecL, modelSpecR, datasetOptions) {
3544
+ this.datasetOptions = datasetOptions;
3545
+ const renamedDatasetKeys = datasetOptions == null ? void 0 : datasetOptions.renamedDatasetKeys;
3546
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
3547
+ renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
3548
+ invertedRenamedKeys.set(newKey, oldKey);
3549
+ });
3550
+ function leftKeyForRightKey(rightKey) {
3551
+ return invertedRenamedKeys.get(rightKey) || rightKey;
3552
+ }
3553
+ const allOutputVarKeysSet = /* @__PURE__ */ new Set();
3554
+ const modelOutputVarKeysSet = /* @__PURE__ */ new Set();
3555
+ function addOutputVars(outputVars, handleRenames) {
3556
+ outputVars.forEach((outputVar, key) => {
3557
+ const remappedKey = handleRenames ? leftKeyForRightKey(key) : key;
3558
+ allOutputVarKeysSet.add(remappedKey);
2251
3559
  if (outputVar.sourceName === void 0) {
2252
3560
  modelOutputVarKeysSet.add(remappedKey);
2253
3561
  }
2254
3562
  });
2255
3563
  }
2256
- addOutputVars(bundleL.modelSpec.outputVars, false);
2257
- addOutputVars(bundleR.modelSpec.outputVars, true);
3564
+ addOutputVars(modelSpecL.outputVars, false);
3565
+ addOutputVars(modelSpecR.outputVars, true);
2258
3566
  this.allOutputVarKeys = Array.from(allOutputVarKeysSet);
2259
3567
  this.modelOutputVarKeys = Array.from(modelOutputVarKeysSet);
2260
- }
2261
- getDatasetKeysForScenario(scenario) {
2262
- if (scenario.kind === "all-inputs" && scenario.position === "at-default") {
2263
- return this.allOutputVarKeys;
2264
- } else {
2265
- return this.modelOutputVarKeys;
3568
+ this.allDatasets = /* @__PURE__ */ new Map();
3569
+ for (const datasetKeyL of this.allOutputVarKeys) {
3570
+ const datasetKeyR = (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(datasetKeyL)) || datasetKeyL;
3571
+ const outputVarL = modelSpecL.outputVars.get(datasetKeyL);
3572
+ const outputVarR = modelSpecR.outputVars.get(datasetKeyR);
3573
+ this.allDatasets.set(datasetKeyL, {
3574
+ kind: "dataset",
3575
+ key: datasetKeyL,
3576
+ outputVarL,
3577
+ outputVarR
3578
+ });
2266
3579
  }
2267
3580
  }
2268
- getDatasetInfo(datasetKey) {
3581
+ // from ComparisonDatasets interface
3582
+ getAllDatasets() {
3583
+ return this.allDatasets.values();
3584
+ }
3585
+ // from ComparisonDatasets interface
3586
+ getDataset(datasetKey) {
3587
+ return this.allDatasets.get(datasetKey);
3588
+ }
3589
+ // from ComparisonDatasets interface
3590
+ getDatasetKeysForScenario(scenario) {
2269
3591
  var _a;
2270
- const modelSpecL = this.bundleL.modelSpec;
2271
- const modelSpecR = this.bundleR.modelSpec;
2272
- const datasetKeyL = datasetKey;
2273
- const datasetKeyR = ((_a = this.renamedDatasetKeys) == null ? void 0 : _a.get(datasetKeyL)) || datasetKeyL;
2274
- const outputVarL = modelSpecL.outputVars.get(datasetKeyL);
2275
- const outputVarR = modelSpecR.outputVars.get(datasetKeyR);
2276
- let varName;
2277
- let newVarName;
2278
- let sourceName;
2279
- let newSourceName;
2280
- if (outputVarL && outputVarR && outputVarL.varName != outputVarR.varName) {
2281
- varName = outputVarL.varName;
2282
- newVarName = outputVarR.varName;
2283
- } else {
2284
- const outputVar = outputVarR || outputVarL;
2285
- varName = (outputVar == null ? void 0 : outputVar.varName) || "Unknown";
2286
- }
2287
- if (outputVarL && outputVarR && outputVarL.sourceName != outputVarR.sourceName) {
2288
- sourceName = outputVarL.sourceName;
2289
- newSourceName = outputVarR.sourceName;
3592
+ if (((_a = this.datasetOptions) == null ? void 0 : _a.datasetKeysForScenario) !== void 0) {
3593
+ return this.datasetOptions.datasetKeysForScenario(this.allOutputVarKeys, scenario);
2290
3594
  } else {
2291
- const outputVar = outputVarR || outputVarL;
2292
- sourceName = outputVar == null ? void 0 : outputVar.sourceName;
3595
+ if (scenario.settings.kind === "all-inputs-settings" && scenario.settings.position === "at-default") {
3596
+ return this.allOutputVarKeys;
3597
+ } else {
3598
+ return this.modelOutputVarKeys;
3599
+ }
2293
3600
  }
2294
- return {
2295
- varName,
2296
- newVarName,
2297
- sourceName,
2298
- newSourceName,
2299
- relatedItems: (outputVarR == null ? void 0 : outputVarR.relatedItems) || []
2300
- };
2301
3601
  }
2302
3602
  };
2303
3603
 
2304
- // src/config/scenario-manager.ts
2305
- var import_assert_never8 = require("assert-never");
2306
- var ScenarioManager = class {
2307
- constructor(bundleL, bundleR) {
2308
- this.bundleL = bundleL;
2309
- this.bundleR = bundleR;
2310
- this.scenarios = /* @__PURE__ */ new Map();
2311
- this.scenarioInfo = /* @__PURE__ */ new Map();
2312
- this.defaultInfoForGroup = /* @__PURE__ */ new Map();
2313
- this.groupInfo = /* @__PURE__ */ new Map();
2314
- }
2315
- getScenarios() {
2316
- return [...this.scenarios.values()];
2317
- }
2318
- getScenario(scenarioKey) {
2319
- return this.scenarios.get(scenarioKey);
2320
- }
2321
- getScenarioGroupInfo(groupKey) {
2322
- return this.groupInfo.get(groupKey);
2323
- }
2324
- getScenarioInfo(scenario, groupKey) {
2325
- if (scenario.key === "all_inputs_at_default") {
2326
- const defaultInfo = this.defaultInfoForGroup.get(groupKey);
2327
- if (defaultInfo) {
2328
- return defaultInfo;
2329
- }
2330
- }
2331
- return this.scenarioInfo.get(scenario.key);
2332
- }
2333
- setDefaultScenarioInfoForGroup(groupKey, scenarioInfo) {
2334
- this.defaultInfoForGroup.set(groupKey, scenarioInfo);
2335
- }
2336
- addScenario(scenario, scenarioInfo, groupInfo) {
2337
- this.scenarios.set(scenario.key, scenario);
2338
- if (!scenarioInfo) {
2339
- scenarioInfo = this.getInfoForScenario(scenario);
2340
- }
2341
- this.scenarioInfo.set(scenario.key, scenarioInfo);
2342
- if (!groupInfo) {
2343
- groupInfo = this.getGroupInfoForScenario(scenario);
2344
- }
2345
- this.groupInfo.set(scenario.groupKey, groupInfo);
2346
- }
2347
- addScenarioMatrix() {
2348
- const inputVarIdsL = Array.from(this.bundleL.modelSpec.inputVars.keys());
2349
- const inputVarIdsR = Array.from(this.bundleR.modelSpec.inputVars.keys());
2350
- const inputVarIds = /* @__PURE__ */ new Set([...inputVarIdsL, ...inputVarIdsR]);
2351
- this.addScenario(allInputsAtPositionScenario("at-default"));
2352
- this.addScenario(allInputsAtPositionScenario("at-minimum"));
2353
- this.addScenario(allInputsAtPositionScenario("at-maximum"));
2354
- for (const inputVarId of inputVarIds) {
2355
- this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, "at-minimum"));
2356
- this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, "at-maximum"));
2357
- }
2358
- }
2359
- getGroupInfoForScenario(scenario) {
2360
- switch (scenario.kind) {
2361
- case "all-inputs":
2362
- return {
2363
- title: "All Inputs",
2364
- relatedItems: []
2365
- };
2366
- case "settings":
2367
- if (scenario.settings.length === 1) {
2368
- const inputVar = this.getInputVarForSetting(scenario.settings[0]);
2369
- let relatedItems;
2370
- let subtitle;
2371
- if (inputVar == null ? void 0 : inputVar.relatedItem) {
2372
- relatedItems = [inputVar.relatedItem];
2373
- subtitle = inputVar.relatedItem.locationPath.join('&nbsp;<span class="related-sep">&gt;</span>&nbsp;');
2374
- } else {
2375
- relatedItems = [];
2376
- subtitle = void 0;
2377
- }
2378
- return {
2379
- title: (inputVar == null ? void 0 : inputVar.varName) || "Unknown Input",
2380
- subtitle,
2381
- relatedItems
2382
- };
2383
- } else {
2384
- return {
2385
- title: "Multiple Inputs",
2386
- relatedItems: this.getRelatedItemsForSettings(scenario.settings)
2387
- };
2388
- }
2389
- default:
2390
- (0, import_assert_never8.assertNever)(scenario);
3604
+ // src/comparison/config/comparison-scenarios.ts
3605
+ function getComparisonScenarios(scenarios) {
3606
+ return new ComparisonScenariosImpl(scenarios);
3607
+ }
3608
+ var ComparisonScenariosImpl = class {
3609
+ constructor(scenarios) {
3610
+ this.scenarioDefs = /* @__PURE__ */ new Map();
3611
+ for (const scenario of scenarios) {
3612
+ this.scenarioDefs.set(scenario.key, scenario);
2391
3613
  }
2392
3614
  }
2393
- getInfoForScenario(scenario) {
2394
- switch (scenario.kind) {
2395
- case "all-inputs":
2396
- return this.getInfoForPositionSetting(void 0, scenario.position);
2397
- case "settings":
2398
- if (scenario.settings.length === 1 && scenario.settings[0].kind === "position") {
2399
- const setting = scenario.settings[0];
2400
- return this.getInfoForPositionSetting(setting.inputVarId, setting.position);
2401
- } else {
2402
- return {
2403
- title: `PLACEHOLDER (scenario info not provided)`,
2404
- position: 1
2405
- };
2406
- }
2407
- default:
2408
- (0, import_assert_never8.assertNever)(scenario);
2409
- }
3615
+ /**
3616
+ * Return all `ComparisonScenario` instances that are available for comparisons.
3617
+ */
3618
+ getAllScenarios() {
3619
+ return this.scenarioDefs.values();
2410
3620
  }
2411
- getInfoForPositionSetting(inputVarId, inputPosition2) {
2412
- const title = inputPosition2.replace("-", " ");
2413
- let subtitle;
2414
- if (inputVarId) {
2415
- const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId);
2416
- const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId);
2417
- const valueL = inputValue(inputVarL, inputPosition2);
2418
- const valueR = inputValue(inputVarR, inputPosition2);
2419
- if (valueL !== valueR) {
2420
- let values = "";
2421
- values += "(";
2422
- values += `<span class='dataset-color-0'>${valueL}</span>`;
2423
- values += "&nbsp;|&nbsp;";
2424
- values += `<span class='dataset-color-1'>${valueR}</span>`;
2425
- values += ")";
2426
- subtitle = values;
3621
+ /**
3622
+ * Return the scenario definition for the given key.
3623
+ *
3624
+ * @param key The key for the scenario.
3625
+ */
3626
+ getScenario(key) {
3627
+ return this.scenarioDefs.get(key);
3628
+ }
3629
+ };
3630
+
3631
+ // src/config/synchronized-model.ts
3632
+ function synchronizedBundleModel(sourceModel) {
3633
+ const promiseQueue = new PromiseQueue();
3634
+ return {
3635
+ modelSpec: sourceModel.modelSpec,
3636
+ getDatasetsForScenario: (scenarioSpec, datasetKeys) => {
3637
+ return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenarioSpec, datasetKeys));
3638
+ },
3639
+ getGraphDataForScenario: (scenarioSpec, graphId) => {
3640
+ return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenarioSpec, graphId));
3641
+ },
3642
+ getGraphLinksForScenario: sourceModel.getGraphLinksForScenario.bind(sourceModel)
3643
+ };
3644
+ }
3645
+ var PromiseQueue = class {
3646
+ constructor() {
3647
+ this.tasks = [];
3648
+ this.runningCount = 0;
3649
+ }
3650
+ add(f) {
3651
+ return new Promise((resolve, reject) => {
3652
+ const run = () => __async(this, null, function* () {
3653
+ this.runningCount++;
3654
+ const promise = f();
3655
+ try {
3656
+ const result = yield promise;
3657
+ resolve(result);
3658
+ } catch (e) {
3659
+ reject(e);
3660
+ } finally {
3661
+ this.runningCount--;
3662
+ this.runNext();
3663
+ }
3664
+ });
3665
+ if (this.runningCount < 1) {
3666
+ run();
2427
3667
  } else {
2428
- subtitle = `(${valueL})`;
3668
+ this.tasks.push(run);
2429
3669
  }
2430
- } else {
2431
- subtitle = void 0;
2432
- }
2433
- let position;
2434
- switch (inputPosition2) {
2435
- case "at-default":
2436
- position = 0;
2437
- break;
2438
- case "at-minimum":
2439
- position = 1;
2440
- break;
2441
- case "at-maximum":
2442
- position = 2;
2443
- break;
2444
- default:
2445
- (0, import_assert_never8.assertNever)(inputPosition2);
2446
- }
2447
- return {
2448
- title,
2449
- subtitle,
2450
- position
2451
- };
3670
+ });
2452
3671
  }
2453
- getRelatedItemsForSettings(settings) {
2454
- const relatedItems = [];
2455
- for (const setting of settings) {
2456
- const inputVar = this.getInputVarForSetting(setting);
2457
- if (inputVar == null ? void 0 : inputVar.relatedItem) {
2458
- relatedItems.push(inputVar.relatedItem);
3672
+ runNext() {
3673
+ if (this.tasks.length > 0) {
3674
+ const task = this.tasks.shift();
3675
+ if (task) {
3676
+ task();
2459
3677
  }
2460
3678
  }
2461
- return relatedItems;
2462
- }
2463
- getInputVarForSetting(setting) {
2464
- const inputVarId = setting.inputVarId;
2465
- const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId);
2466
- const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId);
2467
- return inputVarR || inputVarL;
2468
3679
  }
2469
3680
  };
2470
- function inputValue(inputVar, position) {
2471
- if (inputVar) {
2472
- switch (position) {
2473
- case "at-default":
2474
- return inputVar.defaultValue.toString();
2475
- case "at-minimum":
2476
- return inputVar.minValue.toString();
2477
- case "at-maximum":
2478
- return inputVar.maxValue.toString();
2479
- default:
2480
- (0, import_assert_never8.assertNever)(position);
3681
+
3682
+ // src/config/config.ts
3683
+ function createConfig(options) {
3684
+ return __async(this, null, function* () {
3685
+ var _a;
3686
+ const origCurrentBundle = yield loadSynchronized(options.current);
3687
+ let currentBundle;
3688
+ let comparisonConfig;
3689
+ if (options.comparison === void 0) {
3690
+ currentBundle = origCurrentBundle;
3691
+ } else {
3692
+ const baselineBundle = yield loadSynchronized(options.comparison.baseline);
3693
+ const renamedDatasetKeys = (_a = options.comparison.datasets) == null ? void 0 : _a.renamedDatasetKeys;
3694
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
3695
+ renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
3696
+ invertedRenamedKeys.set(newKey, oldKey);
3697
+ });
3698
+ const rightKeyForLeftKey = (leftKey) => {
3699
+ return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
3700
+ };
3701
+ const leftKeyForRightKey = (rightKey) => {
3702
+ return invertedRenamedKeys.get(rightKey) || rightKey;
3703
+ };
3704
+ const origBundleModelR = origCurrentBundle.model;
3705
+ const adjBundleModelR = {
3706
+ modelSpec: origBundleModelR.modelSpec,
3707
+ getDatasetsForScenario: (scenarioSpec, datasetKeys) => __async(this, null, function* () {
3708
+ const rightKeys = datasetKeys.map(rightKeyForLeftKey);
3709
+ const result = yield origBundleModelR.getDatasetsForScenario(scenarioSpec, rightKeys);
3710
+ const mapWithRightKeys = result.datasetMap;
3711
+ const mapWithLeftKeys = /* @__PURE__ */ new Map();
3712
+ for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
3713
+ const leftKey = leftKeyForRightKey(rightKey);
3714
+ mapWithLeftKeys.set(leftKey, dataset);
3715
+ }
3716
+ return {
3717
+ datasetMap: mapWithLeftKeys,
3718
+ modelRunTime: result.modelRunTime
3719
+ };
3720
+ }),
3721
+ getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
3722
+ getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
3723
+ };
3724
+ currentBundle = __spreadProps(__spreadValues({}, origCurrentBundle), {
3725
+ model: adjBundleModelR
3726
+ });
3727
+ const modelSpecL = baselineBundle.model.modelSpec;
3728
+ const modelSpecR = currentBundle.model.modelSpec;
3729
+ const modelInputsL = new ModelInputs(modelSpecL);
3730
+ const modelInputsR = new ModelInputs(modelSpecR);
3731
+ const comparisonDefs = resolveComparisonSpecsFromSources(modelInputsL, modelInputsR, options.comparison.specs);
3732
+ comparisonConfig = {
3733
+ bundleL: baselineBundle,
3734
+ bundleR: currentBundle,
3735
+ thresholds: options.comparison.thresholds,
3736
+ scenarios: getComparisonScenarios(comparisonDefs.scenarios),
3737
+ datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),
3738
+ viewGroups: comparisonDefs.viewGroups
3739
+ };
2481
3740
  }
2482
- } else {
2483
- return "n/a";
2484
- }
3741
+ const checkConfig = {
3742
+ bundle: currentBundle,
3743
+ tests: options.check.tests
3744
+ };
3745
+ return {
3746
+ check: checkConfig,
3747
+ comparison: comparisonConfig
3748
+ };
3749
+ });
3750
+ }
3751
+ function loadSynchronized(sourceBundle) {
3752
+ return __async(this, null, function* () {
3753
+ const sourceModel = yield sourceBundle.bundle.initModel();
3754
+ const synchronizedModel = synchronizedBundleModel(sourceModel);
3755
+ return {
3756
+ name: sourceBundle.name,
3757
+ version: sourceBundle.bundle.version,
3758
+ model: synchronizedModel
3759
+ };
3760
+ });
2485
3761
  }
2486
3762
 
2487
3763
  // src/perf/perf-runner.ts
2488
- var import_assert_never9 = require("assert-never");
3764
+ var import_assert_never12 = require("assert-never");
2489
3765
 
2490
3766
  // src/perf/perf-stats.ts
2491
3767
  var PerfStats = class {
@@ -2529,26 +3805,26 @@ var PerfRunner = class {
2529
3805
  this.bundleModelL = bundleModelL;
2530
3806
  this.bundleModelR = bundleModelR;
2531
3807
  this.mode = mode;
2532
- const scenario = allInputsAtPositionScenario("at-default");
3808
+ const scenarioSpec = allInputsAtPositionSpec("at-default");
2533
3809
  this.taskQueue = new TaskQueue({
2534
- process: async (request) => {
3810
+ process: (request) => __async(this, null, function* () {
2535
3811
  switch (request.kind) {
2536
3812
  case "left": {
2537
- const result = await bundleModelL.getDatasetsForScenario(scenario, []);
3813
+ const result = yield bundleModelL.getDatasetsForScenario(scenarioSpec, []);
2538
3814
  return {
2539
3815
  runTimeL: result.modelRunTime
2540
3816
  };
2541
3817
  }
2542
3818
  case "right": {
2543
- const result = await bundleModelR.getDatasetsForScenario(scenario, []);
3819
+ const result = yield bundleModelR.getDatasetsForScenario(scenarioSpec, []);
2544
3820
  return {
2545
3821
  runTimeR: result.modelRunTime
2546
3822
  };
2547
3823
  }
2548
3824
  case "both": {
2549
- const [resultL, resultR] = await Promise.all([
2550
- bundleModelL.getDatasetsForScenario(scenario, []),
2551
- bundleModelR.getDatasetsForScenario(scenario, [])
3825
+ const [resultL, resultR] = yield Promise.all([
3826
+ bundleModelL.getDatasetsForScenario(scenarioSpec, []),
3827
+ bundleModelR.getDatasetsForScenario(scenarioSpec, [])
2552
3828
  ]);
2553
3829
  return {
2554
3830
  runTimeL: resultL.modelRunTime,
@@ -2556,9 +3832,9 @@ var PerfRunner = class {
2556
3832
  };
2557
3833
  }
2558
3834
  default:
2559
- (0, import_assert_never9.assertNever)(request.kind);
3835
+ (0, import_assert_never12.assertNever)(request.kind);
2560
3836
  }
2561
- }
3837
+ })
2562
3838
  });
2563
3839
  }
2564
3840
  start() {
@@ -2604,46 +3880,164 @@ var PerfRunner = class {
2604
3880
  }
2605
3881
  };
2606
3882
 
2607
- // src/suite/suite-runner.ts
2608
- var import_assert_never10 = __toESM(require("assert-never"), 1);
2609
-
2610
3883
  // src/data/data-planner.ts
2611
- var ScenarioTaskSet = class {
2612
- constructor(scenario) {
2613
- this.scenario = scenario;
2614
- this.modelTasks = /* @__PURE__ */ new Map();
2615
- this.modelImplTasks = /* @__PURE__ */ new Map();
2616
- this.requestKind = "check";
3884
+ var DataPlanner = class {
3885
+ /**
3886
+ * @param batchSize The maximum number of impl vars that can be fetched
3887
+ * with a single request; this is usually the same as the number of
3888
+ * normal model outputs.
3889
+ */
3890
+ constructor(batchSize) {
3891
+ this.batchSize = batchSize;
3892
+ /** Tasks that need to access both "left" and "right" models in parallel. */
3893
+ this.taskSetsLR = /* @__PURE__ */ new Map();
3894
+ /** Tasks that only need to access the "left" model. */
3895
+ this.taskSetsL = /* @__PURE__ */ new Map();
3896
+ /** Tasks that only need to access the "right" model. */
3897
+ this.taskSetsR = /* @__PURE__ */ new Map();
3898
+ this.complete = false;
2617
3899
  }
2618
- addTask(kind, datasetKey, dataFunc) {
2619
- if (kind === "compare") {
2620
- this.requestKind = "compare";
3900
+ /**
3901
+ * Add a request to the plan for the given scenario(s) and data task.
3902
+ *
3903
+ * @param scenarioSpecL The input scenario used to configure the "left" model, or undefined if no data
3904
+ * is needed from the left model.
3905
+ * @param scenarioSpecR The input scenario used to configure the "right" model, or undefined if no data
3906
+ * is needed from the right model.
3907
+ * @param datasetKey The key for the dataset to be fetched from each model for the given scenario.
3908
+ * @param dataAction The action to be performed with the fetched datasets.
3909
+ */
3910
+ addRequest(scenarioSpecL, scenarioSpecR, datasetKey, dataAction) {
3911
+ if (scenarioSpecL === void 0 && scenarioSpecR === void 0) {
3912
+ console.warn("WARNING: Both scenario specs are undefined for DataPlanner request, skipping");
3913
+ return;
3914
+ }
3915
+ let taskSetsMap;
3916
+ let uid;
3917
+ if (scenarioSpecL && scenarioSpecR) {
3918
+ taskSetsMap = this.taskSetsLR;
3919
+ uid = scenarioPairUid(scenarioSpecL, scenarioSpecR);
3920
+ } else if (scenarioSpecR) {
3921
+ taskSetsMap = this.taskSetsR;
3922
+ uid = scenarioSpecR.uid;
3923
+ } else {
3924
+ taskSetsMap = this.taskSetsL;
3925
+ uid = scenarioSpecL.uid;
2621
3926
  }
2622
- const dataTask = {
3927
+ let taskSet = taskSetsMap.get(uid);
3928
+ if (!taskSet) {
3929
+ taskSet = new DataTaskSet(scenarioSpecL, scenarioSpecR);
3930
+ taskSetsMap.set(uid, taskSet);
3931
+ }
3932
+ taskSet.addTask({
2623
3933
  datasetKey,
2624
- dataFunc
3934
+ dataAction
3935
+ });
3936
+ }
3937
+ /**
3938
+ * Build a plan that minimizes the number of data fetches needed.
3939
+ */
3940
+ buildPlan() {
3941
+ if (this.complete) {
3942
+ throw new Error("DataPlanner.buildPlan() can only be called once");
3943
+ }
3944
+ this.complete = true;
3945
+ const lKeyMappings = /* @__PURE__ */ new Map();
3946
+ const rKeyMappings = /* @__PURE__ */ new Map();
3947
+ for (const lrUid of this.taskSetsLR.keys()) {
3948
+ const [lUid, rUid] = lrUid.split("::");
3949
+ if (!lKeyMappings.has(lUid)) {
3950
+ lKeyMappings.set(lUid, lrUid);
3951
+ }
3952
+ if (!rKeyMappings.has(rUid)) {
3953
+ rKeyMappings.set(rUid, lrUid);
3954
+ }
3955
+ }
3956
+ function merge(taskSetsLR, taskSetsForSide, keyMappingsForSide) {
3957
+ for (const [keyForSide, taskSetForSide] of taskSetsForSide.entries()) {
3958
+ const lrKey = keyMappingsForSide.get(keyForSide);
3959
+ if (lrKey) {
3960
+ const taskSetLR = taskSetsLR.get(lrKey);
3961
+ taskSetLR.merge(taskSetForSide);
3962
+ taskSetsForSide.delete(keyForSide);
3963
+ }
3964
+ }
3965
+ }
3966
+ merge(this.taskSetsLR, this.taskSetsL, lKeyMappings);
3967
+ merge(this.taskSetsLR, this.taskSetsR, rKeyMappings);
3968
+ const requests = [];
3969
+ const batchSize = this.batchSize;
3970
+ function addRequests(taskSets) {
3971
+ for (const taskSet of taskSets) {
3972
+ requests.push(...taskSet.buildRequests(batchSize));
3973
+ }
3974
+ }
3975
+ addRequests(this.taskSetsLR.values());
3976
+ addRequests(this.taskSetsL.values());
3977
+ addRequests(this.taskSetsR.values());
3978
+ return {
3979
+ requests
2625
3980
  };
3981
+ }
3982
+ };
3983
+ var DataTaskSet = class {
3984
+ constructor(scenarioSpecL, scenarioSpecR) {
3985
+ this.scenarioSpecL = scenarioSpecL;
3986
+ this.scenarioSpecR = scenarioSpecR;
3987
+ this.modelTasks = /* @__PURE__ */ new Map();
3988
+ this.modelImplTasks = /* @__PURE__ */ new Map();
3989
+ }
3990
+ /**
3991
+ * Add a task that will be performed when the data is fetched for the scenario(s)
3992
+ * associated with this task set.
3993
+ *
3994
+ * @param dataTask A task that performs an action using the fetched dataset(s).
3995
+ */
3996
+ addTask(dataTask) {
2626
3997
  let taskMap;
2627
- if (datasetKey.startsWith("ModelImpl")) {
3998
+ if (dataTask.datasetKey.startsWith("ModelImpl")) {
2628
3999
  taskMap = this.modelImplTasks;
2629
4000
  } else {
2630
4001
  taskMap = this.modelTasks;
2631
4002
  }
2632
- let tasks = taskMap.get(datasetKey);
4003
+ let tasks = taskMap.get(dataTask.datasetKey);
2633
4004
  if (!tasks) {
2634
4005
  tasks = [];
2635
- taskMap.set(datasetKey, tasks);
4006
+ taskMap.set(dataTask.datasetKey, tasks);
2636
4007
  }
2637
4008
  tasks.push(dataTask);
2638
4009
  }
4010
+ /**
4011
+ * Add all tasks from the given set into this set.
4012
+ *
4013
+ * @param otherTaskSet The other task set that will be merged into this one.
4014
+ */
4015
+ merge(otherTaskSet) {
4016
+ for (const tasks of otherTaskSet.modelTasks.values()) {
4017
+ for (const task of tasks) {
4018
+ this.addTask(task);
4019
+ }
4020
+ }
4021
+ for (const tasks of otherTaskSet.modelImplTasks.values()) {
4022
+ for (const task of tasks) {
4023
+ this.addTask(task);
4024
+ }
4025
+ }
4026
+ }
4027
+ /**
4028
+ * Create one or more data requests that can be used to fetch data for the configured scenario(s).
4029
+ *
4030
+ * @param batchSize The maximum number of impl vars that can be fetched with a single request.
4031
+ * This is usually the same as the number of normal model outputs.
4032
+ */
2639
4033
  buildRequests(batchSize) {
2640
4034
  const dataRequests = [];
2641
4035
  if (this.modelTasks.size > 0) {
2642
4036
  const dataTasks = [];
2643
4037
  this.modelTasks.forEach((tasks) => dataTasks.push(...tasks));
2644
4038
  dataRequests.push({
2645
- kind: this.requestKind,
2646
- scenario: this.scenario,
4039
+ scenarioSpecL: this.scenarioSpecL,
4040
+ scenarioSpecR: this.scenarioSpecR,
2647
4041
  dataTasks
2648
4042
  });
2649
4043
  }
@@ -2656,8 +4050,8 @@ var ScenarioTaskSet = class {
2656
4050
  dataTasks.push(...this.modelImplTasks.get(datasetKey));
2657
4051
  }
2658
4052
  dataRequests.push({
2659
- kind: this.requestKind,
2660
- scenario: this.scenario,
4053
+ scenarioSpecL: this.scenarioSpecL,
4054
+ scenarioSpecR: this.scenarioSpecR,
2661
4055
  dataTasks
2662
4056
  });
2663
4057
  }
@@ -2665,29 +4059,11 @@ var ScenarioTaskSet = class {
2665
4059
  return dataRequests;
2666
4060
  }
2667
4061
  };
2668
- var DataPlanner = class {
2669
- constructor(batchSize) {
2670
- this.batchSize = batchSize;
2671
- this.scenarioTaskSets = /* @__PURE__ */ new Map();
2672
- }
2673
- addRequest(kind, scenario, datasetKey, dataFunc) {
2674
- let scenarioTaskSet = this.scenarioTaskSets.get(scenario.key);
2675
- if (!scenarioTaskSet) {
2676
- scenarioTaskSet = new ScenarioTaskSet(scenario);
2677
- this.scenarioTaskSets.set(scenario.key, scenarioTaskSet);
2678
- }
2679
- scenarioTaskSet.addTask(kind, datasetKey, dataFunc);
2680
- }
2681
- buildPlan() {
2682
- const requests = [];
2683
- for (const taskSet of this.scenarioTaskSets.values()) {
2684
- requests.push(...taskSet.buildRequests(this.batchSize));
2685
- }
2686
- return {
2687
- requests
2688
- };
2689
- }
2690
- };
4062
+ function scenarioPairUid(scenarioSpecL, scenarioSpecR) {
4063
+ const uidL = (scenarioSpecL == null ? void 0 : scenarioSpecL.uid) || "";
4064
+ const uidR = (scenarioSpecR == null ? void 0 : scenarioSpecR.uid) || "";
4065
+ return `${uidL}::${uidR}`;
4066
+ }
2691
4067
 
2692
4068
  // src/check/check-runner.ts
2693
4069
  function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios) {
@@ -2697,7 +4073,7 @@ function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplify
2697
4073
  const checkPlan = checkPlanner.buildPlan();
2698
4074
  const refDatasets = /* @__PURE__ */ new Map();
2699
4075
  for (const [dataRefKey, dataRef] of checkPlan.dataRefs.entries()) {
2700
- refDataPlanner.addRequest("check", dataRef.scenario.scenario, dataRef.dataset.datasetKey, (datasets) => {
4076
+ refDataPlanner.addRequest(void 0, dataRef.scenario.spec, dataRef.dataset.datasetKey, (datasets) => {
2701
4077
  const dataset = datasets.datasetR;
2702
4078
  if (dataset) {
2703
4079
  refDatasets.set(dataRefKey, dataset);
@@ -2706,7 +4082,7 @@ function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplify
2706
4082
  }
2707
4083
  const checkResults = /* @__PURE__ */ new Map();
2708
4084
  for (const [checkKey, checkTask] of checkPlan.tasks.entries()) {
2709
- dataPlanner.addRequest("check", checkTask.scenario.scenario, checkTask.dataset.datasetKey, (datasets) => {
4085
+ dataPlanner.addRequest(void 0, checkTask.scenario.spec, checkTask.dataset.datasetKey, (datasets) => {
2710
4086
  const dataset = datasets.datasetR;
2711
4087
  const checkResult = runCheck(checkTask, dataset, refDatasets);
2712
4088
  checkResults.set(checkKey, checkResult);
@@ -2737,7 +4113,7 @@ function runCheck(checkTask, dataset, refDatasets) {
2737
4113
  name: dataRef.dataset.name
2738
4114
  }
2739
4115
  };
2740
- } else if (dataRef.scenario.scenario === void 0) {
4116
+ } else if (dataRef.scenario.spec === void 0) {
2741
4117
  if (dataRef.scenario.error) {
2742
4118
  return {
2743
4119
  status: "error",
@@ -2774,16 +4150,15 @@ function runCheck(checkTask, dataset, refDatasets) {
2774
4150
  return checkTask.action.run(dataset, opRefDatasets);
2775
4151
  }
2776
4152
 
2777
- // src/compare/compare-runner.ts
2778
- function runCompare(compareConfig, dataPlanner, simplifyScenarios) {
2779
- const scenarios = simplifyScenarios ? [allInputsAtPositionScenario("at-default")] : compareConfig.scenarios.getScenarios();
2780
- const datasetReports = [];
2781
- for (const scenario of scenarios) {
2782
- const datasetKeys = compareConfig.datasets.getDatasetKeysForScenario(scenario);
4153
+ // src/comparison/run/comparison-runner.ts
4154
+ function runComparisons(comparisonConfig, dataPlanner) {
4155
+ const testReports = [];
4156
+ for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {
4157
+ const datasetKeys = comparisonConfig.datasets.getDatasetKeysForScenario(scenario);
2783
4158
  for (const datasetKey of datasetKeys) {
2784
- dataPlanner.addRequest("compare", scenario, datasetKey, (datasets) => {
4159
+ dataPlanner.addRequest(scenario.specL, scenario.specR, datasetKey, (datasets) => {
2785
4160
  const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR);
2786
- datasetReports.push({
4161
+ testReports.push({
2787
4162
  scenarioKey: scenario.key,
2788
4163
  datasetKey,
2789
4164
  diffReport
@@ -2792,7 +4167,7 @@ function runCompare(compareConfig, dataPlanner, simplifyScenarios) {
2792
4167
  }
2793
4168
  }
2794
4169
  return () => {
2795
- return datasetReports;
4170
+ return testReports;
2796
4171
  };
2797
4172
  }
2798
4173
 
@@ -2830,9 +4205,9 @@ var SuiteRunner = class {
2830
4205
  const checkSpec = checkSpecResult.value;
2831
4206
  const simplifyScenarios = (options == null ? void 0 : options.simplifyScenarios) === true;
2832
4207
  const buildCheckReport2 = runChecks(this.config.check, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios);
2833
- let buildCompareDatasetReports;
2834
- if (this.config.compare) {
2835
- buildCompareDatasetReports = runCompare(this.config.compare, dataPlanner, simplifyScenarios);
4208
+ let buildComparisonTestReports;
4209
+ if (this.config.comparison) {
4210
+ buildComparisonTestReports = runComparisons(this.config.comparison, dataPlanner);
2836
4211
  }
2837
4212
  this.taskQueue.onIdle = (error) => {
2838
4213
  var _a2, _b2, _c2, _d2;
@@ -2843,17 +4218,17 @@ var SuiteRunner = class {
2843
4218
  (_b2 = (_a2 = this.callbacks).onError) == null ? void 0 : _b2.call(_a2, error);
2844
4219
  } else {
2845
4220
  const checkReport = buildCheckReport2();
2846
- let compareReport;
2847
- if (this.config.compare) {
2848
- compareReport = {
2849
- datasetReports: buildCompareDatasetReports(),
4221
+ let comparisonReport;
4222
+ if (this.config.comparison) {
4223
+ comparisonReport = {
4224
+ testReports: buildComparisonTestReports(),
2850
4225
  perfReportL: this.perfStatsL.toReport(),
2851
4226
  perfReportR: this.perfStatsR.toReport()
2852
4227
  };
2853
4228
  }
2854
4229
  (_d2 = (_c2 = this.callbacks).onComplete) == null ? void 0 : _d2.call(_c2, {
2855
4230
  checkReport,
2856
- compareReport
4231
+ comparisonReport
2857
4232
  });
2858
4233
  }
2859
4234
  };
@@ -2862,10 +4237,10 @@ var SuiteRunner = class {
2862
4237
  const dataRequests = [...refDataPlan.requests, ...dataPlan.requests];
2863
4238
  const taskCount = dataRequests.length;
2864
4239
  if (taskCount === 0) {
2865
- let compareReport;
2866
- if (this.config.compare) {
2867
- compareReport = {
2868
- datasetReports: [],
4240
+ let comparisonReport;
4241
+ if (this.config.comparison) {
4242
+ comparisonReport = {
4243
+ testReports: [],
2869
4244
  perfReportL: this.perfStatsL.toReport(),
2870
4245
  perfReportR: this.perfStatsR.toReport()
2871
4246
  };
@@ -2876,7 +4251,7 @@ var SuiteRunner = class {
2876
4251
  checkReport: {
2877
4252
  groups: []
2878
4253
  },
2879
- compareReport
4254
+ comparisonReport
2880
4255
  });
2881
4256
  return;
2882
4257
  }
@@ -2890,51 +4265,46 @@ var SuiteRunner = class {
2890
4265
  });
2891
4266
  }
2892
4267
  }
2893
- async processRequest(request) {
2894
- const datasetKeySet = /* @__PURE__ */ new Set();
2895
- for (const dataTask of request.dataTasks) {
2896
- datasetKeySet.add(dataTask.datasetKey);
2897
- }
2898
- const datasetKeys = [...datasetKeySet];
2899
- const scenario = request.scenario;
2900
- let datasetsResultL;
2901
- let datasetsResultR;
2902
- switch (request.kind) {
2903
- case "check": {
2904
- const bundleModel = this.config.check.bundle.model;
2905
- datasetsResultR = await bundleModel.getDatasetsForScenario(scenario, datasetKeys);
2906
- break;
4268
+ processRequest(request) {
4269
+ return __async(this, null, function* () {
4270
+ var _a, _b;
4271
+ const datasetKeySet = /* @__PURE__ */ new Set();
4272
+ for (const dataTask of request.dataTasks) {
4273
+ datasetKeySet.add(dataTask.datasetKey);
2907
4274
  }
2908
- case "compare": {
2909
- const bundleModelL = this.config.compare.bundleL.model;
2910
- const bundleModelR = this.config.compare.bundleR.model;
2911
- const [resultL, resultR] = await Promise.all([
2912
- bundleModelL.getDatasetsForScenario(scenario, datasetKeys),
2913
- bundleModelR.getDatasetsForScenario(scenario, datasetKeys)
2914
- ]);
2915
- datasetsResultL = resultL;
2916
- datasetsResultR = resultR;
2917
- break;
4275
+ const datasetKeys = [...datasetKeySet];
4276
+ function getDatasets(bundleModel, scenarioSpec) {
4277
+ return __async(this, null, function* () {
4278
+ if (bundleModel && scenarioSpec) {
4279
+ return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys);
4280
+ } else {
4281
+ return void 0;
4282
+ }
4283
+ });
2918
4284
  }
2919
- default:
2920
- (0, import_assert_never10.default)(request.kind);
2921
- }
2922
- if (datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime) {
2923
- this.perfStatsL.addRun(datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime);
2924
- }
2925
- if (datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime) {
2926
- this.perfStatsR.addRun(datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime);
2927
- }
2928
- const datasetMapL = datasetsResultL == null ? void 0 : datasetsResultL.datasetMap;
2929
- const datasetMapR = datasetsResultR == null ? void 0 : datasetsResultR.datasetMap;
2930
- for (const dataTask of request.dataTasks) {
2931
- const datasetL = datasetMapL == null ? void 0 : datasetMapL.get(dataTask.datasetKey);
2932
- const datasetR = datasetMapR == null ? void 0 : datasetMapR.get(dataTask.datasetKey);
2933
- dataTask.dataFunc({
2934
- datasetL,
2935
- datasetR
2936
- });
2937
- }
4285
+ const bundleModelL = (_a = this.config.comparison) == null ? void 0 : _a.bundleL.model;
4286
+ const bundleModelR = ((_b = this.config.comparison) == null ? void 0 : _b.bundleR.model) || this.config.check.bundle.model;
4287
+ const [datasetsResultL, datasetsResultR] = yield Promise.all([
4288
+ getDatasets(bundleModelL, request.scenarioSpecL),
4289
+ getDatasets(bundleModelR, request.scenarioSpecR)
4290
+ ]);
4291
+ if (datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime) {
4292
+ this.perfStatsL.addRun(datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime);
4293
+ }
4294
+ if (datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime) {
4295
+ this.perfStatsR.addRun(datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime);
4296
+ }
4297
+ const datasetMapL = datasetsResultL == null ? void 0 : datasetsResultL.datasetMap;
4298
+ const datasetMapR = datasetsResultR == null ? void 0 : datasetsResultR.datasetMap;
4299
+ for (const dataTask of request.dataTasks) {
4300
+ const datasetL = datasetMapL == null ? void 0 : datasetMapL.get(dataTask.datasetKey);
4301
+ const datasetR = datasetMapR == null ? void 0 : datasetMapR.get(dataTask.datasetKey);
4302
+ dataTask.dataAction({
4303
+ datasetL,
4304
+ datasetR
4305
+ });
4306
+ }
4307
+ });
2938
4308
  }
2939
4309
  };
2940
4310
  function runSuite(config, callbacks, options) {
@@ -2945,44 +4315,35 @@ function runSuite(config, callbacks, options) {
2945
4315
  };
2946
4316
  }
2947
4317
 
2948
- // src/suite/suite-summary.ts
4318
+ // src/suite/suite-reporting.ts
2949
4319
  function suiteSummaryFromReport(suiteReport) {
2950
4320
  const checkSummary = checkSummaryFromReport(suiteReport.checkReport);
2951
- let compareSummary;
2952
- if (suiteReport.compareReport) {
2953
- compareSummary = compareSummaryFromReport(suiteReport.compareReport);
4321
+ let comparisonSummary;
4322
+ if (suiteReport.comparisonReport) {
4323
+ comparisonSummary = comparisonSummaryFromReport(suiteReport.comparisonReport);
2954
4324
  }
2955
4325
  return {
2956
4326
  checkSummary,
2957
- compareSummary
4327
+ comparisonSummary
2958
4328
  };
2959
4329
  }
2960
4330
  // Annotate the CommonJS export names for ESM import in node:
2961
4331
  0 && (module.exports = {
2962
4332
  CheckDataCoordinator,
2963
- CompareDataCoordinator,
2964
- DatasetManager,
4333
+ ComparisonDataCoordinator,
2965
4334
  PerfRunner,
2966
4335
  PerfStats,
2967
- ScenarioManager,
2968
- allInputsAtPositionScenario,
4336
+ categorizeComparisonTestSummaries,
2969
4337
  checkReportFromSummary,
2970
4338
  checkSummaryFromReport,
2971
- compareDatasets,
2972
- compareSummaryFromReport,
4339
+ comparisonSummaryFromReport,
2973
4340
  createConfig,
2974
4341
  datasetMessage,
2975
4342
  diffDatasets,
2976
4343
  diffGraphs,
2977
- inputAtPositionScenario,
2978
- inputAtValueScenario,
2979
- matrixScenarios,
2980
- positionSetting,
2981
4344
  predicateMessage,
2982
4345
  runSuite,
2983
4346
  scenarioMessage,
2984
- settingsScenario,
2985
- suiteSummaryFromReport,
2986
- valueSetting
4347
+ suiteSummaryFromReport
2987
4348
  });
2988
4349
  //# sourceMappingURL=index.cjs.map