@sdeverywhere/check-core 0.1.12 → 0.1.14

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
@@ -545,7 +545,7 @@ function predicateReport(predicatePlan, checkKey, result) {
545
545
  opValue = `${sym} ${predOp}`;
546
546
  } else {
547
547
  const dataRef = (_a = predicatePlan.dataRefs) == null ? void 0 : _a.get(op);
548
- if (!dataRef) {
548
+ if (!dataRef || dataRef.refs.length === 0) {
549
549
  return;
550
550
  }
551
551
  const opDataRef = {
@@ -553,8 +553,9 @@ function predicateReport(predicatePlan, checkKey, result) {
553
553
  dataRef
554
554
  };
555
555
  opRef = opDataRef;
556
- opValue = `${sym} '${dataRef.dataset.name}'`;
557
- const refScenarioSpec = (_b = dataRef.scenario) == null ? void 0 : _b.spec;
556
+ const refDatasetNames = dataRef.refs.map((ref) => `'${ref.dataset.name}'`);
557
+ opValue = `${sym} ${refDatasetNames.join(" + ")}`;
558
+ const refScenarioSpec = (_b = dataRef.refs[0].scenario) == null ? void 0 : _b.spec;
558
559
  if (!refScenarioSpec) {
559
560
  return;
560
561
  }
@@ -1111,6 +1112,12 @@ var check_schema_default = {
1111
1112
  type: "number"
1112
1113
  },
1113
1114
  predicate_ref_data: {
1115
+ oneOf: [
1116
+ { $ref: "#/$defs/predicate_ref_data_single_dataset" },
1117
+ { $ref: "#/$defs/predicate_ref_data_multiple_datasets" }
1118
+ ]
1119
+ },
1120
+ predicate_ref_data_single_dataset: {
1114
1121
  type: "object",
1115
1122
  additionalProperties: false,
1116
1123
  properties: {
@@ -1119,6 +1126,27 @@ var check_schema_default = {
1119
1126
  },
1120
1127
  required: ["dataset"]
1121
1128
  },
1129
+ predicate_ref_data_multiple_datasets: {
1130
+ type: "object",
1131
+ additionalProperties: false,
1132
+ properties: {
1133
+ op: { $ref: "#/$defs/predicate_ref_data_datasets_op" },
1134
+ datasets: {
1135
+ type: "array",
1136
+ items: { $ref: "#/$defs/predicate_ref_data_datasets_item" },
1137
+ minItems: 1
1138
+ },
1139
+ scenario: { $ref: "#/$defs/predicate_ref_data_scenario" }
1140
+ },
1141
+ required: ["op", "datasets"]
1142
+ },
1143
+ predicate_ref_data_datasets_op: {
1144
+ type: "string",
1145
+ enum: ["sum"]
1146
+ },
1147
+ predicate_ref_data_datasets_item: {
1148
+ oneOf: [{ type: "string" }, { $ref: "#/$defs/dataset_name" }]
1149
+ },
1122
1150
  predicate_ref_data_dataset: {
1123
1151
  oneOf: [{ $ref: "#/$defs/dataset_name" }, { $ref: "#/$defs/predicate_ref_data_dataset_special" }]
1124
1152
  },
@@ -1971,6 +1999,17 @@ var CheckPlanner = class {
1971
1999
  * this will add a reference to the scenario/dataset pair so that the data can
1972
2000
  * be fetched in a later stage.
1973
2001
  *
2002
+ * A predicate can also reference multiple datasets that are combined into a
2003
+ * single dataset, for example:
2004
+ * ```
2005
+ * approx:
2006
+ * op: sum
2007
+ * datasets:
2008
+ * - 'X'
2009
+ * - 'Y'
2010
+ * ```
2011
+ * in which case one reference is added for each of the datasets.
2012
+ *
1974
2013
  * @param predicateSpec The predicate spec.
1975
2014
  * @param checkScenario The scenario in which the dataset is being checked.
1976
2015
  * @param checkDataset The dataset that is being checked.
@@ -1982,25 +2021,22 @@ var CheckPlanner = class {
1982
2021
  if (predOp === void 0 || typeof predOp === "number") {
1983
2022
  return;
1984
2023
  }
1985
- let refDataset;
1986
- if (typeof predOp.dataset === "string") {
2024
+ const refDatasets = [];
2025
+ if (predOp.datasets !== void 0) {
2026
+ for (const datasetSpec of predOp.datasets) {
2027
+ const refDatasetSpec = typeof datasetSpec === "string" ? { name: datasetSpec } : { name: datasetSpec.name, source: datasetSpec.source };
2028
+ refDatasets.push(this.resolveRefDataset(refDatasetSpec));
2029
+ }
2030
+ } else if (typeof predOp.dataset === "string") {
1987
2031
  switch (predOp.dataset) {
1988
2032
  case "inherit":
1989
- refDataset = checkDataset;
2033
+ refDatasets.push(checkDataset);
1990
2034
  break;
1991
2035
  default:
1992
2036
  (0, import_assert_never5.default)(predOp.dataset);
1993
2037
  }
1994
2038
  } else {
1995
- const refDatasetSpec = { name: predOp.dataset.name };
1996
- const matchedRefDatasets = expandDatasets(this.modelSpec, refDatasetSpec);
1997
- if (matchedRefDatasets.length === 1) {
1998
- refDataset = matchedRefDatasets[0];
1999
- } else {
2000
- refDataset = {
2001
- name: predOp.dataset.name
2002
- };
2003
- }
2039
+ refDatasets.push(this.resolveRefDataset({ name: predOp.dataset.name }));
2004
2040
  }
2005
2041
  let refScenario;
2006
2042
  if (typeof predOp.scenario === "string") {
@@ -2023,22 +2059,28 @@ var CheckPlanner = class {
2023
2059
  };
2024
2060
  }
2025
2061
  }
2026
- let dataRefKey;
2027
- if (refScenario.spec && refDataset.datasetKey) {
2028
- dataRefKey = `${refScenario.spec.uid}::${refDataset.datasetKey}`;
2029
- }
2030
- const dataRef = {
2031
- key: dataRefKey,
2032
- dataset: refDataset,
2033
- scenario: refScenario
2034
- };
2035
- if (dataRefKey) {
2036
- this.dataRefs.set(dataRefKey, dataRef);
2037
- }
2062
+ const refs = refDatasets.map((refDataset) => {
2063
+ let dataRefKey;
2064
+ if (refScenario.spec && refDataset.datasetKey) {
2065
+ dataRefKey = `${refScenario.spec.uid}::${refDataset.datasetKey}`;
2066
+ }
2067
+ const ref = {
2068
+ key: dataRefKey,
2069
+ dataset: refDataset,
2070
+ scenario: refScenario
2071
+ };
2072
+ if (dataRefKey) {
2073
+ this.dataRefs.set(dataRefKey, ref);
2074
+ }
2075
+ return ref;
2076
+ });
2038
2077
  if (dataRefs === void 0) {
2039
2078
  dataRefs = /* @__PURE__ */ new Map();
2040
2079
  }
2041
- dataRefs.set(op, dataRef);
2080
+ dataRefs.set(op, {
2081
+ op: predOp.op,
2082
+ refs
2083
+ });
2042
2084
  };
2043
2085
  addDataRef("gt");
2044
2086
  addDataRef("gte");
@@ -2048,6 +2090,23 @@ var CheckPlanner = class {
2048
2090
  addDataRef("approx");
2049
2091
  return dataRefs;
2050
2092
  }
2093
+ /**
2094
+ * Resolve the dataset that matches the given spec. If the spec does not expand
2095
+ * to exactly one dataset, this will return a `CheckDataset` with an undefined
2096
+ * `datasetKey` so that the error can be reported later.
2097
+ *
2098
+ * @param refDatasetSpec The spec for the referenced dataset.
2099
+ */
2100
+ resolveRefDataset(refDatasetSpec) {
2101
+ const matchedRefDatasets = expandDatasets(this.modelSpec, refDatasetSpec);
2102
+ if (matchedRefDatasets.length === 1) {
2103
+ return matchedRefDatasets[0];
2104
+ } else {
2105
+ return {
2106
+ name: refDatasetSpec.name
2107
+ };
2108
+ }
2109
+ }
2051
2110
  };
2052
2111
 
2053
2112
  // src/check/check-summary.ts
@@ -5397,14 +5456,15 @@ function scenarioPairUid(scenarioSpecL, scenarioSpecR, constantsL, constantsR, l
5397
5456
  }
5398
5457
 
5399
5458
  // src/check/check-runner.ts
5459
+ var import_assert_never13 = __toESM(require("assert-never"), 1);
5400
5460
  function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, skipChecks) {
5401
5461
  const modelSpec = checkConfig.bundle.modelSpec;
5402
5462
  const checkPlanner = new CheckPlanner(modelSpec);
5403
5463
  checkPlanner.addAllChecks(checkSpec, skipChecks);
5404
5464
  const checkPlan = checkPlanner.buildPlan();
5405
5465
  const refDatasets = /* @__PURE__ */ new Map();
5406
- for (const [dataRefKey, dataRef] of checkPlan.dataRefs.entries()) {
5407
- refDataPlanner.addRequest(void 0, dataRef.scenario.spec, dataRef.dataset.datasetKey, (datasets) => {
5466
+ for (const [dataRefKey, ref] of checkPlan.dataRefs.entries()) {
5467
+ refDataPlanner.addRequest(void 0, ref.scenario.spec, ref.dataset.datasetKey, (datasets) => {
5408
5468
  const dataset = datasets.datasetR;
5409
5469
  if (dataset) {
5410
5470
  refDatasets.set(dataRefKey, dataset);
@@ -5438,52 +5498,91 @@ function runCheck(checkTask, dataset, refDatasets) {
5438
5498
  if (checkTask.dataRefs) {
5439
5499
  opRefDatasets = /* @__PURE__ */ new Map();
5440
5500
  for (const [op, dataRef] of checkTask.dataRefs.entries()) {
5441
- const refDataset = refDatasets == null ? void 0 : refDatasets.get(dataRef.key);
5442
- if (refDataset === void 0) {
5443
- if (dataRef.dataset.datasetKey === void 0) {
5444
- return {
5445
- status: "error",
5446
- errorInfo: {
5447
- kind: "unknown-dataset",
5448
- name: dataRef.dataset.name
5449
- }
5450
- };
5451
- } else if (dataRef.scenario.spec === void 0) {
5452
- if (dataRef.scenario.error) {
5453
- return {
5454
- status: "error",
5455
- errorInfo: {
5456
- kind: dataRef.scenario.error.kind,
5457
- name: dataRef.scenario.error.name
5458
- }
5459
- };
5460
- } else {
5461
- let inputName;
5462
- if (dataRef.scenario.inputDescs.length > 0) {
5463
- inputName = dataRef.scenario.inputDescs[0].name;
5464
- } else {
5465
- inputName = "unknown";
5466
- }
5467
- return {
5468
- status: "error",
5469
- errorInfo: {
5470
- kind: "unknown-input",
5471
- name: inputName
5472
- }
5473
- };
5474
- }
5475
- } else {
5476
- return {
5477
- status: "error",
5478
- message: "unresolved data reference"
5479
- };
5501
+ const resolvedDatasets = [];
5502
+ for (const ref of dataRef.refs) {
5503
+ const refDataset = refDatasets == null ? void 0 : refDatasets.get(ref.key);
5504
+ if (refDataset === void 0) {
5505
+ return errorResultForRefDataset(ref);
5480
5506
  }
5507
+ resolvedDatasets.push(refDataset);
5481
5508
  }
5482
- opRefDatasets.set(op, refDataset);
5509
+ opRefDatasets.set(op, combineRefDatasets(dataRef, resolvedDatasets));
5483
5510
  }
5484
5511
  }
5485
5512
  return checkTask.action.run(dataset, opRefDatasets);
5486
5513
  }
5514
+ function errorResultForRefDataset(ref) {
5515
+ if (ref.dataset.datasetKey === void 0) {
5516
+ return {
5517
+ status: "error",
5518
+ errorInfo: {
5519
+ kind: "unknown-dataset",
5520
+ name: ref.dataset.name
5521
+ }
5522
+ };
5523
+ } else if (ref.scenario.spec === void 0) {
5524
+ if (ref.scenario.error) {
5525
+ return {
5526
+ status: "error",
5527
+ errorInfo: {
5528
+ kind: ref.scenario.error.kind,
5529
+ name: ref.scenario.error.name
5530
+ }
5531
+ };
5532
+ } else {
5533
+ let inputName;
5534
+ if (ref.scenario.inputDescs.length > 0) {
5535
+ inputName = ref.scenario.inputDescs[0].name;
5536
+ } else {
5537
+ inputName = "unknown";
5538
+ }
5539
+ return {
5540
+ status: "error",
5541
+ errorInfo: {
5542
+ kind: "unknown-input",
5543
+ name: inputName
5544
+ }
5545
+ };
5546
+ }
5547
+ } else {
5548
+ return {
5549
+ status: "error",
5550
+ message: "unresolved data reference"
5551
+ };
5552
+ }
5553
+ }
5554
+ function combineRefDatasets(dataRef, datasets) {
5555
+ if (dataRef.op === void 0) {
5556
+ return datasets[0];
5557
+ }
5558
+ switch (dataRef.op) {
5559
+ case "sum":
5560
+ return sumDatasets(datasets);
5561
+ default:
5562
+ (0, import_assert_never13.default)(dataRef.op);
5563
+ }
5564
+ }
5565
+ function sumDatasets(datasets) {
5566
+ const firstDataset = datasets[0];
5567
+ const otherDatasets = datasets.slice(1);
5568
+ const summed = /* @__PURE__ */ new Map();
5569
+ for (const [time, value] of firstDataset) {
5570
+ let sum = value;
5571
+ let complete = true;
5572
+ for (const otherDataset of otherDatasets) {
5573
+ const otherValue = otherDataset.get(time);
5574
+ if (otherValue === void 0) {
5575
+ complete = false;
5576
+ break;
5577
+ }
5578
+ sum += otherValue;
5579
+ }
5580
+ if (complete) {
5581
+ summed.set(time, sum);
5582
+ }
5583
+ }
5584
+ return summed;
5585
+ }
5487
5586
 
5488
5587
  // src/comparison/run/comparison-runner.ts
5489
5588
  function runComparisons(comparisonConfig, dataPlanner, skipScenarios) {
@@ -5619,8 +5718,9 @@ var SuiteRunner = class {
5619
5718
  }
5620
5719
  const refDataPlan = refDataPlanner.buildPlan();
5621
5720
  const dataPlan = dataPlanner.buildPlan();
5622
- const dataRequests = [...refDataPlan.requests, ...dataPlan.requests];
5623
- const taskCount = dataRequests.length;
5721
+ const refDataRequests = refDataPlan.requests;
5722
+ const dataRequests = dataPlan.requests;
5723
+ const taskCount = refDataRequests.length + dataRequests.length;
5624
5724
  if (taskCount === 0) {
5625
5725
  const checkReport = buildCheckReport2();
5626
5726
  let comparisonReport;
@@ -5658,23 +5758,42 @@ var SuiteRunner = class {
5658
5758
  };
5659
5759
  let tasksCompleted = 0;
5660
5760
  let dataTaskId = 1;
5661
- for (const dataRequest of dataRequests) {
5662
- const task = {
5663
- key: `suite-runner-${dataTaskId++}`,
5664
- kind: "suite-runner",
5665
- process: (bundleModels) => __async(this, null, function* () {
5666
- var _a2, _b2;
5667
- this.pendingTaskKeys.delete(task.key);
5668
- yield this.processRequest(dataRequest, bundleModels);
5669
- tasksCompleted++;
5670
- (_b2 = (_a2 = this.callbacks).onProgress) == null ? void 0 : _b2.call(_a2, tasksCompleted / taskCount);
5671
- if (tasksCompleted === taskCount) {
5672
- buildReport();
5673
- }
5674
- })
5675
- };
5676
- this.taskQueue.addTask(task);
5677
- this.pendingTaskKeys.add(task.key);
5761
+ const scheduleRequests = (requests, onGroupComplete) => {
5762
+ let groupTasksCompleted = 0;
5763
+ for (const dataRequest of requests) {
5764
+ const task = {
5765
+ key: `suite-runner-${dataTaskId++}`,
5766
+ kind: "suite-runner",
5767
+ process: (bundleModels) => __async(this, null, function* () {
5768
+ var _a2, _b2;
5769
+ this.pendingTaskKeys.delete(task.key);
5770
+ yield this.processRequest(dataRequest, bundleModels);
5771
+ tasksCompleted++;
5772
+ (_b2 = (_a2 = this.callbacks).onProgress) == null ? void 0 : _b2.call(_a2, tasksCompleted / taskCount);
5773
+ groupTasksCompleted++;
5774
+ if (groupTasksCompleted === requests.length) {
5775
+ onGroupComplete();
5776
+ }
5777
+ })
5778
+ };
5779
+ this.taskQueue.addTask(task);
5780
+ this.pendingTaskKeys.add(task.key);
5781
+ }
5782
+ };
5783
+ const scheduleDataRequests = () => {
5784
+ if (this.stopped) {
5785
+ return;
5786
+ }
5787
+ if (dataRequests.length > 0) {
5788
+ scheduleRequests(dataRequests, buildReport);
5789
+ } else {
5790
+ buildReport();
5791
+ }
5792
+ };
5793
+ if (refDataRequests.length > 0) {
5794
+ scheduleRequests(refDataRequests, scheduleDataRequests);
5795
+ } else {
5796
+ scheduleDataRequests();
5678
5797
  }
5679
5798
  }
5680
5799
  processRequest(request, bundleModels) {