@sdeverywhere/check-core 0.1.5 → 0.1.7

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
@@ -64,55 +64,207 @@ var __async = (__this, __arguments, generator) => {
64
64
  };
65
65
 
66
66
  // src/index.ts
67
- var src_exports = {};
68
- __export(src_exports, {
67
+ var index_exports = {};
68
+ __export(index_exports, {
69
69
  CheckDataCoordinator: () => CheckDataCoordinator,
70
70
  ComparisonDataCoordinator: () => ComparisonDataCoordinator,
71
- PerfRunner: () => PerfRunner,
72
71
  PerfStats: () => PerfStats,
73
72
  categorizeComparisonTestSummaries: () => categorizeComparisonTestSummaries,
74
73
  checkReportFromSummary: () => checkReportFromSummary,
75
74
  checkSummaryFromReport: () => checkSummaryFromReport,
76
75
  comparisonSummaryFromReport: () => comparisonSummaryFromReport,
76
+ createCheckDataCoordinator: () => createCheckDataCoordinator,
77
+ createCheckDataCoordinatorForTests: () => createCheckDataCoordinatorForTests,
78
+ createComparisonDataCoordinator: () => createComparisonDataCoordinator,
77
79
  createConfig: () => createConfig,
78
80
  datasetMessage: () => datasetMessage,
81
+ decodeImplVars: () => decodeImplVars,
79
82
  diffDatasets: () => diffDatasets,
80
83
  diffGraphs: () => diffGraphs,
84
+ encodeImplVars: () => encodeImplVars,
81
85
  getScoresForTestSummaries: () => getScoresForTestSummaries,
82
86
  predicateMessage: () => predicateMessage,
87
+ runPerf: () => runPerf,
83
88
  runSuite: () => runSuite,
89
+ runTrace: () => runTrace,
84
90
  scenarioMessage: () => scenarioMessage,
85
- suiteSummaryFromReport: () => suiteSummaryFromReport
91
+ suiteSummaryFromReport: () => suiteSummaryFromReport,
92
+ testSummaryFromReport: () => testSummaryFromReport
86
93
  });
87
- module.exports = __toCommonJS(src_exports);
94
+ module.exports = __toCommonJS(index_exports);
95
+
96
+ // src/bundle/impl-vars-codec.ts
97
+ function encodeImplVars(input) {
98
+ const subscripts = [];
99
+ const variables = [];
100
+ const varTypes = [];
101
+ const varInstances = {};
102
+ const subscriptMap = /* @__PURE__ */ new Map();
103
+ const variableMap = /* @__PURE__ */ new Map();
104
+ const varTypeMap = /* @__PURE__ */ new Map();
105
+ for (const [groupKey, implVars] of Object.entries(input)) {
106
+ const instances = [];
107
+ for (const implVar of implVars) {
108
+ const varIdInfo = parseSubscripts(implVar.varId);
109
+ const varNameInfo = parseSubscripts(implVar.varName);
110
+ let varTypeIndex = varTypeMap.get(implVar.varType);
111
+ if (varTypeIndex === void 0) {
112
+ varTypeIndex = varTypes.length;
113
+ varTypes.push(implVar.varType);
114
+ varTypeMap.set(implVar.varType, varTypeIndex);
115
+ }
116
+ let variableIndex = variableMap.get(implVar.varIndex);
117
+ if (variableIndex === void 0) {
118
+ variableIndex = variables.length;
119
+ variables.push({
120
+ n: varNameInfo.base,
121
+ i: varIdInfo.base,
122
+ x: implVar.varIndex
123
+ });
124
+ variableMap.set(implVar.varIndex, variableIndex);
125
+ }
126
+ let subscriptIndices;
127
+ let subscriptValues;
128
+ if (varIdInfo.subscripts.length > 0) {
129
+ subscriptIndices = [];
130
+ subscriptValues = implVar.subscriptIndices || [];
131
+ for (let i = 0; i < varIdInfo.subscripts.length; i++) {
132
+ const subscriptId = varIdInfo.subscripts[i];
133
+ const subscriptName = varNameInfo.subscripts[i];
134
+ let subscriptIndex = subscriptMap.get(subscriptId);
135
+ if (subscriptIndex === void 0) {
136
+ subscriptIndex = subscripts.length;
137
+ subscripts.push({
138
+ n: subscriptName,
139
+ i: subscriptId
140
+ });
141
+ subscriptMap.set(subscriptId, subscriptIndex);
142
+ }
143
+ subscriptIndices.push(subscriptIndex);
144
+ }
145
+ }
146
+ const instance = [varTypeIndex, variableIndex];
147
+ if (subscriptIndices) {
148
+ instance.push(...subscriptIndices, ...subscriptValues);
149
+ }
150
+ instances.push(instance);
151
+ }
152
+ varInstances[groupKey] = instances;
153
+ }
154
+ return {
155
+ subscripts,
156
+ variables,
157
+ varTypes,
158
+ varInstances
159
+ };
160
+ }
161
+ function decodeImplVars(encoded) {
162
+ const result = {};
163
+ for (const [groupKey, instances] of Object.entries(encoded.varInstances)) {
164
+ const implVars = [];
165
+ for (const instance of instances) {
166
+ const varType = encoded.varTypes[instance[0]];
167
+ const variable = encoded.variables[instance[1]];
168
+ let varId = variable.i;
169
+ let varName = variable.n;
170
+ if (instance.length > 2) {
171
+ const subscriptIds = [];
172
+ const subscriptNames = [];
173
+ const subscriptCount = (instance.length - 2) / 2;
174
+ const subscriptIndices = instance.slice(2, 2 + subscriptCount);
175
+ for (const subscriptIndex of subscriptIndices) {
176
+ const subscript = encoded.subscripts[subscriptIndex];
177
+ subscriptIds.push(subscript.i);
178
+ subscriptNames.push(subscript.n);
179
+ }
180
+ varId += `[${subscriptIds.join(",")}]`;
181
+ varName += `[${subscriptNames.join(",")}]`;
182
+ }
183
+ const implVar = {
184
+ varId,
185
+ varName,
186
+ varType,
187
+ varIndex: variable.x,
188
+ subscriptIndices: instance.length > 2 ? instance.slice(2 + (instance.length - 2) / 2) : void 0
189
+ };
190
+ implVars.push(implVar);
191
+ }
192
+ result[groupKey] = implVars;
193
+ }
194
+ return result;
195
+ }
196
+ function parseSubscripts(text) {
197
+ const bracketIndex = text.indexOf("[");
198
+ if (bracketIndex === -1) {
199
+ return { base: text, subscripts: [] };
200
+ }
201
+ const base = text.substring(0, bracketIndex);
202
+ const subscriptText = text.substring(bracketIndex + 1, text.lastIndexOf("]"));
203
+ const subscripts = subscriptText.split(",").map((s) => s.trim());
204
+ return { base, subscripts };
205
+ }
88
206
 
89
207
  // src/_shared/task-queue.ts
90
- var TaskQueue = class {
91
- constructor(processor) {
92
- this.processor = processor;
208
+ var TaskQueue = class _TaskQueue {
209
+ /**
210
+ * @param executors The map of available task executors.
211
+ */
212
+ constructor(executors) {
213
+ this.executors = executors;
93
214
  /** The queue of task keys, most recent at front. */
94
215
  this.taskKeyQueue = [];
95
216
  /** The map of tasks. */
96
217
  this.taskMap = /* @__PURE__ */ new Map();
218
+ // /** The set of keys for the executors that are currently processing tasks. */
219
+ // private readonly activeExecutorKeys: Set<TaskExecutorKey> = new Set()
220
+ /** The idle event listeners. */
221
+ this.idleListeners = [];
97
222
  /** Whether tasks are being processed. */
98
223
  this.processing = false;
99
224
  /** Whether `shutdown` has been called. */
100
225
  this.stopped = false;
101
226
  }
102
- addTask(key, input, onComplete) {
227
+ /**
228
+ * Initialize the shared `TaskQueue` instance.
229
+ *
230
+ * @param executors The map of available task executors.
231
+ */
232
+ static initialize(executors) {
233
+ if (executors.size === 0) {
234
+ throw new Error("Must provide at least one executor");
235
+ }
236
+ this.instance = new _TaskQueue(executors);
237
+ }
238
+ /**
239
+ * Get the shared `TaskQueue` instance.
240
+ */
241
+ static getInstance() {
242
+ if (!this.instance) {
243
+ throw new Error("TaskQueue not initialized; must call `initialize` first");
244
+ }
245
+ return this.instance;
246
+ }
247
+ /**
248
+ * Add a task to the queue.
249
+ *
250
+ * @param task The task to add.
251
+ */
252
+ addTask(task) {
103
253
  if (this.stopped) {
104
254
  return;
105
255
  }
106
- if (this.taskMap.has(key)) {
107
- throw new Error(`Task already added for key ${key}`);
256
+ if (this.taskMap.has(task.key)) {
257
+ throw new Error(`Task already added for key ${task.key}`);
108
258
  }
109
- this.taskKeyQueue.push(key);
110
- this.taskMap.set(key, {
111
- input,
112
- onComplete
113
- });
259
+ this.taskKeyQueue.push(task.key);
260
+ this.taskMap.set(task.key, task);
114
261
  this.processTasksIfNeeded();
115
262
  }
263
+ /**
264
+ * Cancel a task.
265
+ *
266
+ * @param taskKey The key of the task to cancel.
267
+ */
116
268
  cancelTask(taskKey) {
117
269
  const index = this.taskKeyQueue.indexOf(taskKey);
118
270
  if (index >= 0) {
@@ -120,9 +272,37 @@ var TaskQueue = class {
120
272
  }
121
273
  this.taskMap.delete(taskKey);
122
274
  }
275
+ /**
276
+ * Add an idle listener.
277
+ *
278
+ * @param listener The listener to add.
279
+ */
280
+ onIdle(listener) {
281
+ this.idleListeners.push(listener);
282
+ }
283
+ /**
284
+ * Remove an idle listener.
285
+ *
286
+ * @param listener The listener to remove.
287
+ */
288
+ removeIdleListener(listener) {
289
+ this.idleListeners.splice(this.idleListeners.indexOf(listener), 1);
290
+ }
291
+ /**
292
+ * Notify the idle listeners.
293
+ *
294
+ * @param error The error to notify the listeners with.
295
+ */
296
+ notifyIdle(error) {
297
+ for (const listener of this.idleListeners) {
298
+ listener(error);
299
+ }
300
+ }
301
+ /**
302
+ * Shutdown the task queue, cancelling all pending tasks.
303
+ */
123
304
  shutdown() {
124
305
  this.stopped = true;
125
- this.processing = false;
126
306
  this.taskKeyQueue.length = 0;
127
307
  this.taskMap.clear();
128
308
  }
@@ -130,75 +310,98 @@ var TaskQueue = class {
130
310
  if (!this.stopped && !this.processing) {
131
311
  this.processing = true;
132
312
  setTimeout(() => {
133
- this.processNextTask();
313
+ this.processNextTasks();
134
314
  });
135
315
  }
136
316
  }
137
- processNextTask() {
317
+ processNextTasks() {
138
318
  return __async(this, null, function* () {
139
- var _a, _b;
140
- const taskKey = this.taskKeyQueue.shift();
141
- if (!taskKey) {
319
+ const taskKeys = this.taskKeyQueue.splice(0, this.executors.size);
320
+ if (taskKeys.length === 0) {
321
+ this.processing = false;
322
+ if (!this.stopped) {
323
+ this.notifyIdle();
324
+ }
142
325
  return;
143
326
  }
144
- const task = this.taskMap.get(taskKey);
145
- if (task) {
327
+ const executeCalls = [];
328
+ const availableExecutorKeys = Array.from(this.executors.keys());
329
+ for (const taskKey of taskKeys) {
330
+ const task = this.taskMap.get(taskKey);
331
+ if (!task) {
332
+ continue;
333
+ }
146
334
  this.taskMap.delete(taskKey);
147
- } else {
148
- return;
335
+ const executorKey = availableExecutorKeys.shift();
336
+ const executor = this.executors.get(executorKey);
337
+ if (!executor) {
338
+ throw new Error(`No executor found for key ${executorKey}`);
339
+ }
340
+ executeCalls.push(executor.execute(task));
149
341
  }
150
- let output;
151
342
  try {
152
- output = yield this.processor.process(task.input);
343
+ yield Promise.all(executeCalls);
153
344
  } catch (e) {
154
345
  if (!this.stopped) {
155
346
  this.shutdown();
156
- (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
347
+ this.notifyIdle(e);
157
348
  }
158
349
  return;
159
350
  }
160
- task.onComplete(output);
161
351
  if (this.taskKeyQueue.length > 0) {
162
352
  setTimeout(() => {
163
- this.processNextTask();
353
+ this.processNextTasks();
164
354
  });
165
355
  } else {
166
356
  this.processing = false;
167
357
  if (!this.stopped) {
168
- (_b = this.onIdle) == null ? void 0 : _b.call(this);
358
+ this.notifyIdle();
169
359
  }
170
360
  }
171
361
  });
172
362
  }
173
363
  };
364
+ function createExecutor(bundleModelL, bundleModelR) {
365
+ return {
366
+ execute: (task) => __async(null, null, function* () {
367
+ return task.process({
368
+ L: bundleModelL,
369
+ R: bundleModelR
370
+ });
371
+ })
372
+ };
373
+ }
174
374
 
175
375
  // src/check/check-data-coordinator.ts
176
376
  var CheckDataCoordinator = class {
177
- constructor(bundleModel) {
178
- this.bundleModel = bundleModel;
179
- this.taskQueue = new TaskQueue({
180
- process: (request) => __async(this, null, function* () {
181
- const result = yield this.bundleModel.getDatasetsForScenario(request.scenarioSpec, [request.datasetKey]);
182
- const dataset = result.datasetMap.get(request.datasetKey);
183
- return {
184
- dataset
185
- };
186
- })
187
- });
377
+ constructor(taskQueue) {
378
+ this.taskQueue = taskQueue;
188
379
  }
189
380
  requestDataset(requestKey, scenarioSpec, datasetKey, onResponse) {
190
- const request = {
191
- scenarioSpec,
192
- datasetKey
381
+ const task = {
382
+ key: requestKey,
383
+ kind: "check-data-coordinator",
384
+ process: (bundleModels) => __async(null, null, function* () {
385
+ const bundleModelR = bundleModels.R;
386
+ const result = yield bundleModelR.getDatasetsForScenario(scenarioSpec, [datasetKey]);
387
+ const dataset = result.datasetMap.get(datasetKey);
388
+ onResponse(dataset);
389
+ })
193
390
  };
194
- this.taskQueue.addTask(requestKey, request, (response) => {
195
- onResponse(response.dataset);
196
- });
391
+ this.taskQueue.addTask(task);
197
392
  }
198
393
  cancelRequest(key) {
199
394
  this.taskQueue.cancelTask(key);
200
395
  }
201
396
  };
397
+ function createCheckDataCoordinator() {
398
+ return new CheckDataCoordinator(TaskQueue.getInstance());
399
+ }
400
+ function createCheckDataCoordinatorForTests(bundleModel) {
401
+ const executor = createExecutor(void 0, bundleModel);
402
+ const taskQueue = new TaskQueue(/* @__PURE__ */ new Map([["test-executor-0", executor]]));
403
+ return new CheckDataCoordinator(taskQueue);
404
+ }
202
405
 
203
406
  // src/check/check-report.ts
204
407
  var import_assert_never2 = __toESM(require("assert-never"), 1);
@@ -260,6 +463,10 @@ function buildCheckReport(checkPlan, checkResults) {
260
463
  testStatus = "failed";
261
464
  scenarioStatus = "failed";
262
465
  datasetStatus = "failed";
466
+ } else if (checkResult.status === "skipped" && testStatus === "passed") {
467
+ testStatus = "skipped";
468
+ scenarioStatus = "skipped";
469
+ datasetStatus = "skipped";
263
470
  }
264
471
  }
265
472
  predicateReports.push(predicateReport(predicatePlan, checkKey, checkResult));
@@ -1195,16 +1402,6 @@ function actionForPredicate(predicateSpec) {
1195
1402
  };
1196
1403
  }
1197
1404
 
1198
- // src/_shared/combo.ts
1199
- function cartesianProductOf(arr) {
1200
- return arr.reduce(
1201
- (a, b) => {
1202
- return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
1203
- },
1204
- [[]]
1205
- );
1206
- }
1207
-
1208
1405
  // src/check/check-dataset.ts
1209
1406
  function expandDatasets(modelSpec, datasetSpec) {
1210
1407
  var _a;
@@ -1233,25 +1430,10 @@ function expandDatasets(modelSpec, datasetSpec) {
1233
1430
  name: match.outputVar.varName
1234
1431
  });
1235
1432
  } else if (match.implVar) {
1236
- const implVar = match.implVar;
1237
- if (implVar.dimensions.length > 0) {
1238
- const baseDatasetKey = match.datasetKey;
1239
- const subscripts = [...implVar.dimensions.map((dim) => dim.subscripts)];
1240
- const subscriptCombos = cartesianProductOf(subscripts);
1241
- for (const subscriptCombo of subscriptCombos) {
1242
- const subIdParts = subscriptCombo.map((sub) => `[${sub.id}]`).join("");
1243
- const subNameParts = subscriptCombo.map((sub) => sub.name).join(",");
1244
- checkDatasets.push({
1245
- datasetKey: `${baseDatasetKey}${subIdParts}`,
1246
- name: `${implVar.varName}[${subNameParts}]`
1247
- });
1248
- }
1249
- } else {
1250
- checkDatasets.push({
1251
- datasetKey: match.datasetKey,
1252
- name: implVar.varName
1253
- });
1254
- }
1433
+ checkDatasets.push({
1434
+ datasetKey: match.datasetKey,
1435
+ name: match.implVar.varName
1436
+ });
1255
1437
  }
1256
1438
  }
1257
1439
  return checkDatasets;
@@ -1659,12 +1841,18 @@ var CheckPlanner = class {
1659
1841
  this.dataRefs = /* @__PURE__ */ new Map();
1660
1842
  this.checkKey = 1;
1661
1843
  }
1662
- addAllChecks(checkSpec, simplifyScenarios) {
1844
+ addAllChecks(checkSpec, skipChecks) {
1845
+ function skipCheckKey(groupName, testName) {
1846
+ return `${groupName.toLowerCase()} :: ${testName.toLowerCase()}`;
1847
+ }
1848
+ const skipChecksSet = new Set(skipChecks.map((check) => skipCheckKey(check.groupName, check.testName)));
1663
1849
  for (const groupSpec of checkSpec.groups) {
1664
1850
  const groupName = groupSpec.describe;
1665
1851
  const planTests = [];
1666
1852
  for (const testSpec of groupSpec.tests) {
1667
1853
  const testName = testSpec.it;
1854
+ const shouldSkip = skipChecksSet.has(skipCheckKey(groupName, testName));
1855
+ const simplifyScenarios = false;
1668
1856
  const checkScenarios = expandScenarios(this.modelSpec, testSpec.scenarios || [], simplifyScenarios);
1669
1857
  const checkDatasets = [];
1670
1858
  for (const datasetSpec of testSpec.datasets) {
@@ -1705,7 +1893,8 @@ var CheckPlanner = class {
1705
1893
  scenario: checkScenario,
1706
1894
  dataset: checkDataset,
1707
1895
  action: checkAction,
1708
- dataRefs
1896
+ dataRefs,
1897
+ skip: shouldSkip
1709
1898
  });
1710
1899
  }
1711
1900
  planDatasets.push({
@@ -1842,6 +2031,7 @@ function checkSummaryFromReport(checkReport) {
1842
2031
  break;
1843
2032
  case "failed":
1844
2033
  case "error":
2034
+ case "skipped":
1845
2035
  predicateSummaries.push({
1846
2036
  checkKey: predicate.checkKey,
1847
2037
  result: predicate.result
@@ -1859,14 +2049,14 @@ function checkSummaryFromReport(checkReport) {
1859
2049
  predicateSummaries
1860
2050
  };
1861
2051
  }
1862
- function checkReportFromSummary(checkConfig, checkSummary) {
2052
+ function checkReportFromSummary(checkConfig, checkSummary, skipChecks = []) {
1863
2053
  const checkSpecResult = parseTestYaml(checkConfig.tests);
1864
2054
  if (checkSpecResult.isErr()) {
1865
2055
  return void 0;
1866
2056
  }
1867
2057
  const checkSpec = checkSpecResult.value;
1868
- const checkPlanner = new CheckPlanner(checkConfig.bundle.model.modelSpec);
1869
- checkPlanner.addAllChecks(checkSpec, false);
2058
+ const checkPlanner = new CheckPlanner(checkConfig.bundle.modelSpec);
2059
+ checkPlanner.addAllChecks(checkSpec, skipChecks);
1870
2060
  const checkPlan = checkPlanner.buildPlan();
1871
2061
  const checkResults = /* @__PURE__ */ new Map();
1872
2062
  for (const predicateSummary of checkSummary.predicateSummaries) {
@@ -1876,98 +2066,116 @@ function checkReportFromSummary(checkConfig, checkSummary) {
1876
2066
  }
1877
2067
 
1878
2068
  // src/comparison/run/comparison-data-coordinator.ts
1879
- var import_assert_never7 = require("assert-never");
1880
2069
  var ComparisonDataCoordinator = class {
1881
- constructor(bundleModelL, bundleModelR) {
1882
- this.bundleModelL = bundleModelL;
1883
- this.bundleModelR = bundleModelR;
1884
- this.taskQueue = new TaskQueue({
1885
- process: (request) => __async(this, null, function* () {
1886
- switch (request.kind) {
1887
- case "dataset":
1888
- return this.processDatasetRequest(request);
1889
- case "graph-data":
1890
- return this.processGraphDataRequest(request);
1891
- default:
1892
- (0, import_assert_never7.assertNever)(request);
2070
+ constructor(taskQueue) {
2071
+ this.taskQueue = taskQueue;
2072
+ }
2073
+ /**
2074
+ * Request datasets from the two models.
2075
+ *
2076
+ * @param requestKey The unique key for the request.
2077
+ * @param sourceL The source of the first ("left") dataset. If "left", the datasets will
2078
+ * be fetched from the "left" bundle's model, otherwise they will be fetched from the
2079
+ * "right" bundle's model.
2080
+ * @param scenarioSpecL The scenario used for the first ("left") model of the comparison.
2081
+ * @param sourceR The source of the second ("right") dataset. If "left", the datasets
2082
+ * will be fetched from the "left" bundle's model, otherwise they will be fetched from
2083
+ * the "right" bundle's model.
2084
+ * @param scenarioSpecR The scenario used for the second ("right") model of the comparison.
2085
+ * @param graphId The keys of the datasets to be fetched.
2086
+ * @param onResponse The callback that will be called with the dataset maps.
2087
+ */
2088
+ requestDatasetMaps(requestKey, sourceL, scenarioSpecL, sourceR, scenarioSpecR, datasetKeys, onResponse) {
2089
+ function fetchDatasets(bundleModel, scenarioSpec) {
2090
+ return __async(this, null, function* () {
2091
+ if (scenarioSpec) {
2092
+ return bundleModel.getDatasetsForScenario(scenarioSpec, datasetKeys);
2093
+ } else {
2094
+ return void 0;
2095
+ }
2096
+ });
2097
+ }
2098
+ const task = {
2099
+ key: requestKey,
2100
+ kind: "comparison-data-coordinator",
2101
+ process: (bundleModels) => __async(null, null, function* () {
2102
+ const modelL = sourceL === "left" ? bundleModels.L : bundleModels.R;
2103
+ const modelR = sourceR === "left" ? bundleModels.L : bundleModels.R;
2104
+ let resultL;
2105
+ let resultR;
2106
+ if (modelL === modelR) {
2107
+ resultL = yield fetchDatasets(modelL, scenarioSpecL);
2108
+ resultR = yield fetchDatasets(modelR, scenarioSpecR);
2109
+ } else {
2110
+ const results = yield Promise.all([
2111
+ fetchDatasets(modelL, scenarioSpecL),
2112
+ fetchDatasets(modelR, scenarioSpecR)
2113
+ ]);
2114
+ resultL = results[0];
2115
+ resultR = results[1];
1893
2116
  }
2117
+ onResponse(resultL == null ? void 0 : resultL.datasetMap, resultR == null ? void 0 : resultR.datasetMap);
1894
2118
  })
1895
- });
1896
- }
1897
- processDatasetRequest(request) {
1898
- return __async(this, null, function* () {
1899
- function fetchDatasets(bundleModel, scenarioSpec) {
1900
- return __async(this, null, function* () {
1901
- if (scenarioSpec) {
1902
- return bundleModel.getDatasetsForScenario(scenarioSpec, request.datasetKeys);
1903
- } else {
1904
- return void 0;
1905
- }
1906
- });
1907
- }
1908
- const [resultL, resultR] = yield Promise.all([
1909
- fetchDatasets(this.bundleModelL, request.scenarioSpecL),
1910
- fetchDatasets(this.bundleModelR, request.scenarioSpecR)
1911
- ]);
1912
- return {
1913
- kind: "dataset",
1914
- datasetMapL: resultL == null ? void 0 : resultL.datasetMap,
1915
- datasetMapR: resultR == null ? void 0 : resultR.datasetMap
1916
- };
1917
- });
1918
- }
1919
- processGraphDataRequest(request) {
1920
- return __async(this, null, function* () {
1921
- function fetchGraphData(bundleModel, scenarioSpec) {
1922
- return __async(this, null, function* () {
1923
- if (scenarioSpec) {
1924
- return bundleModel.getGraphDataForScenario(scenarioSpec, request.graphId);
1925
- } else {
1926
- return void 0;
1927
- }
1928
- });
1929
- }
1930
- const [graphDataL, graphDataR] = yield Promise.all([
1931
- fetchGraphData(this.bundleModelL, request.scenarioSpecL),
1932
- fetchGraphData(this.bundleModelR, request.scenarioSpecR)
1933
- ]);
1934
- return {
1935
- kind: "graph-data",
1936
- graphDataL,
1937
- graphDataR
1938
- };
1939
- });
1940
- }
1941
- requestDatasetMaps(requestKey, scenarioSpecL, scenarioSpecR, datasetKeys, onResponse) {
1942
- const request = {
1943
- kind: "dataset",
1944
- scenarioSpecL,
1945
- scenarioSpecR,
1946
- datasetKeys
1947
2119
  };
1948
- this.taskQueue.addTask(requestKey, request, (response) => {
1949
- if (response.kind === "dataset") {
1950
- onResponse(response.datasetMapL, response.datasetMapR);
1951
- }
1952
- });
2120
+ this.taskQueue.addTask(task);
1953
2121
  }
1954
- requestGraphData(requestKey, scenarioSpecL, scenarioSpecR, graphId, onResponse) {
1955
- const request = {
1956
- kind: "graph-data",
1957
- scenarioSpecL,
1958
- scenarioSpecR,
1959
- graphId
2122
+ /**
2123
+ * Request graph data from the two models.
2124
+ *
2125
+ * @param requestKey The unique key for the request.
2126
+ * @param sourceL The source of the first ("left") dataset. If "left", the datasets will
2127
+ * be fetched from the "left" bundle's model, otherwise they will be fetched from the
2128
+ * "right" bundle's model.
2129
+ * @param scenarioSpecL The scenario used for the first ("left") model of the comparison.
2130
+ * @param sourceR The source of the second ("right") dataset. If "left", the datasets
2131
+ * will be fetched from the "left" bundle's model, otherwise they will be fetched from
2132
+ * the "right" bundle's model.
2133
+ * @param scenarioSpecR The scenario used for the second ("right") model of the comparison.
2134
+ * @param graphId The ID of the graph for which data will be fetched.
2135
+ * @param onResponse The callback that will be called with the graph data.
2136
+ */
2137
+ requestGraphData(requestKey, sourceL, scenarioSpecL, sourceR, scenarioSpecR, graphId, onResponse) {
2138
+ function fetchGraphData(bundleModel, scenarioSpec) {
2139
+ return __async(this, null, function* () {
2140
+ var _a;
2141
+ if (scenarioSpec) {
2142
+ return (_a = bundleModel.getGraphDataForScenario) == null ? void 0 : _a.call(bundleModel, scenarioSpec, graphId);
2143
+ } else {
2144
+ return void 0;
2145
+ }
2146
+ });
2147
+ }
2148
+ const task = {
2149
+ key: requestKey,
2150
+ kind: "comparison-data-coordinator",
2151
+ process: (bundleModels) => __async(null, null, function* () {
2152
+ const modelL = sourceL === "left" ? bundleModels.L : bundleModels.R;
2153
+ const modelR = sourceR === "left" ? bundleModels.L : bundleModels.R;
2154
+ let graphDataL;
2155
+ let graphDataR;
2156
+ if (modelL === modelR) {
2157
+ graphDataL = yield fetchGraphData(modelL, scenarioSpecL);
2158
+ graphDataR = yield fetchGraphData(modelR, scenarioSpecR);
2159
+ } else {
2160
+ const results = yield Promise.all([
2161
+ fetchGraphData(modelL, scenarioSpecL),
2162
+ fetchGraphData(modelR, scenarioSpecR)
2163
+ ]);
2164
+ graphDataL = results[0];
2165
+ graphDataR = results[1];
2166
+ }
2167
+ onResponse(graphDataL, graphDataR);
2168
+ })
1960
2169
  };
1961
- this.taskQueue.addTask(requestKey, request, (response) => {
1962
- if (response.kind === "graph-data") {
1963
- onResponse(response.graphDataL, response.graphDataR);
1964
- }
1965
- });
2170
+ this.taskQueue.addTask(task);
1966
2171
  }
1967
2172
  cancelRequest(key) {
1968
2173
  this.taskQueue.cancelTask(key);
1969
2174
  }
1970
2175
  };
2176
+ function createComparisonDataCoordinator() {
2177
+ return new ComparisonDataCoordinator(TaskQueue.getInstance());
2178
+ }
1971
2179
 
1972
2180
  // src/comparison/diff-datasets/diff-datasets.ts
1973
2181
  function diffDatasets(datasetL, datasetR) {
@@ -2117,14 +2325,14 @@ function diffGraphs(graphL, graphR, scenarioKey, testSummaries) {
2117
2325
 
2118
2326
  // src/comparison/report/comparison-reporting.ts
2119
2327
  function comparisonSummaryFromReport(comparisonReport) {
2328
+ var _a, _b;
2120
2329
  const terseSummaries = [];
2121
2330
  for (const r of comparisonReport.testReports) {
2122
- if (r.diffReport.validity === "both" && r.diffReport.maxDiff > 0) {
2123
- terseSummaries.push({
2124
- s: r.scenarioKey,
2125
- d: r.datasetKey,
2126
- md: r.diffReport.maxDiff
2127
- });
2331
+ const baselineMaxDiff = (_a = r.baselineDiffReport) == null ? void 0 : _a.maxDiff;
2332
+ const baselineAvgDiff = (_b = r.baselineDiffReport) == null ? void 0 : _b.avgDiff;
2333
+ const summary = testSummaryFromReport(r, baselineMaxDiff, baselineAvgDiff);
2334
+ if (summary) {
2335
+ terseSummaries.push(summary);
2128
2336
  }
2129
2337
  }
2130
2338
  return {
@@ -2133,6 +2341,43 @@ function comparisonSummaryFromReport(comparisonReport) {
2133
2341
  perfReportR: comparisonReport.perfReportR
2134
2342
  };
2135
2343
  }
2344
+ function testSummaryFromReport(r, baselineMaxDiff, baselineAvgDiff) {
2345
+ var _a;
2346
+ function baselineRelativeDiff(diffValue, baselineDiffValue) {
2347
+ if (baselineDiffValue !== void 0) {
2348
+ const epsilon = 1e-6;
2349
+ if (baselineDiffValue === 0) {
2350
+ baselineDiffValue = epsilon;
2351
+ }
2352
+ return diffValue / baselineDiffValue;
2353
+ } else {
2354
+ if (diffValue === 0) {
2355
+ return 0;
2356
+ } else {
2357
+ return 1;
2358
+ }
2359
+ }
2360
+ }
2361
+ if (r.diffReport === void 0) {
2362
+ return {
2363
+ s: r.scenarioKey,
2364
+ d: r.datasetKey
2365
+ };
2366
+ } else if (((_a = r.diffReport) == null ? void 0 : _a.validity) === "both" && r.diffReport.maxDiff > 0) {
2367
+ const maxDiffRelativeToBaseline = baselineRelativeDiff(r.diffReport.maxDiff, baselineMaxDiff);
2368
+ const avgDiffRelativeToBaseline = baselineRelativeDiff(r.diffReport.avgDiff, baselineAvgDiff);
2369
+ return {
2370
+ s: r.scenarioKey,
2371
+ d: r.datasetKey,
2372
+ md: r.diffReport.maxDiff,
2373
+ ad: r.diffReport.avgDiff,
2374
+ mdb: maxDiffRelativeToBaseline,
2375
+ adb: avgDiffRelativeToBaseline
2376
+ };
2377
+ } else {
2378
+ return void 0;
2379
+ }
2380
+ }
2136
2381
  function restoreFromTerseSummaries(comparisonConfig, terseSummaries) {
2137
2382
  const existingSummaries = /* @__PURE__ */ new Map();
2138
2383
  for (const summary of terseSummaries) {
@@ -2145,24 +2390,33 @@ function restoreFromTerseSummaries(comparisonConfig, terseSummaries) {
2145
2390
  for (const datasetKey of datasetKeys) {
2146
2391
  const key = `${scenario.key}::${datasetKey}`;
2147
2392
  const existingSummary = existingSummaries.get(key);
2148
- const maxDiff = (existingSummary == null ? void 0 : existingSummary.md) || 0;
2149
- allTestSummaries.push({
2150
- s: scenario.key,
2151
- d: datasetKey,
2152
- md: maxDiff
2153
- });
2393
+ if (existingSummary) {
2394
+ allTestSummaries.push(existingSummary);
2395
+ } else {
2396
+ allTestSummaries.push({
2397
+ s: scenario.key,
2398
+ d: datasetKey,
2399
+ md: 0,
2400
+ ad: 0,
2401
+ mdb: 0,
2402
+ adb: 0
2403
+ });
2404
+ }
2154
2405
  }
2155
2406
  }
2156
2407
  return allTestSummaries;
2157
2408
  }
2158
2409
 
2159
2410
  // src/comparison/report/buckets.ts
2160
- function getBucketIndex(diffPct, thresholds) {
2161
- if (diffPct === 0) {
2411
+ function getBucketIndex(diff, thresholds) {
2412
+ if (diff === void 0) {
2413
+ return thresholds.length + 2;
2414
+ }
2415
+ if (diff === 0) {
2162
2416
  return 0;
2163
2417
  }
2164
2418
  for (let i = 0; i < thresholds.length; i++) {
2165
- if (diffPct < thresholds[i]) {
2419
+ if (diff < thresholds[i]) {
2166
2420
  return i + 1;
2167
2421
  }
2168
2422
  }
@@ -2170,14 +2424,32 @@ function getBucketIndex(diffPct, thresholds) {
2170
2424
  }
2171
2425
 
2172
2426
  // src/comparison/report/comparison-group-scores.ts
2173
- function getScoresForTestSummaries(testSummaries, thresholds) {
2174
- const diffCountByBucket = Array(thresholds.length + 2).fill(0);
2175
- const totalMaxDiffByBucket = Array(thresholds.length + 2).fill(0);
2427
+ function getScoresForTestSummaries(testSummaries, thresholds, sortMode) {
2428
+ const diffCountByBucket = Array(thresholds.length + 3).fill(0);
2429
+ const totalDiffByBucket = Array(thresholds.length + 3).fill(0);
2176
2430
  let totalDiffCount = 0;
2431
+ let valueKey;
2432
+ switch (sortMode) {
2433
+ case "max-diff":
2434
+ valueKey = "md";
2435
+ break;
2436
+ case "avg-diff":
2437
+ valueKey = "ad";
2438
+ break;
2439
+ case "max-diff-relative":
2440
+ valueKey = "mdb";
2441
+ break;
2442
+ case "avg-diff-relative":
2443
+ valueKey = "adb";
2444
+ break;
2445
+ }
2177
2446
  for (const testSummary of testSummaries) {
2178
- const bucketIndex = getBucketIndex(testSummary.md, thresholds);
2447
+ const value = testSummary[valueKey];
2448
+ const bucketIndex = getBucketIndex(value, thresholds);
2179
2449
  diffCountByBucket[bucketIndex]++;
2180
- totalMaxDiffByBucket[bucketIndex] += testSummary.md;
2450
+ if (value !== void 0) {
2451
+ totalDiffByBucket[bucketIndex] += value;
2452
+ }
2181
2453
  totalDiffCount++;
2182
2454
  }
2183
2455
  let diffPercentByBucket;
@@ -2188,20 +2460,20 @@ function getScoresForTestSummaries(testSummaries, thresholds) {
2188
2460
  }
2189
2461
  return {
2190
2462
  totalDiffCount,
2191
- totalMaxDiffByBucket,
2463
+ totalDiffByBucket,
2192
2464
  diffCountByBucket,
2193
2465
  diffPercentByBucket
2194
2466
  };
2195
2467
  }
2196
2468
 
2197
2469
  // src/comparison/report/comparison-grouping.ts
2198
- var import_assert_never8 = require("assert-never");
2199
- function categorizeComparisonTestSummaries(comparisonConfig, terseSummaries) {
2470
+ var import_assert_never7 = require("assert-never");
2471
+ function categorizeComparisonTestSummaries(comparisonConfig, terseSummaries, sortMode) {
2200
2472
  const allTestSummaries = restoreFromTerseSummaries(comparisonConfig, terseSummaries);
2201
2473
  const groupsByScenario = groupComparisonTestSummaries(allTestSummaries, "by-scenario");
2202
- const byScenario = categorizeComparisonGroups(comparisonConfig, [...groupsByScenario.values()]);
2474
+ const byScenario = categorizeComparisonGroups(comparisonConfig, [...groupsByScenario.values()], sortMode);
2203
2475
  const groupsByDataset = groupComparisonTestSummaries(allTestSummaries, "by-dataset");
2204
- const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()]);
2476
+ const byDataset = categorizeComparisonGroups(comparisonConfig, [...groupsByDataset.values()], sortMode);
2205
2477
  return {
2206
2478
  allTestSummaries,
2207
2479
  byScenario,
@@ -2220,7 +2492,7 @@ function groupComparisonTestSummaries(testSummaries, groupKind) {
2220
2492
  groupKey = testSummary.s;
2221
2493
  break;
2222
2494
  default:
2223
- (0, import_assert_never8.assertNever)(groupKind);
2495
+ (0, import_assert_never7.assertNever)(groupKind);
2224
2496
  }
2225
2497
  const group = groups.get(groupKey);
2226
2498
  if (group) {
@@ -2235,7 +2507,7 @@ function groupComparisonTestSummaries(testSummaries, groupKind) {
2235
2507
  }
2236
2508
  return groups;
2237
2509
  }
2238
- function categorizeComparisonGroups(comparisonConfig, allGroups) {
2510
+ function categorizeComparisonGroups(comparisonConfig, allGroups, sortMode) {
2239
2511
  const allGroupSummaries = /* @__PURE__ */ new Map();
2240
2512
  const withErrors = [];
2241
2513
  const onlyInLeft = [];
@@ -2245,7 +2517,9 @@ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2245
2517
  function addSummaryForGroup(group, root, validInL, validInR) {
2246
2518
  let scores;
2247
2519
  if (validInL && validInR) {
2248
- scores = getScoresForTestSummaries(group.testSummaries, comparisonConfig.thresholds);
2520
+ const isRelativeMode = sortMode === "max-diff-relative" || sortMode === "avg-diff-relative";
2521
+ const thresholds = isRelativeMode ? comparisonConfig.ratioThresholds : comparisonConfig.thresholds;
2522
+ scores = getScoresForTestSummaries(group.testSummaries, thresholds, sortMode);
2249
2523
  }
2250
2524
  const groupSummary = {
2251
2525
  root,
@@ -2254,7 +2528,9 @@ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2254
2528
  };
2255
2529
  allGroupSummaries.set(group.key, groupSummary);
2256
2530
  if (validInL && validInR) {
2257
- if (scores.totalDiffCount !== scores.diffCountByBucket[0]) {
2531
+ const noDiffCount = scores.diffCountByBucket[0];
2532
+ const skippedCount = scores.diffCountByBucket[5];
2533
+ if (scores.totalDiffCount !== noDiffCount + skippedCount) {
2258
2534
  withDiffs.push(groupSummary);
2259
2535
  } else {
2260
2536
  withoutDiffs.push(groupSummary);
@@ -2284,7 +2560,7 @@ function categorizeComparisonGroups(comparisonConfig, allGroups) {
2284
2560
  break;
2285
2561
  }
2286
2562
  default:
2287
- (0, import_assert_never8.assertNever)(group.kind);
2563
+ (0, import_assert_never7.assertNever)(group.kind);
2288
2564
  }
2289
2565
  }
2290
2566
  if (withDiffs.length > 1) {
@@ -2346,13 +2622,13 @@ function sortScenarioGroupSummaries(summaries) {
2346
2622
  });
2347
2623
  }
2348
2624
  function compareScores(a, b) {
2349
- if (a.totalMaxDiffByBucket.length !== b.totalMaxDiffByBucket.length) {
2625
+ if (a.totalDiffByBucket.length !== b.totalDiffByBucket.length) {
2350
2626
  return 0;
2351
2627
  }
2352
- const len = a.totalMaxDiffByBucket.length;
2628
+ const len = a.totalDiffByBucket.length;
2353
2629
  for (let i = len - 1; i >= 0; i--) {
2354
- const aTotal = a.totalMaxDiffByBucket[i];
2355
- const bTotal = b.totalMaxDiffByBucket[i];
2630
+ const aTotal = a.totalDiffByBucket[i];
2631
+ const bTotal = b.totalDiffByBucket[i];
2356
2632
  if (aTotal > bTotal) {
2357
2633
  return 1;
2358
2634
  } else if (aTotal < bTotal) {
@@ -2364,7 +2640,7 @@ function compareScores(a, b) {
2364
2640
 
2365
2641
  // src/comparison/config/parse/comparison-parser.ts
2366
2642
  var import_ajv2 = __toESM(require("ajv"), 1);
2367
- var import_assert_never9 = __toESM(require("assert-never"), 1);
2643
+ var import_assert_never8 = __toESM(require("assert-never"), 1);
2368
2644
  var import_neverthrow2 = require("neverthrow");
2369
2645
  var import_yaml2 = __toESM(require("yaml"), 1);
2370
2646
 
@@ -2900,7 +3176,7 @@ function parseComparisonSpecs(specSource) {
2900
3176
  parsed = import_yaml2.default.parse(specSource.content);
2901
3177
  break;
2902
3178
  default:
2903
- (0, import_assert_never9.default)(specSource.kind);
3179
+ (0, import_assert_never8.default)(specSource.kind);
2904
3180
  }
2905
3181
  if (validate(parsed)) {
2906
3182
  for (const specItem of parsed) {
@@ -3127,7 +3403,7 @@ function viewGroupSpecFromParsed(parsedViewGroup) {
3127
3403
  }
3128
3404
 
3129
3405
  // src/comparison/config/resolve/comparison-resolver.ts
3130
- var import_assert_never11 = require("assert-never");
3406
+ var import_assert_never10 = require("assert-never");
3131
3407
 
3132
3408
  // src/bundle/model-inputs.ts
3133
3409
  var ModelInputs = class {
@@ -3190,7 +3466,7 @@ var ModelInputs = class {
3190
3466
  };
3191
3467
 
3192
3468
  // src/comparison/config/resolve/comparison-scenario-specs.ts
3193
- var import_assert_never10 = require("assert-never");
3469
+ var import_assert_never9 = require("assert-never");
3194
3470
  function scenarioSpecsFromSettings(settings) {
3195
3471
  switch (settings.kind) {
3196
3472
  case "all-inputs-settings": {
@@ -3203,7 +3479,7 @@ function scenarioSpecsFromSettings(settings) {
3203
3479
  return [specL, specR];
3204
3480
  }
3205
3481
  default:
3206
- (0, import_assert_never10.assertNever)(settings);
3482
+ (0, import_assert_never9.assertNever)(settings);
3207
3483
  }
3208
3484
  }
3209
3485
  function scenarioSpecFromInputs(inputs, side) {
@@ -3422,7 +3698,7 @@ function resolveScenariosFromSpec(modelInputsL, modelInputsR, scenarioSpec, genK
3422
3698
  ];
3423
3699
  }
3424
3700
  default:
3425
- (0, import_assert_never11.assertNever)(scenarioSpec);
3701
+ (0, import_assert_never10.assertNever)(scenarioSpec);
3426
3702
  }
3427
3703
  }
3428
3704
  function resolveScenarioMatrix(modelInputsL, modelInputsR, genKey) {
@@ -3478,7 +3754,7 @@ function resolveScenarioForInputSpecs(modelInputsL, modelInputsR, key, id, title
3478
3754
  case "input-at-value":
3479
3755
  return resolveInputForName(modelInputsL, modelInputsR, inputSpec.inputName, inputSpec.value);
3480
3756
  default:
3481
- (0, import_assert_never11.assertNever)(inputSpec);
3757
+ (0, import_assert_never10.assertNever)(inputSpec);
3482
3758
  }
3483
3759
  });
3484
3760
  const settings = {
@@ -3511,7 +3787,7 @@ function resolveScenarioForDistinctInputSpecs(modelInputsL, modelInputsR, key, i
3511
3787
  inputState = resolveInputForNameInModel(modelInputs, inputSpec.inputName, inputSpec.value);
3512
3788
  break;
3513
3789
  default:
3514
- (0, import_assert_never11.assertNever)(inputSpec);
3790
+ (0, import_assert_never10.assertNever)(inputSpec);
3515
3791
  }
3516
3792
  if (inputState.error !== void 0) {
3517
3793
  inputsWithErrors.push({
@@ -3727,7 +4003,7 @@ function inputValueAtPosition2(inputVar, position) {
3727
4003
  case "at-maximum":
3728
4004
  return inputVar.maxValue;
3729
4005
  default:
3730
- (0, import_assert_never11.assertNever)(position);
4006
+ (0, import_assert_never10.assertNever)(position);
3731
4007
  }
3732
4008
  }
3733
4009
  var ResolvedScenarioGroups = class {
@@ -3786,7 +4062,7 @@ function resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, grap
3786
4062
  return [...graphIds];
3787
4063
  }
3788
4064
  default:
3789
- (0, import_assert_never11.assertNever)(graphsSpec.preset);
4065
+ (0, import_assert_never10.assertNever)(graphsSpec.preset);
3790
4066
  }
3791
4067
  }
3792
4068
  // eslint-disable-next-line no-fallthrough
@@ -3800,7 +4076,7 @@ function resolveGraphsFromSpec(modelSpecL, modelSpecR, resolvedGraphGroups, grap
3800
4076
  return groupSpec.graphIds;
3801
4077
  }
3802
4078
  default:
3803
- (0, import_assert_never11.assertNever)(graphsSpec);
4079
+ (0, import_assert_never10.assertNever)(graphsSpec);
3804
4080
  }
3805
4081
  }
3806
4082
  function resolveViewForScenarioId(resolvedScenarios, viewTitle, viewSubtitle, scenarioId, graphIds, graphOrder) {
@@ -3974,7 +4250,7 @@ function resolveViewGroupFromSpec(modelSpecL, modelSpecR, modelOutputs, resolved
3974
4250
  views.push(resolveViewForScenario(void 0, void 0, scenario, graphIds, graphOrder));
3975
4251
  break;
3976
4252
  default:
3977
- (0, import_assert_never11.assertNever)(scenario);
4253
+ (0, import_assert_never10.assertNever)(scenario);
3978
4254
  }
3979
4255
  }
3980
4256
  } else {
@@ -3983,13 +4259,13 @@ function resolveViewGroupFromSpec(modelSpecL, modelSpecR, modelOutputs, resolved
3983
4259
  break;
3984
4260
  }
3985
4261
  default:
3986
- (0, import_assert_never11.assertNever)(refSpec);
4262
+ (0, import_assert_never10.assertNever)(refSpec);
3987
4263
  }
3988
4264
  }
3989
4265
  break;
3990
4266
  }
3991
4267
  default:
3992
- (0, import_assert_never11.assertNever)(viewGroupSpec);
4268
+ (0, import_assert_never10.assertNever)(viewGroupSpec);
3993
4269
  }
3994
4270
  return {
3995
4271
  kind: "view-group",
@@ -4052,9 +4328,9 @@ var ComparisonDatasetsImpl = class {
4052
4328
  }
4053
4329
  const allOutputVarKeysSet = /* @__PURE__ */ new Set();
4054
4330
  const modelOutputVarKeysSet = /* @__PURE__ */ new Set();
4055
- function addOutputVars(outputVars, handleRenames) {
4331
+ function addOutputVars(outputVars, handleRenames2) {
4056
4332
  outputVars.forEach((outputVar, key) => {
4057
- const remappedKey = handleRenames ? leftKeyForRightKey(key) : key;
4333
+ const remappedKey = handleRenames2 ? leftKeyForRightKey(key) : key;
4058
4334
  allOutputVarKeysSet.add(remappedKey);
4059
4335
  if (outputVar.sourceName === void 0) {
4060
4336
  modelOutputVarKeysSet.add(remappedKey);
@@ -4168,109 +4444,41 @@ var ComparisonScenariosImpl = class {
4168
4444
  }
4169
4445
  };
4170
4446
 
4171
- // src/config/synchronized-model.ts
4172
- function synchronizedBundleModel(sourceModel) {
4173
- const promiseQueue = new PromiseQueue();
4174
- return {
4175
- modelSpec: sourceModel.modelSpec,
4176
- getDatasetsForScenario: (scenarioSpec, datasetKeys) => {
4177
- return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenarioSpec, datasetKeys));
4178
- },
4179
- getGraphDataForScenario: (scenarioSpec, graphId) => {
4180
- return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenarioSpec, graphId));
4181
- },
4182
- getGraphLinksForScenario: sourceModel.getGraphLinksForScenario.bind(sourceModel)
4183
- };
4184
- }
4185
- var PromiseQueue = class {
4186
- constructor() {
4187
- this.tasks = [];
4188
- this.runningCount = 0;
4189
- }
4190
- add(f) {
4191
- return new Promise((resolve, reject) => {
4192
- const run = () => __async(this, null, function* () {
4193
- this.runningCount++;
4194
- const promise = f();
4195
- try {
4196
- const result = yield promise;
4197
- resolve(result);
4198
- } catch (e) {
4199
- reject(e);
4200
- } finally {
4201
- this.runningCount--;
4202
- this.runNext();
4203
- }
4204
- });
4205
- if (this.runningCount < 1) {
4206
- run();
4207
- } else {
4208
- this.tasks.push(run);
4209
- }
4210
- });
4211
- }
4212
- runNext() {
4213
- if (this.tasks.length > 0) {
4214
- const task = this.tasks.shift();
4215
- if (task) {
4216
- task();
4217
- }
4218
- }
4219
- }
4220
- };
4221
-
4222
4447
  // src/config/config.ts
4223
4448
  function createConfig(options) {
4224
4449
  return __async(this, null, function* () {
4225
- var _a;
4226
- const origCurrentBundle = yield loadSynchronized(options.current);
4450
+ var _a, _b, _c, _d, _e;
4451
+ let concurrentModels;
4452
+ if (options.concurrency === void 0) {
4453
+ concurrentModels = 1;
4454
+ } else if (options.concurrency === 0) {
4455
+ let coreCount;
4456
+ if (typeof navigator !== "undefined") {
4457
+ coreCount = navigator.hardwareConcurrency;
4458
+ }
4459
+ if (coreCount === void 0 || coreCount < 1) {
4460
+ coreCount = 1;
4461
+ }
4462
+ concurrentModels = Math.max(1, Math.floor(coreCount / 2));
4463
+ } else {
4464
+ concurrentModels = Math.max(1, options.concurrency);
4465
+ }
4466
+ const origCurrentBundle = yield loadBundle(options.current, concurrentModels);
4227
4467
  let currentBundle;
4228
4468
  let comparisonConfig;
4229
4469
  if (options.comparison === void 0) {
4230
4470
  currentBundle = origCurrentBundle;
4231
4471
  } else {
4232
- const baselineBundle = yield loadSynchronized(options.comparison.baseline);
4233
- const renamedDatasetKeys = (_a = options.comparison.datasets) == null ? void 0 : _a.renamedDatasetKeys;
4234
- const invertedRenamedKeys = /* @__PURE__ */ new Map();
4235
- renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
4236
- invertedRenamedKeys.set(newKey, oldKey);
4237
- });
4238
- const rightKeyForLeftKey = (leftKey) => {
4239
- return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
4240
- };
4241
- const leftKeyForRightKey = (rightKey) => {
4242
- return invertedRenamedKeys.get(rightKey) || rightKey;
4243
- };
4244
- const origBundleModelR = origCurrentBundle.model;
4245
- const adjBundleModelR = {
4246
- modelSpec: origBundleModelR.modelSpec,
4247
- getDatasetsForScenario: (scenarioSpec, datasetKeys) => __async(this, null, function* () {
4248
- const rightKeys = datasetKeys.map(rightKeyForLeftKey);
4249
- const result = yield origBundleModelR.getDatasetsForScenario(scenarioSpec, rightKeys);
4250
- const mapWithRightKeys = result.datasetMap;
4251
- const mapWithLeftKeys = /* @__PURE__ */ new Map();
4252
- for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
4253
- const leftKey = leftKeyForRightKey(rightKey);
4254
- mapWithLeftKeys.set(leftKey, dataset);
4255
- }
4256
- return {
4257
- datasetMap: mapWithLeftKeys,
4258
- modelRunTime: result.modelRunTime
4259
- };
4260
- }),
4261
- getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
4262
- getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
4263
- };
4264
- currentBundle = __spreadProps(__spreadValues({}, origCurrentBundle), {
4265
- model: adjBundleModelR
4266
- });
4267
- const modelSpecL = baselineBundle.model.modelSpec;
4268
- const modelSpecR = currentBundle.model.modelSpec;
4472
+ const baselineBundle = yield loadBundle(options.comparison.baseline, concurrentModels);
4473
+ currentBundle = handleRenames(origCurrentBundle, (_a = options.comparison.datasets) == null ? void 0 : _a.renamedDatasetKeys);
4474
+ const modelSpecL = baselineBundle.modelSpec;
4475
+ const modelSpecR = currentBundle.modelSpec;
4269
4476
  const comparisonDefs = resolveComparisonSpecsFromSources(modelSpecL, modelSpecR, options.comparison.specs);
4270
4477
  comparisonConfig = {
4271
4478
  bundleL: baselineBundle,
4272
4479
  bundleR: currentBundle,
4273
- thresholds: options.comparison.thresholds,
4480
+ thresholds: (_b = options.comparison.thresholds) != null ? _b : [1, 5, 10],
4481
+ ratioThresholds: (_c = options.comparison.ratioThresholds) != null ? _c : [1, 2, 3],
4274
4482
  scenarios: getComparisonScenarios(comparisonDefs.scenarios),
4275
4483
  datasets: getComparisonDatasets(modelSpecL, modelSpecR, options.comparison.datasets),
4276
4484
  viewGroups: comparisonDefs.viewGroups,
@@ -4281,26 +4489,77 @@ function createConfig(options) {
4281
4489
  bundle: currentBundle,
4282
4490
  tests: options.check.tests
4283
4491
  };
4492
+ const executors = /* @__PURE__ */ new Map();
4493
+ for (let i = 0; i < checkConfig.bundle.models.length; i++) {
4494
+ const bundleModelL = (_d = comparisonConfig == null ? void 0 : comparisonConfig.bundleL.models) == null ? void 0 : _d[i];
4495
+ const bundleModelR = ((_e = comparisonConfig == null ? void 0 : comparisonConfig.bundleR.models) == null ? void 0 : _e[i]) || checkConfig.bundle.models[i];
4496
+ const executor = createExecutor(bundleModelL, bundleModelR);
4497
+ executors.set(`executor-${i}`, executor);
4498
+ }
4499
+ TaskQueue.initialize(executors);
4284
4500
  return {
4285
4501
  check: checkConfig,
4286
4502
  comparison: comparisonConfig
4287
4503
  };
4288
4504
  });
4289
4505
  }
4290
- function loadSynchronized(sourceBundle) {
4506
+ function loadBundle(bundle, concurrentModels) {
4291
4507
  return __async(this, null, function* () {
4292
- const sourceModel = yield sourceBundle.bundle.initModel();
4293
- const synchronizedModel = synchronizedBundleModel(sourceModel);
4508
+ const initCalls = Array.from({ length: concurrentModels }, () => bundle.bundle.initModel());
4509
+ const models = yield Promise.all(initCalls);
4294
4510
  return {
4295
- name: sourceBundle.name,
4296
- version: sourceBundle.bundle.version,
4297
- model: synchronizedModel
4511
+ name: bundle.name,
4512
+ version: bundle.bundle.version,
4513
+ modelSpec: bundle.bundle.modelSpec,
4514
+ models
4515
+ };
4516
+ });
4517
+ }
4518
+ function handleRenames(origCurrentBundle, renamedDatasetKeys) {
4519
+ if (renamedDatasetKeys === void 0 || renamedDatasetKeys.size === 0) {
4520
+ return origCurrentBundle;
4521
+ }
4522
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
4523
+ renamedDatasetKeys.forEach((newKey, oldKey) => {
4524
+ invertedRenamedKeys.set(newKey, oldKey);
4525
+ });
4526
+ const rightKeyForLeftKey = (leftKey) => {
4527
+ return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
4528
+ };
4529
+ const leftKeyForRightKey = (rightKey) => {
4530
+ return invertedRenamedKeys.get(rightKey) || rightKey;
4531
+ };
4532
+ function wrapModel(origBundleModelR) {
4533
+ var _a, _b, _c;
4534
+ return {
4535
+ modelSpec: origBundleModelR.modelSpec,
4536
+ getDatasetsForScenario: (scenarioSpec, datasetKeys) => __async(null, null, function* () {
4537
+ const rightKeys = datasetKeys.map(rightKeyForLeftKey);
4538
+ const result = yield origBundleModelR.getDatasetsForScenario(scenarioSpec, rightKeys);
4539
+ const mapWithRightKeys = result.datasetMap;
4540
+ const mapWithLeftKeys = /* @__PURE__ */ new Map();
4541
+ for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
4542
+ const leftKey = leftKeyForRightKey(rightKey);
4543
+ mapWithLeftKeys.set(leftKey, dataset);
4544
+ }
4545
+ return {
4546
+ datasetMap: mapWithLeftKeys,
4547
+ modelRunTime: result.modelRunTime
4548
+ };
4549
+ }),
4550
+ getGraphDataForScenario: (_a = origBundleModelR.getGraphDataForScenario) == null ? void 0 : _a.bind(origBundleModelR),
4551
+ getGraphLinksForScenario: (_b = origBundleModelR.getGraphLinksForScenario) == null ? void 0 : _b.bind(origBundleModelR),
4552
+ createGraphView: (_c = origBundleModelR.createGraphView) == null ? void 0 : _c.bind(origBundleModelR)
4298
4553
  };
4554
+ }
4555
+ const wrappedModels = origCurrentBundle.models.map(wrapModel);
4556
+ return __spreadProps(__spreadValues({}, origCurrentBundle), {
4557
+ models: wrappedModels
4299
4558
  });
4300
4559
  }
4301
4560
 
4302
4561
  // src/perf/perf-runner.ts
4303
- var import_assert_never12 = require("assert-never");
4562
+ var import_assert_never11 = require("assert-never");
4304
4563
 
4305
4564
  // src/perf/perf-stats.ts
4306
4565
  var PerfStats = class {
@@ -4337,80 +4596,110 @@ var PerfStats = class {
4337
4596
  };
4338
4597
 
4339
4598
  // src/perf/perf-runner.ts
4340
- var warmupCount = 5;
4341
- var runCount = 100;
4599
+ function runPerfWithTaskQueue(taskQueue, callbacks, options) {
4600
+ const perfRunner = new PerfRunner(taskQueue, callbacks, options);
4601
+ perfRunner.start();
4602
+ return () => {
4603
+ perfRunner.cancel();
4604
+ };
4605
+ }
4606
+ function runPerf(callbacks, options) {
4607
+ const taskQueue = TaskQueue.getInstance();
4608
+ return runPerfWithTaskQueue(taskQueue, callbacks, options);
4609
+ }
4342
4610
  var PerfRunner = class {
4343
- constructor(bundleModelL, bundleModelR, mode = "serial") {
4344
- this.bundleModelL = bundleModelL;
4345
- this.bundleModelR = bundleModelR;
4346
- this.mode = mode;
4347
- const scenarioSpec = allInputsAtPositionSpec("at-default");
4348
- this.taskQueue = new TaskQueue({
4349
- process: (request) => __async(this, null, function* () {
4350
- switch (request.kind) {
4351
- case "left": {
4352
- const result = yield bundleModelL.getDatasetsForScenario(scenarioSpec, []);
4353
- return {
4354
- runTimeL: result.modelRunTime
4355
- };
4356
- }
4357
- case "right": {
4358
- const result = yield bundleModelR.getDatasetsForScenario(scenarioSpec, []);
4359
- return {
4360
- runTimeR: result.modelRunTime
4361
- };
4362
- }
4363
- case "both": {
4364
- const [resultL, resultR] = yield Promise.all([
4365
- bundleModelL.getDatasetsForScenario(scenarioSpec, []),
4366
- bundleModelR.getDatasetsForScenario(scenarioSpec, [])
4367
- ]);
4368
- return {
4369
- runTimeL: resultL.modelRunTime,
4370
- runTimeR: resultR.modelRunTime
4371
- };
4372
- }
4373
- default:
4374
- (0, import_assert_never12.assertNever)(request.kind);
4375
- }
4376
- })
4377
- });
4611
+ constructor(taskQueue, callbacks, options) {
4612
+ this.taskQueue = taskQueue;
4613
+ this.callbacks = callbacks;
4614
+ this.options = options;
4615
+ this.pendingTaskKeys = /* @__PURE__ */ new Set();
4616
+ this.stopped = false;
4617
+ }
4618
+ cancel() {
4619
+ if (!this.stopped) {
4620
+ for (const taskKey of this.pendingTaskKeys) {
4621
+ this.taskQueue.cancelTask(taskKey);
4622
+ }
4623
+ this.stopped = true;
4624
+ }
4378
4625
  }
4379
4626
  start() {
4627
+ var _a, _b, _c, _d, _e, _f;
4380
4628
  const statsL = new PerfStats();
4381
4629
  const statsR = new PerfStats();
4382
- this.taskQueue.onIdle = (error) => {
4383
- var _a;
4384
- if (error) {
4385
- this.onError(error);
4386
- } else {
4387
- (_a = this.onComplete) == null ? void 0 : _a.call(this, statsL.toReport(), statsR.toReport());
4388
- }
4389
- };
4390
- const taskQueue = this.taskQueue;
4391
- function addTask(index, warmup, kind) {
4392
- const key = `${warmup ? "warmup-" : ""}${kind}-${index}`;
4393
- const request = {
4394
- kind
4395
- };
4396
- taskQueue.addTask(key, request, (response) => {
4397
- if (!warmup && response.runTimeL !== void 0) {
4398
- statsL.addRun(response.runTimeL);
4399
- }
4400
- if (!warmup && response.runTimeR !== void 0) {
4401
- statsR.addRun(response.runTimeR);
4402
- }
4403
- });
4630
+ const scenarioSpec = allInputsAtPositionSpec("at-default");
4631
+ const warmupCount = (_b = (_a = this.options) == null ? void 0 : _a.warmupCount) != null ? _b : 5;
4632
+ const runCount = (_d = (_c = this.options) == null ? void 0 : _c.runCount) != null ? _d : 100;
4633
+ let totalTasks = 0;
4634
+ if (((_e = this.options) == null ? void 0 : _e.mode) === "parallel") {
4635
+ totalTasks = warmupCount + runCount;
4636
+ } else {
4637
+ totalTasks = (warmupCount + runCount) * 2;
4404
4638
  }
4639
+ let tasksCompleted = 0;
4640
+ let perfTaskId = 1;
4641
+ const addTask = (warmup, kind) => {
4642
+ const task = {
4643
+ key: `perf-runner-${perfTaskId++}`,
4644
+ kind: "perf-runner",
4645
+ process: (bundleModels) => __async(this, null, function* () {
4646
+ var _a2, _b2, _c2, _d2;
4647
+ this.pendingTaskKeys.delete(task.key);
4648
+ try {
4649
+ let runTimeL;
4650
+ let runTimeR;
4651
+ switch (kind) {
4652
+ case "left": {
4653
+ const result = yield bundleModels.L.getDatasetsForScenario(scenarioSpec, []);
4654
+ runTimeL = result.modelRunTime;
4655
+ break;
4656
+ }
4657
+ case "right": {
4658
+ const result = yield bundleModels.R.getDatasetsForScenario(scenarioSpec, []);
4659
+ runTimeR = result.modelRunTime;
4660
+ break;
4661
+ }
4662
+ case "both": {
4663
+ const [resultL, resultR] = yield Promise.all([
4664
+ bundleModels.L.getDatasetsForScenario(scenarioSpec, []),
4665
+ bundleModels.R.getDatasetsForScenario(scenarioSpec, [])
4666
+ ]);
4667
+ runTimeL = resultL.modelRunTime;
4668
+ runTimeR = resultR.modelRunTime;
4669
+ break;
4670
+ }
4671
+ default:
4672
+ (0, import_assert_never11.assertNever)(kind);
4673
+ }
4674
+ if (!warmup) {
4675
+ if (runTimeL !== void 0) {
4676
+ statsL.addRun(runTimeL);
4677
+ }
4678
+ if (runTimeR !== void 0) {
4679
+ statsR.addRun(runTimeR);
4680
+ }
4681
+ }
4682
+ tasksCompleted++;
4683
+ if (tasksCompleted === totalTasks) {
4684
+ (_b2 = (_a2 = this.callbacks).onComplete) == null ? void 0 : _b2.call(_a2, statsL.toReport(), statsR.toReport());
4685
+ }
4686
+ } catch (error) {
4687
+ (_d2 = (_c2 = this.callbacks).onError) == null ? void 0 : _d2.call(_c2, error);
4688
+ }
4689
+ })
4690
+ };
4691
+ this.taskQueue.addTask(task);
4692
+ this.pendingTaskKeys.add(task.key);
4693
+ };
4405
4694
  function addTasks(kind) {
4406
4695
  for (let i = 0; i < warmupCount; i++) {
4407
- addTask(i, true, kind);
4696
+ addTask(true, kind);
4408
4697
  }
4409
4698
  for (let i = 0; i < runCount; i++) {
4410
- addTask(i, false, kind);
4699
+ addTask(false, kind);
4411
4700
  }
4412
4701
  }
4413
- if (this.mode === "parallel") {
4702
+ if (((_f = this.options) == null ? void 0 : _f.mode) === "parallel") {
4414
4703
  addTasks("both");
4415
4704
  } else {
4416
4705
  addTasks("left");
@@ -4419,6 +4708,303 @@ var PerfRunner = class {
4419
4708
  }
4420
4709
  };
4421
4710
 
4711
+ // src/trace/trace-runner.ts
4712
+ var import_assert_never12 = require("assert-never");
4713
+ function runTraceWithTaskQueue(modelSpec, taskQueue, callbacks, options) {
4714
+ const traceRunner = new TraceRunner(taskQueue, callbacks);
4715
+ traceRunner.start(modelSpec, options);
4716
+ return () => {
4717
+ traceRunner.cancel();
4718
+ };
4719
+ }
4720
+ function runTrace(modelSpec, callbacks, options) {
4721
+ const taskQueue = TaskQueue.getInstance();
4722
+ return runTraceWithTaskQueue(modelSpec, taskQueue, callbacks, options);
4723
+ }
4724
+ var TraceRunner = class {
4725
+ constructor(taskQueue, callbacks) {
4726
+ this.taskQueue = taskQueue;
4727
+ this.callbacks = callbacks;
4728
+ this.pendingTaskKeys = /* @__PURE__ */ new Set();
4729
+ this.stopped = false;
4730
+ }
4731
+ cancel() {
4732
+ if (!this.stopped) {
4733
+ for (const taskKey of this.pendingTaskKeys) {
4734
+ this.taskQueue.cancelTask(taskKey);
4735
+ }
4736
+ this.stopped = true;
4737
+ }
4738
+ }
4739
+ start(modelSpec, options) {
4740
+ const allDatasetKeys = [...modelSpec.implVars.keys()];
4741
+ const traceRequests = [];
4742
+ const batchSize = 2e3;
4743
+ for (let i = 0; i < allDatasetKeys.length; i += batchSize) {
4744
+ const datasetKeysForBatch = allDatasetKeys.slice(i, i + batchSize);
4745
+ switch (options.kind) {
4746
+ case "compare-to-bundle":
4747
+ traceRequests.push({
4748
+ kind: "compare-to-bundle",
4749
+ datasetKeys: datasetKeysForBatch,
4750
+ bundleSide0: options.bundleSide0,
4751
+ scenarioSpec0: options.scenarioSpec0,
4752
+ bundleSide1: options.bundleSide1,
4753
+ scenarioSpec1: options.scenarioSpec1
4754
+ });
4755
+ break;
4756
+ case "compare-to-ext-data":
4757
+ traceRequests.push({
4758
+ kind: "compare-to-ext-data",
4759
+ datasetKeys: datasetKeysForBatch,
4760
+ extData: options.extData,
4761
+ bundleSide: options.bundleSide,
4762
+ scenarioSpec: options.scenarioSpec
4763
+ });
4764
+ break;
4765
+ default:
4766
+ (0, import_assert_never12.assertNever)(options);
4767
+ }
4768
+ }
4769
+ const allDatasetReports = /* @__PURE__ */ new Map();
4770
+ const taskCount = traceRequests.length;
4771
+ let tasksCompleted = 0;
4772
+ let traceTaskId = 1;
4773
+ for (const traceRequest of traceRequests) {
4774
+ const task = {
4775
+ key: `trace-runner-${traceTaskId++}`,
4776
+ kind: "trace-runner",
4777
+ process: (bundleModels) => __async(this, null, function* () {
4778
+ var _a, _b;
4779
+ this.pendingTaskKeys.delete(task.key);
4780
+ let datasetReports;
4781
+ switch (traceRequest.kind) {
4782
+ case "compare-to-bundle":
4783
+ datasetReports = yield processCompareToBundleRequest(traceRequest, bundleModels);
4784
+ break;
4785
+ case "compare-to-ext-data":
4786
+ datasetReports = yield processCompareToExtDataRequest(traceRequest, bundleModels);
4787
+ break;
4788
+ default:
4789
+ (0, import_assert_never12.assertNever)(traceRequest);
4790
+ }
4791
+ for (const datasetReport of datasetReports) {
4792
+ allDatasetReports.set(datasetReport.datasetKey, datasetReport);
4793
+ }
4794
+ tasksCompleted++;
4795
+ if (tasksCompleted === taskCount) {
4796
+ const traceReport = {
4797
+ datasetReports: allDatasetReports
4798
+ };
4799
+ (_b = (_a = this.callbacks).onComplete) == null ? void 0 : _b.call(_a, traceReport);
4800
+ }
4801
+ })
4802
+ };
4803
+ this.taskQueue.addTask(task);
4804
+ this.pendingTaskKeys.add(task.key);
4805
+ }
4806
+ }
4807
+ };
4808
+ function processCompareToBundleRequest(request, bundleModels) {
4809
+ return __async(this, null, function* () {
4810
+ const bundleModel0 = request.bundleSide0 === "left" ? bundleModels.L : bundleModels.R;
4811
+ const bundleModel1 = request.bundleSide1 === "left" ? bundleModels.L : bundleModels.R;
4812
+ let result0;
4813
+ let result1;
4814
+ if (bundleModel1 === bundleModel0) {
4815
+ result0 = yield bundleModel0.getDatasetsForScenario(request.scenarioSpec0, request.datasetKeys);
4816
+ result1 = yield bundleModel1.getDatasetsForScenario(request.scenarioSpec1, request.datasetKeys);
4817
+ } else {
4818
+ ;
4819
+ [result0, result1] = yield Promise.all([
4820
+ bundleModel0.getDatasetsForScenario(request.scenarioSpec0, request.datasetKeys),
4821
+ bundleModel1.getDatasetsForScenario(request.scenarioSpec1, request.datasetKeys)
4822
+ ]);
4823
+ }
4824
+ const datasetReports = [];
4825
+ for (const datasetKey of request.datasetKeys) {
4826
+ const dataset0 = result0.datasetMap.get(datasetKey);
4827
+ const dataset1 = result1.datasetMap.get(datasetKey);
4828
+ const datasetReport = diffDatasets2(
4829
+ datasetKey,
4830
+ dataset0,
4831
+ dataset1,
4832
+ /*matchPrecisionOfLeft=*/
4833
+ false
4834
+ );
4835
+ datasetReports.push(datasetReport);
4836
+ }
4837
+ return datasetReports;
4838
+ });
4839
+ }
4840
+ function processCompareToExtDataRequest(request, bundleModels) {
4841
+ return __async(this, null, function* () {
4842
+ const bundleModel = request.bundleSide === "left" ? bundleModels.L : bundleModels.R;
4843
+ const resultR = yield bundleModel.getDatasetsForScenario(request.scenarioSpec, request.datasetKeys);
4844
+ const datasetReports = [];
4845
+ for (const datasetKey of request.datasetKeys) {
4846
+ let datasetL = request.extData.get(datasetKey);
4847
+ if (datasetL === void 0) {
4848
+ const datasetKeyParts = datasetKey.split("[");
4849
+ if (datasetKeyParts.length === 2) {
4850
+ const baseKey = datasetKeyParts[0];
4851
+ const keySubParts = datasetKeyParts[1].replace("]", "");
4852
+ const keySubIds = keySubParts.split(",");
4853
+ const subIdPermutations = permutationsOf(keySubIds);
4854
+ for (const subIds of subIdPermutations) {
4855
+ const datDatasetKey = `${baseKey}[${subIds.join(",")}]`;
4856
+ datasetL = request.extData.get(datDatasetKey);
4857
+ if (datasetL !== void 0) {
4858
+ break;
4859
+ }
4860
+ }
4861
+ }
4862
+ if (datasetL === void 0) {
4863
+ console.warn(`WARNING: Failed to find data in dat file for key=${datasetKey}`);
4864
+ }
4865
+ }
4866
+ const datasetR = resultR.datasetMap.get(datasetKey);
4867
+ const datasetReport = diffDatasets2(
4868
+ datasetKey,
4869
+ datasetL,
4870
+ datasetR,
4871
+ /*matchPrecisionOfLeft=*/
4872
+ true
4873
+ );
4874
+ datasetReports.push(datasetReport);
4875
+ }
4876
+ return datasetReports;
4877
+ });
4878
+ }
4879
+ function diffDatasets2(datasetKey, datasetL, datasetR, matchPrecisionOfLeft) {
4880
+ const points = /* @__PURE__ */ new Map();
4881
+ let minValueL = Number.MAX_VALUE;
4882
+ let maxValueL = Number.MIN_VALUE;
4883
+ let minValueR = Number.MAX_VALUE;
4884
+ let maxValueR = Number.MIN_VALUE;
4885
+ let minValue = Number.MAX_VALUE;
4886
+ let maxValue = Number.MIN_VALUE;
4887
+ let minRawDiff = Number.MAX_VALUE;
4888
+ let maxRawDiff = -1;
4889
+ let maxDiffPoint;
4890
+ let diffCount = 0;
4891
+ let totalRawDiff = 0;
4892
+ if (datasetL && datasetR) {
4893
+ const times = /* @__PURE__ */ new Set([...datasetL.keys(), ...datasetR.keys()]);
4894
+ for (const t of times) {
4895
+ const valueL = datasetL.get(t);
4896
+ if (valueL !== void 0) {
4897
+ if (valueL < minValueL) minValueL = valueL;
4898
+ if (valueL > maxValueL) maxValueL = valueL;
4899
+ if (valueL < minValue) minValue = valueL;
4900
+ if (valueL > maxValue) maxValue = valueL;
4901
+ }
4902
+ let valueR;
4903
+ const rawValueR = datasetR.get(t);
4904
+ if (rawValueR !== void 0) {
4905
+ if (matchPrecisionOfLeft && valueL !== void 0) {
4906
+ valueR = matchPrecision(rawValueR, valueL);
4907
+ } else {
4908
+ valueR = rawValueR;
4909
+ }
4910
+ if (valueR < minValueR) minValueR = valueR;
4911
+ if (valueR > maxValueR) maxValueR = valueR;
4912
+ if (valueR < minValue) minValue = valueR;
4913
+ if (valueR > maxValue) maxValue = valueR;
4914
+ }
4915
+ if (valueL === void 0 || valueR === void 0) {
4916
+ continue;
4917
+ }
4918
+ const point = {
4919
+ time: t,
4920
+ valueL,
4921
+ valueR
4922
+ };
4923
+ points.set(t, point);
4924
+ const rawDiff = Math.abs(valueR - valueL);
4925
+ if (rawDiff < minRawDiff) {
4926
+ minRawDiff = rawDiff;
4927
+ }
4928
+ if (rawDiff > maxRawDiff) {
4929
+ maxRawDiff = rawDiff;
4930
+ maxDiffPoint = point;
4931
+ }
4932
+ diffCount++;
4933
+ totalRawDiff += rawDiff;
4934
+ }
4935
+ }
4936
+ function pct(x) {
4937
+ return x * 100;
4938
+ }
4939
+ let minDiff;
4940
+ let maxDiff;
4941
+ let avgDiff;
4942
+ if (minValueL === maxValueL && minValueR === maxValueR) {
4943
+ const diff = pct(maxValueL !== 0 ? Math.abs((maxValueR - maxValueL) / maxValueL) : 1);
4944
+ minDiff = diff;
4945
+ maxDiff = diff;
4946
+ avgDiff = diff;
4947
+ } else {
4948
+ const spread = maxValue - minValue;
4949
+ minDiff = pct(spread > 0 ? minRawDiff / spread : 0);
4950
+ maxDiff = pct(spread > 0 ? maxRawDiff / spread : 0);
4951
+ const avgRawDiff = totalRawDiff / diffCount;
4952
+ avgDiff = pct(spread > 0 ? avgRawDiff / spread : 0);
4953
+ }
4954
+ let validity;
4955
+ if (datasetL && datasetR) {
4956
+ validity = "both";
4957
+ } else if (datasetL) {
4958
+ validity = "left-only";
4959
+ } else if (datasetR) {
4960
+ validity = "right-only";
4961
+ } else {
4962
+ validity = "neither";
4963
+ }
4964
+ return {
4965
+ datasetKey,
4966
+ validity,
4967
+ points,
4968
+ minValue,
4969
+ maxValue,
4970
+ avgDiff,
4971
+ minDiff,
4972
+ maxDiff,
4973
+ maxDiffPoint
4974
+ };
4975
+ }
4976
+ function matchPrecision(x, baseline) {
4977
+ const s = baseline.toString();
4978
+ if (s.includes("e")) {
4979
+ return x;
4980
+ }
4981
+ const parts = s.split(".");
4982
+ if (parts.length < 2) {
4983
+ return x;
4984
+ }
4985
+ const sigDigits = parts[1].replace(/0+$/, "").length;
4986
+ if (sigDigits > 21) {
4987
+ return x;
4988
+ }
4989
+ return parseFloat(x.toFixed(sigDigits));
4990
+ }
4991
+ function permutationsOf(inputArr) {
4992
+ const result = [];
4993
+ const permute = (arr, m = []) => {
4994
+ if (arr.length === 0) {
4995
+ result.push(m);
4996
+ } else {
4997
+ for (let i = 0; i < arr.length; i++) {
4998
+ const curr = arr.slice();
4999
+ const next = curr.splice(i, 1);
5000
+ permute(curr.slice(), m.concat(next));
5001
+ }
5002
+ }
5003
+ };
5004
+ permute(inputArr);
5005
+ return result;
5006
+ }
5007
+
4422
5008
  // src/data/data-planner.ts
4423
5009
  var DataPlanner = class {
4424
5010
  /**
@@ -4605,10 +5191,10 @@ function scenarioPairUid(scenarioSpecL, scenarioSpecR) {
4605
5191
  }
4606
5192
 
4607
5193
  // src/check/check-runner.ts
4608
- function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios) {
4609
- const modelSpec = checkConfig.bundle.model.modelSpec;
5194
+ function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, skipChecks) {
5195
+ const modelSpec = checkConfig.bundle.modelSpec;
4610
5196
  const checkPlanner = new CheckPlanner(modelSpec);
4611
- checkPlanner.addAllChecks(checkSpec, simplifyScenarios);
5197
+ checkPlanner.addAllChecks(checkSpec, skipChecks);
4612
5198
  const checkPlan = checkPlanner.buildPlan();
4613
5199
  const refDatasets = /* @__PURE__ */ new Map();
4614
5200
  for (const [dataRefKey, dataRef] of checkPlan.dataRefs.entries()) {
@@ -4621,11 +5207,15 @@ function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplify
4621
5207
  }
4622
5208
  const checkResults = /* @__PURE__ */ new Map();
4623
5209
  for (const [checkKey, checkTask] of checkPlan.tasks.entries()) {
4624
- dataPlanner.addRequest(void 0, checkTask.scenario.spec, checkTask.dataset.datasetKey, (datasets) => {
4625
- const dataset = datasets.datasetR;
4626
- const checkResult = runCheck(checkTask, dataset, refDatasets);
4627
- checkResults.set(checkKey, checkResult);
4628
- });
5210
+ if (checkTask.skip === true) {
5211
+ checkResults.set(checkKey, { status: "skipped" });
5212
+ } else {
5213
+ dataPlanner.addRequest(void 0, checkTask.scenario.spec, checkTask.dataset.datasetKey, (datasets) => {
5214
+ const dataset = datasets.datasetR;
5215
+ const checkResult = runCheck(checkTask, dataset, refDatasets);
5216
+ checkResults.set(checkKey, checkResult);
5217
+ });
5218
+ }
4629
5219
  }
4630
5220
  return () => {
4631
5221
  return buildCheckReport(checkPlan, checkResults);
@@ -4690,21 +5280,73 @@ function runCheck(checkTask, dataset, refDatasets) {
4690
5280
  }
4691
5281
 
4692
5282
  // src/comparison/run/comparison-runner.ts
4693
- function runComparisons(comparisonConfig, dataPlanner) {
5283
+ function runComparisons(comparisonConfig, dataPlanner, skipScenarios) {
5284
+ function skipScenarioKey(title, subtitle) {
5285
+ let key = title.toLowerCase();
5286
+ if (subtitle) {
5287
+ key += ` :: ${subtitle.toLowerCase()}`;
5288
+ }
5289
+ return key;
5290
+ }
5291
+ const skipScenariosSet = new Set(skipScenarios.map((scenario) => skipScenarioKey(scenario.title, scenario.subtitle)));
5292
+ const allScenarios = [...comparisonConfig.scenarios.getAllScenarios()];
5293
+ let baselineScenario;
5294
+ const baselineScenarioIndex = allScenarios.findIndex((scenario) => {
5295
+ const settings = scenario.settings;
5296
+ return settings.kind === "all-inputs-settings" && settings.position === "at-default";
5297
+ });
5298
+ if (baselineScenarioIndex !== -1) {
5299
+ baselineScenario = allScenarios.splice(baselineScenarioIndex, 1)[0];
5300
+ }
4694
5301
  const testReports = [];
4695
- for (const scenario of comparisonConfig.scenarios.getAllScenarios()) {
5302
+ const baselineDiffReports = /* @__PURE__ */ new Map();
5303
+ function runComparisonsForScenario(scenario, isBaseline) {
4696
5304
  const datasetKeys = comparisonConfig.datasets.getDatasetKeysForScenario(scenario);
4697
- for (const datasetKey of datasetKeys) {
4698
- dataPlanner.addRequest(scenario.specL, scenario.specR, datasetKey, (datasets) => {
4699
- const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR);
5305
+ const shouldSkip = skipScenariosSet.has(skipScenarioKey(scenario.title, scenario.subtitle));
5306
+ if (shouldSkip) {
5307
+ for (const datasetKey of datasetKeys) {
4700
5308
  testReports.push({
4701
5309
  scenarioKey: scenario.key,
4702
5310
  datasetKey,
4703
- diffReport
5311
+ diffReport: void 0
4704
5312
  });
5313
+ }
5314
+ return;
5315
+ }
5316
+ for (const datasetKey of datasetKeys) {
5317
+ dataPlanner.addRequest(scenario.specL, scenario.specR, datasetKey, (datasets) => {
5318
+ const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR);
5319
+ if (isBaseline) {
5320
+ baselineDiffReports.set(datasetKey, diffReport);
5321
+ testReports.push({
5322
+ scenarioKey: scenario.key,
5323
+ datasetKey,
5324
+ diffReport
5325
+ });
5326
+ } else {
5327
+ const baselineDiffReport = baselineDiffReports.get(datasetKey);
5328
+ testReports.push({
5329
+ scenarioKey: scenario.key,
5330
+ datasetKey,
5331
+ diffReport,
5332
+ baselineDiffReport
5333
+ });
5334
+ }
4705
5335
  });
4706
5336
  }
4707
5337
  }
5338
+ runComparisonsForScenario(
5339
+ baselineScenario,
5340
+ /*isBaseline=*/
5341
+ true
5342
+ );
5343
+ for (const scenario of allScenarios) {
5344
+ runComparisonsForScenario(
5345
+ scenario,
5346
+ /*isBaseline=*/
5347
+ false
5348
+ );
5349
+ }
4708
5350
  return () => {
4709
5351
  return testReports;
4710
5352
  };
@@ -4712,74 +5354,52 @@ function runComparisons(comparisonConfig, dataPlanner) {
4712
5354
 
4713
5355
  // src/suite/suite-runner.ts
4714
5356
  var SuiteRunner = class {
4715
- constructor(config, callbacks) {
5357
+ constructor(config, taskQueue, callbacks) {
4716
5358
  this.config = config;
5359
+ this.taskQueue = taskQueue;
4717
5360
  this.callbacks = callbacks;
4718
5361
  this.perfStatsL = new PerfStats();
4719
5362
  this.perfStatsR = new PerfStats();
5363
+ this.pendingTaskKeys = /* @__PURE__ */ new Set();
4720
5364
  this.stopped = false;
4721
- this.taskQueue = new TaskQueue({
4722
- process: (request) => {
4723
- return this.processRequest(request);
4724
- }
4725
- });
4726
5365
  }
4727
5366
  cancel() {
4728
5367
  if (!this.stopped) {
5368
+ for (const taskKey of this.pendingTaskKeys) {
5369
+ this.taskQueue.cancelTask(taskKey);
5370
+ }
4729
5371
  this.stopped = true;
4730
- this.taskQueue.shutdown();
4731
5372
  }
4732
5373
  }
4733
5374
  start(options) {
4734
5375
  var _a, _b, _c, _d, _e, _f, _g, _h;
4735
5376
  (_b = (_a = this.callbacks).onProgress) == null ? void 0 : _b.call(_a, 0);
4736
- const modelSpec = this.config.check.bundle.model.modelSpec;
4737
- const dataPlanner = new DataPlanner(modelSpec.outputVars.size);
4738
- const refDataPlanner = new DataPlanner(modelSpec.outputVars.size);
5377
+ const modelSpecR = this.config.check.bundle.modelSpec;
5378
+ const dataPlanner = new DataPlanner(modelSpecR.outputVars.size);
5379
+ const refDataPlanner = new DataPlanner(modelSpecR.outputVars.size);
4739
5380
  const checkSpecResult = parseTestYaml(this.config.check.tests);
4740
5381
  if (checkSpecResult.isErr()) {
4741
5382
  (_d = (_c = this.callbacks).onError) == null ? void 0 : _d.call(_c, checkSpecResult.error);
4742
5383
  return;
4743
5384
  }
4744
5385
  const checkSpec = checkSpecResult.value;
4745
- const simplifyScenarios = (options == null ? void 0 : options.simplifyScenarios) === true;
4746
- const buildCheckReport2 = runChecks(this.config.check, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios);
5386
+ const skipChecks = (options == null ? void 0 : options.skipChecks) || [];
5387
+ const buildCheckReport2 = runChecks(this.config.check, checkSpec, dataPlanner, refDataPlanner, skipChecks);
4747
5388
  let buildComparisonTestReports;
4748
5389
  if (this.config.comparison) {
4749
- buildComparisonTestReports = runComparisons(this.config.comparison, dataPlanner);
5390
+ const skipScenarios = (options == null ? void 0 : options.skipComparisonScenarios) || [];
5391
+ buildComparisonTestReports = runComparisons(this.config.comparison, dataPlanner, skipScenarios);
4750
5392
  }
4751
- this.taskQueue.onIdle = (error) => {
4752
- var _a2, _b2, _c2, _d2;
4753
- if (this.stopped) {
4754
- return;
4755
- }
4756
- if (error) {
4757
- (_b2 = (_a2 = this.callbacks).onError) == null ? void 0 : _b2.call(_a2, error);
4758
- } else {
4759
- const checkReport = buildCheckReport2();
4760
- let comparisonReport;
4761
- if (this.config.comparison) {
4762
- comparisonReport = {
4763
- testReports: buildComparisonTestReports(),
4764
- perfReportL: this.perfStatsL.toReport(),
4765
- perfReportR: this.perfStatsR.toReport()
4766
- };
4767
- }
4768
- (_d2 = (_c2 = this.callbacks).onComplete) == null ? void 0 : _d2.call(_c2, {
4769
- checkReport,
4770
- comparisonReport
4771
- });
4772
- }
4773
- };
4774
5393
  const refDataPlan = refDataPlanner.buildPlan();
4775
5394
  const dataPlan = dataPlanner.buildPlan();
4776
5395
  const dataRequests = [...refDataPlan.requests, ...dataPlan.requests];
4777
5396
  const taskCount = dataRequests.length;
4778
5397
  if (taskCount === 0) {
5398
+ const checkReport = buildCheckReport2();
4779
5399
  let comparisonReport;
4780
5400
  if (this.config.comparison) {
4781
5401
  comparisonReport = {
4782
- testReports: [],
5402
+ testReports: buildComparisonTestReports(),
4783
5403
  perfReportL: this.perfStatsL.toReport(),
4784
5404
  perfReportR: this.perfStatsR.toReport()
4785
5405
  };
@@ -4787,26 +5407,54 @@ var SuiteRunner = class {
4787
5407
  this.cancel();
4788
5408
  (_f = (_e = this.callbacks).onProgress) == null ? void 0 : _f.call(_e, 1);
4789
5409
  (_h = (_g = this.callbacks).onComplete) == null ? void 0 : _h.call(_g, {
4790
- checkReport: {
4791
- groups: []
4792
- },
5410
+ checkReport,
4793
5411
  comparisonReport
4794
5412
  });
4795
5413
  return;
4796
5414
  }
5415
+ const buildReport = (error) => {
5416
+ var _a2, _b2, _c2, _d2;
5417
+ if (error) {
5418
+ (_b2 = (_a2 = this.callbacks).onError) == null ? void 0 : _b2.call(_a2, error);
5419
+ } else {
5420
+ const checkReport = buildCheckReport2();
5421
+ let comparisonReport;
5422
+ if (this.config.comparison) {
5423
+ comparisonReport = {
5424
+ testReports: buildComparisonTestReports(),
5425
+ perfReportL: this.perfStatsL.toReport(),
5426
+ perfReportR: this.perfStatsR.toReport()
5427
+ };
5428
+ }
5429
+ (_d2 = (_c2 = this.callbacks).onComplete) == null ? void 0 : _d2.call(_c2, {
5430
+ checkReport,
5431
+ comparisonReport
5432
+ });
5433
+ }
5434
+ };
4797
5435
  let tasksCompleted = 0;
4798
5436
  let dataTaskId = 1;
4799
5437
  for (const dataRequest of dataRequests) {
4800
- this.taskQueue.addTask(`data${dataTaskId++}`, dataRequest, () => {
4801
- var _a2, _b2;
4802
- tasksCompleted++;
4803
- (_b2 = (_a2 = this.callbacks).onProgress) == null ? void 0 : _b2.call(_a2, tasksCompleted / taskCount);
4804
- });
5438
+ const task = {
5439
+ key: `suite-runner-${dataTaskId++}`,
5440
+ kind: "suite-runner",
5441
+ process: (bundleModels) => __async(this, null, function* () {
5442
+ var _a2, _b2;
5443
+ this.pendingTaskKeys.delete(task.key);
5444
+ yield this.processRequest(dataRequest, bundleModels);
5445
+ tasksCompleted++;
5446
+ (_b2 = (_a2 = this.callbacks).onProgress) == null ? void 0 : _b2.call(_a2, tasksCompleted / taskCount);
5447
+ if (tasksCompleted === taskCount) {
5448
+ buildReport();
5449
+ }
5450
+ })
5451
+ };
5452
+ this.taskQueue.addTask(task);
5453
+ this.pendingTaskKeys.add(task.key);
4805
5454
  }
4806
5455
  }
4807
- processRequest(request) {
5456
+ processRequest(request, bundleModels) {
4808
5457
  return __async(this, null, function* () {
4809
- var _a, _b;
4810
5458
  const datasetKeySet = /* @__PURE__ */ new Set();
4811
5459
  for (const dataTask of request.dataTasks) {
4812
5460
  datasetKeySet.add(dataTask.datasetKey);
@@ -4821,8 +5469,8 @@ var SuiteRunner = class {
4821
5469
  }
4822
5470
  });
4823
5471
  }
4824
- const bundleModelL = (_a = this.config.comparison) == null ? void 0 : _a.bundleL.model;
4825
- const bundleModelR = ((_b = this.config.comparison) == null ? void 0 : _b.bundleR.model) || this.config.check.bundle.model;
5472
+ const bundleModelL = bundleModels.L;
5473
+ const bundleModelR = bundleModels.R;
4826
5474
  const [datasetsResultL, datasetsResultR] = yield Promise.all([
4827
5475
  getDatasets(bundleModelL, request.scenarioSpecL),
4828
5476
  getDatasets(bundleModelR, request.scenarioSpecR)
@@ -4846,22 +5494,29 @@ var SuiteRunner = class {
4846
5494
  });
4847
5495
  }
4848
5496
  };
4849
- function runSuite(config, callbacks, options) {
4850
- const suiteRunner = new SuiteRunner(config, callbacks);
5497
+ function runSuiteWithTaskQueue(config, taskQueue, callbacks, options) {
5498
+ const suiteRunner = new SuiteRunner(config, taskQueue, callbacks);
4851
5499
  suiteRunner.start(options);
4852
5500
  return () => {
4853
5501
  suiteRunner.cancel();
4854
5502
  };
4855
5503
  }
5504
+ function runSuite(config, callbacks, options) {
5505
+ const taskQueue = TaskQueue.getInstance();
5506
+ return runSuiteWithTaskQueue(config, taskQueue, callbacks, options);
5507
+ }
4856
5508
 
4857
5509
  // src/suite/suite-reporting.ts
4858
- function suiteSummaryFromReport(suiteReport) {
5510
+ function suiteSummaryFromReport(suiteReport, elapsedMillis) {
4859
5511
  const checkSummary = checkSummaryFromReport(suiteReport.checkReport);
4860
5512
  let comparisonSummary;
4861
5513
  if (suiteReport.comparisonReport) {
4862
5514
  comparisonSummary = comparisonSummaryFromReport(suiteReport.comparisonReport);
4863
5515
  }
5516
+ const date = (/* @__PURE__ */ new Date()).toISOString();
4864
5517
  return {
5518
+ date,
5519
+ elapsed: elapsedMillis,
4865
5520
  checkSummary,
4866
5521
  comparisonSummary
4867
5522
  };
@@ -4870,20 +5525,27 @@ function suiteSummaryFromReport(suiteReport) {
4870
5525
  0 && (module.exports = {
4871
5526
  CheckDataCoordinator,
4872
5527
  ComparisonDataCoordinator,
4873
- PerfRunner,
4874
5528
  PerfStats,
4875
5529
  categorizeComparisonTestSummaries,
4876
5530
  checkReportFromSummary,
4877
5531
  checkSummaryFromReport,
4878
5532
  comparisonSummaryFromReport,
5533
+ createCheckDataCoordinator,
5534
+ createCheckDataCoordinatorForTests,
5535
+ createComparisonDataCoordinator,
4879
5536
  createConfig,
4880
5537
  datasetMessage,
5538
+ decodeImplVars,
4881
5539
  diffDatasets,
4882
5540
  diffGraphs,
5541
+ encodeImplVars,
4883
5542
  getScoresForTestSummaries,
4884
5543
  predicateMessage,
5544
+ runPerf,
4885
5545
  runSuite,
5546
+ runTrace,
4886
5547
  scenarioMessage,
4887
- suiteSummaryFromReport
5548
+ suiteSummaryFromReport,
5549
+ testSummaryFromReport
4888
5550
  });
4889
5551
  //# sourceMappingURL=index.cjs.map