@sdeverywhere/check-core 0.1.0

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 ADDED
@@ -0,0 +1,2988 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __export = (target, all) => {
25
+ for (var name in all)
26
+ __defProp(target, name, { get: all[name], enumerable: true });
27
+ };
28
+ var __copyProps = (to, from, except, desc) => {
29
+ if (from && typeof from === "object" || typeof from === "function") {
30
+ for (let key of __getOwnPropNames(from))
31
+ if (!__hasOwnProp.call(to, key) && key !== except)
32
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
33
+ }
34
+ return to;
35
+ };
36
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
37
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
+
39
+ // src/index.ts
40
+ var src_exports = {};
41
+ __export(src_exports, {
42
+ CheckDataCoordinator: () => CheckDataCoordinator,
43
+ CompareDataCoordinator: () => CompareDataCoordinator,
44
+ DatasetManager: () => DatasetManager,
45
+ PerfRunner: () => PerfRunner,
46
+ PerfStats: () => PerfStats,
47
+ ScenarioManager: () => ScenarioManager,
48
+ allInputsAtPositionScenario: () => allInputsAtPositionScenario,
49
+ checkReportFromSummary: () => checkReportFromSummary,
50
+ checkSummaryFromReport: () => checkSummaryFromReport,
51
+ compareDatasets: () => compareDatasets,
52
+ compareSummaryFromReport: () => compareSummaryFromReport,
53
+ createConfig: () => createConfig,
54
+ datasetMessage: () => datasetMessage,
55
+ diffDatasets: () => diffDatasets,
56
+ diffGraphs: () => diffGraphs,
57
+ inputAtPositionScenario: () => inputAtPositionScenario,
58
+ inputAtValueScenario: () => inputAtValueScenario,
59
+ matrixScenarios: () => matrixScenarios,
60
+ positionSetting: () => positionSetting,
61
+ predicateMessage: () => predicateMessage,
62
+ runSuite: () => runSuite,
63
+ scenarioMessage: () => scenarioMessage,
64
+ settingsScenario: () => settingsScenario,
65
+ suiteSummaryFromReport: () => suiteSummaryFromReport,
66
+ valueSetting: () => valueSetting
67
+ });
68
+ module.exports = __toCommonJS(src_exports);
69
+
70
+ // src/_shared/scenario.ts
71
+ var import_assert_never = require("assert-never");
72
+ function positionSetting(inputVarId, position) {
73
+ return {
74
+ kind: "position",
75
+ inputVarId,
76
+ position
77
+ };
78
+ }
79
+ function valueSetting(inputVarId, value) {
80
+ return {
81
+ kind: "value",
82
+ inputVarId,
83
+ value
84
+ };
85
+ }
86
+ function settingsScenario(key, groupKey, settings) {
87
+ return {
88
+ kind: "settings",
89
+ key,
90
+ groupKey,
91
+ settings
92
+ };
93
+ }
94
+ function inputAtPositionScenario(inputVarId, groupKey, position) {
95
+ const key = keyForInputAtPosition(`input${inputVarId}`, position);
96
+ return settingsScenario(key, groupKey, [positionSetting(inputVarId, position)]);
97
+ }
98
+ function inputAtValueScenario(inputVarId, groupKey, value) {
99
+ const key = keyForInputAtValue(`input${inputVarId}`, value);
100
+ return settingsScenario(key, groupKey, [valueSetting(inputVarId, value)]);
101
+ }
102
+ function allInputsAtPositionScenario(position) {
103
+ return {
104
+ kind: "all-inputs",
105
+ key: keyForInputAtPosition("all_inputs", position),
106
+ groupKey: "all_inputs",
107
+ position
108
+ };
109
+ }
110
+ function matrixScenarios(inputVarIds) {
111
+ const scenarios = [];
112
+ scenarios.push(allInputsAtPositionScenario("at-default"));
113
+ scenarios.push(allInputsAtPositionScenario("at-minimum"));
114
+ scenarios.push(allInputsAtPositionScenario("at-maximum"));
115
+ for (const inputVarId of inputVarIds) {
116
+ scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, "at-minimum"));
117
+ scenarios.push(inputAtPositionScenario(inputVarId, inputVarId, "at-maximum"));
118
+ }
119
+ return scenarios;
120
+ }
121
+ function keyForInputPosition(position) {
122
+ switch (position) {
123
+ case "at-default":
124
+ return "default";
125
+ case "at-minimum":
126
+ return "min";
127
+ case "at-maximum":
128
+ return "max";
129
+ default:
130
+ (0, import_assert_never.assertNever)(position);
131
+ }
132
+ }
133
+ function keyForInputAtPosition(inputKey, position) {
134
+ return `${inputKey}_at_${keyForInputPosition(position)}`;
135
+ }
136
+ function keyForInputAtValue(inputKey, value) {
137
+ return `${inputKey}_at_${value}`;
138
+ }
139
+
140
+ // src/_shared/task-queue.ts
141
+ var TaskQueue = class {
142
+ constructor(processor) {
143
+ this.processor = processor;
144
+ this.taskKeyQueue = [];
145
+ this.taskMap = /* @__PURE__ */ new Map();
146
+ this.processing = false;
147
+ this.stopped = false;
148
+ }
149
+ addTask(key, input, onComplete) {
150
+ if (this.stopped) {
151
+ return;
152
+ }
153
+ if (this.taskMap.has(key)) {
154
+ throw new Error(`Task already added for key ${key}`);
155
+ }
156
+ this.taskKeyQueue.push(key);
157
+ this.taskMap.set(key, {
158
+ input,
159
+ onComplete
160
+ });
161
+ this.processTasksIfNeeded();
162
+ }
163
+ cancelTask(taskKey) {
164
+ const index = this.taskKeyQueue.indexOf(taskKey);
165
+ if (index >= 0) {
166
+ this.taskKeyQueue.splice(index, 1);
167
+ }
168
+ this.taskMap.delete(taskKey);
169
+ }
170
+ shutdown() {
171
+ this.stopped = true;
172
+ this.processing = false;
173
+ this.taskKeyQueue.length = 0;
174
+ this.taskMap.clear();
175
+ }
176
+ processTasksIfNeeded() {
177
+ if (!this.stopped && !this.processing) {
178
+ this.processing = true;
179
+ setTimeout(() => {
180
+ this.processNextTask();
181
+ });
182
+ }
183
+ }
184
+ async processNextTask() {
185
+ var _a, _b;
186
+ const taskKey = this.taskKeyQueue.shift();
187
+ if (!taskKey) {
188
+ return;
189
+ }
190
+ const task = this.taskMap.get(taskKey);
191
+ if (task) {
192
+ this.taskMap.delete(taskKey);
193
+ } else {
194
+ return;
195
+ }
196
+ let output;
197
+ try {
198
+ output = await this.processor.process(task.input);
199
+ } catch (e) {
200
+ if (!this.stopped) {
201
+ this.shutdown();
202
+ (_a = this.onIdle) == null ? void 0 : _a.call(this, e);
203
+ }
204
+ return;
205
+ }
206
+ task.onComplete(output);
207
+ if (this.taskKeyQueue.length > 0) {
208
+ setTimeout(() => {
209
+ this.processNextTask();
210
+ });
211
+ } else {
212
+ this.processing = false;
213
+ if (!this.stopped) {
214
+ (_b = this.onIdle) == null ? void 0 : _b.call(this);
215
+ }
216
+ }
217
+ }
218
+ };
219
+
220
+ // src/check/check-data-coordinator.ts
221
+ var CheckDataCoordinator = class {
222
+ constructor(bundleModel) {
223
+ this.bundleModel = bundleModel;
224
+ this.taskQueue = new TaskQueue({
225
+ process: async (request) => {
226
+ const result = await this.bundleModel.getDatasetsForScenario(request.scenario, [request.datasetKey]);
227
+ const dataset = result.datasetMap.get(request.datasetKey);
228
+ return {
229
+ dataset
230
+ };
231
+ }
232
+ });
233
+ }
234
+ requestDataset(requestKey, scenario, datasetKey, onResponse) {
235
+ const request = {
236
+ scenario,
237
+ datasetKey
238
+ };
239
+ this.taskQueue.addTask(requestKey, request, (response) => {
240
+ onResponse(response.dataset);
241
+ });
242
+ }
243
+ cancelRequest(key) {
244
+ this.taskQueue.cancelTask(key);
245
+ }
246
+ };
247
+
248
+ // src/check/check-report.ts
249
+ var import_assert_never3 = __toESM(require("assert-never"), 1);
250
+
251
+ // src/check/check-predicate.ts
252
+ var import_assert_never2 = __toESM(require("assert-never"), 1);
253
+ function symbolForPredicateOp(op) {
254
+ switch (op) {
255
+ case "gt":
256
+ return ">";
257
+ case "gte":
258
+ return ">=";
259
+ case "lt":
260
+ return "<";
261
+ case "lte":
262
+ return "<=";
263
+ case "eq":
264
+ return "==";
265
+ case "approx":
266
+ return "\u2248";
267
+ default:
268
+ (0, import_assert_never2.default)(op);
269
+ }
270
+ }
271
+
272
+ // src/check/check-report.ts
273
+ function buildCheckReport(checkPlan, checkResults) {
274
+ const groupReports = [];
275
+ for (const groupPlan of checkPlan.groups) {
276
+ const testReports = [];
277
+ for (const testPlan of groupPlan.tests) {
278
+ let testStatus = "passed";
279
+ const scenarioReports = [];
280
+ for (const scenarioPlan of testPlan.scenarios) {
281
+ let scenarioStatus = "passed";
282
+ if (scenarioPlan.checkScenario.scenario === void 0) {
283
+ testStatus = "error";
284
+ scenarioStatus = "error";
285
+ }
286
+ const datasetReports = [];
287
+ for (const datasetPlan of scenarioPlan.datasets) {
288
+ let datasetStatus = "passed";
289
+ if (datasetPlan.checkDataset.datasetKey === void 0) {
290
+ testStatus = "error";
291
+ scenarioStatus = "error";
292
+ datasetStatus = "error";
293
+ }
294
+ const predicateReports = [];
295
+ for (const predicatePlan of datasetPlan.predicates) {
296
+ const checkKey = predicatePlan.checkKey;
297
+ const checkResult = checkResults.get(checkKey);
298
+ if (checkResult) {
299
+ if (checkResult.status !== "passed") {
300
+ if (checkResult.status === "error") {
301
+ testStatus = "error";
302
+ scenarioStatus = "error";
303
+ datasetStatus = "error";
304
+ } else if (checkResult.status === "failed" && testStatus !== "error") {
305
+ testStatus = "failed";
306
+ scenarioStatus = "failed";
307
+ datasetStatus = "failed";
308
+ }
309
+ }
310
+ predicateReports.push(predicateReport(predicatePlan, checkKey, checkResult));
311
+ } else {
312
+ predicateReports.push(predicateReport(predicatePlan, checkKey, { status: "passed" }));
313
+ }
314
+ }
315
+ datasetReports.push({
316
+ checkDataset: datasetPlan.checkDataset,
317
+ status: datasetStatus,
318
+ predicates: predicateReports
319
+ });
320
+ }
321
+ scenarioReports.push({
322
+ checkScenario: scenarioPlan.checkScenario,
323
+ status: scenarioStatus,
324
+ datasets: datasetReports
325
+ });
326
+ }
327
+ testReports.push({
328
+ name: testPlan.name,
329
+ status: testStatus,
330
+ scenarios: scenarioReports
331
+ });
332
+ }
333
+ groupReports.push({
334
+ name: groupPlan.name,
335
+ tests: testReports
336
+ });
337
+ }
338
+ return {
339
+ groups: groupReports
340
+ };
341
+ }
342
+ function predicateReport(predicatePlan, checkKey, result) {
343
+ if (result.status === "error") {
344
+ return {
345
+ checkKey,
346
+ result,
347
+ opRefs: /* @__PURE__ */ new Map(),
348
+ opValues: []
349
+ };
350
+ }
351
+ const predicateSpec = predicatePlan.action.predicateSpec;
352
+ const opRefs = /* @__PURE__ */ new Map();
353
+ const opValues = [];
354
+ function addOp(op) {
355
+ var _a, _b;
356
+ const sym = symbolForPredicateOp(op);
357
+ const predOp = predicateSpec[op];
358
+ if (predOp !== void 0) {
359
+ let opRef;
360
+ let opValue;
361
+ if (typeof predOp === "number") {
362
+ const opConstantRef = {
363
+ kind: "constant",
364
+ value: predOp
365
+ };
366
+ opRef = opConstantRef;
367
+ opValue = `${sym} ${predOp}`;
368
+ } else {
369
+ const dataRef = (_a = predicatePlan.dataRefs) == null ? void 0 : _a.get(op);
370
+ if (!dataRef) {
371
+ return;
372
+ }
373
+ const opDataRef = {
374
+ kind: "data",
375
+ dataRef
376
+ };
377
+ opRef = opDataRef;
378
+ opValue = `${sym} '${dataRef.dataset.name}'`;
379
+ const refScenario = (_b = dataRef.scenario) == null ? void 0 : _b.scenario;
380
+ if (!refScenario) {
381
+ return;
382
+ }
383
+ if (predOp.scenario === "inherit") {
384
+ opValue += ` (w/ same scenario)`;
385
+ } else {
386
+ if (refScenario.kind === "all-inputs" && refScenario.position === "at-default") {
387
+ opValue += ` (w/ default scenario)`;
388
+ } else {
389
+ opValue += ` (w/ configured scenario)`;
390
+ }
391
+ }
392
+ }
393
+ if (op === "approx") {
394
+ const tolerance = predicateSpec.tolerance || 0.1;
395
+ opValue += ` \xB1${tolerance}`;
396
+ }
397
+ opRefs.set(op, opRef);
398
+ opValues.push(opValue);
399
+ }
400
+ }
401
+ addOp("gt");
402
+ addOp("gte");
403
+ addOp("lt");
404
+ addOp("lte");
405
+ addOp("eq");
406
+ addOp("approx");
407
+ if (opValues.length === 0) {
408
+ opValues.push("INVALID PREDICATE");
409
+ }
410
+ return {
411
+ checkKey,
412
+ result,
413
+ opRefs,
414
+ opValues,
415
+ time: predicateSpec.time,
416
+ tolerance: predicateSpec.tolerance
417
+ };
418
+ }
419
+ function scenarioMessage(scenario, bold) {
420
+ const checkScenario = scenario.checkScenario;
421
+ if (checkScenario.scenario === void 0) {
422
+ if (checkScenario.error) {
423
+ switch (checkScenario.error.kind) {
424
+ case "unknown-input-group":
425
+ return `error: input group ${bold(checkScenario.error.name)} is unknown`;
426
+ case "empty-input-group":
427
+ return `error: input group ${bold(checkScenario.error.name)} is empty`;
428
+ default:
429
+ (0, import_assert_never3.default)(checkScenario.error.kind);
430
+ }
431
+ } else {
432
+ const badInputNames = checkScenario.inputDescs.filter((d) => d.inputVar === void 0).map((d) => bold(d.name));
433
+ const label = badInputNames.length === 1 ? "input" : "inputs";
434
+ return `error: unknown ${label} ${badInputNames.join(", ")}`;
435
+ }
436
+ }
437
+ function positionName(position) {
438
+ switch (position) {
439
+ case "at-default":
440
+ return "default";
441
+ case "at-minimum":
442
+ return "minimum";
443
+ case "at-maximum":
444
+ return "maximum";
445
+ default:
446
+ (0, import_assert_never3.default)(position);
447
+ }
448
+ }
449
+ function inputMessage(inputDesc) {
450
+ let msg = bold(inputDesc.name);
451
+ if (inputDesc.position) {
452
+ msg += ` is at ${bold(positionName(inputDesc.position))}`;
453
+ if (inputDesc.value !== void 0) {
454
+ msg += ` (${inputDesc.value})`;
455
+ }
456
+ } else if (inputDesc.value !== void 0) {
457
+ msg += ` is ${bold(inputDesc.value.toString())}`;
458
+ }
459
+ return msg;
460
+ }
461
+ if (checkScenario.scenario.kind === "all-inputs") {
462
+ const position = checkScenario.scenario.position;
463
+ return `when ${bold("all inputs")} are at ${bold(positionName(position))}...`;
464
+ } else if (checkScenario.inputGroupName) {
465
+ let position = "at-default";
466
+ if (checkScenario.scenario.settings[0].kind === "position") {
467
+ position = checkScenario.scenario.settings[0].position;
468
+ }
469
+ const groupName = checkScenario.inputGroupName;
470
+ return `when all inputs in ${bold(groupName)} are at ${bold(positionName(position))}...`;
471
+ } else {
472
+ const inputMessages = checkScenario.inputDescs.map(inputMessage).join(" and ");
473
+ return `when ${inputMessages}...`;
474
+ }
475
+ }
476
+ function datasetMessage(dataset, bold) {
477
+ const checkDataset = dataset.checkDataset;
478
+ if (checkDataset.datasetKey === void 0) {
479
+ return `error: ${bold(checkDataset.name)} did not match any datasets`;
480
+ } else {
481
+ return `then ${bold(checkDataset.name)}...`;
482
+ }
483
+ }
484
+ function predicateMessage(predicate, bold) {
485
+ const result = predicate.result;
486
+ if (result.status === "error") {
487
+ if (result.message) {
488
+ return `error: ${predicate.result.message}`;
489
+ } else if (result.errorInfo) {
490
+ switch (result.errorInfo.kind) {
491
+ case "unknown-dataset":
492
+ return `error: referenced dataset ${bold(result.errorInfo.name)} is unknown`;
493
+ case "unknown-input":
494
+ return `error: referenced input ${bold(result.errorInfo.name)} is unknown`;
495
+ case "unknown-input-group":
496
+ return `error: referenced input group ${bold(result.errorInfo.name)} is unknown`;
497
+ case "empty-input-group":
498
+ return `error: referenced input group ${bold(result.errorInfo.name)} is empty`;
499
+ default:
500
+ (0, import_assert_never3.default)(result.errorInfo.kind);
501
+ }
502
+ } else {
503
+ return `unknown error`;
504
+ }
505
+ }
506
+ const predicateParts = predicate.opValues.map(bold).join(" and ");
507
+ let msg = `should be ${predicateParts}`;
508
+ if (predicate.time !== void 0) {
509
+ if (typeof predicate.time === "number") {
510
+ msg += ` in ${bold(predicate.time.toString())}`;
511
+ } else {
512
+ let minTime;
513
+ let maxTime;
514
+ let minIncl;
515
+ let maxIncl;
516
+ if (Array.isArray(predicate.time)) {
517
+ const timeSpec = predicate.time;
518
+ minTime = timeSpec[0];
519
+ maxTime = timeSpec[1];
520
+ minIncl = true;
521
+ maxIncl = true;
522
+ } else {
523
+ const timeSpec = predicate.time;
524
+ if (timeSpec.after_excl !== void 0) {
525
+ minTime = timeSpec.after_excl;
526
+ minIncl = false;
527
+ } else if (timeSpec.after_incl !== void 0) {
528
+ minTime = timeSpec.after_incl;
529
+ minIncl = true;
530
+ }
531
+ if (timeSpec.before_excl !== void 0) {
532
+ maxTime = timeSpec.before_excl;
533
+ maxIncl = false;
534
+ } else if (timeSpec.before_incl !== void 0) {
535
+ maxTime = timeSpec.before_incl;
536
+ maxIncl = true;
537
+ }
538
+ }
539
+ if (minTime !== void 0 && maxTime !== void 0) {
540
+ const prefix = minIncl ? "[" : "(";
541
+ const suffix = maxIncl ? "]" : ")";
542
+ const range = `${prefix}${minTime}, ${maxTime}${suffix}`;
543
+ msg += ` in ${bold(range)}`;
544
+ } else if (minTime !== void 0) {
545
+ const prefix = minIncl ? "in/after" : "after";
546
+ msg += ` ${prefix} ${bold(minTime.toString())}`;
547
+ } else if (maxTime !== void 0) {
548
+ const prefix = maxIncl ? "in/before" : "before";
549
+ msg += ` ${prefix} ${bold(maxTime.toString())}`;
550
+ }
551
+ }
552
+ }
553
+ if (predicate.result.status === "failed") {
554
+ if (predicate.result.failValue !== void 0) {
555
+ msg += ` but got ${bold(predicate.result.failValue.toString())}`;
556
+ if (predicate.result.failRefValue !== void 0) {
557
+ const failSym = symbolForPredicateOp(predicate.result.failOp);
558
+ const refValue = `${failSym} ${predicate.result.failRefValue.toString()}`;
559
+ msg += ` (expected ${bold(refValue)})`;
560
+ }
561
+ } else if (predicate.result.message) {
562
+ msg += ` but got ${bold(predicate.result.message)}`;
563
+ }
564
+ if (predicate.result.failTime !== void 0) {
565
+ msg += ` in ${bold(predicate.result.failTime.toString())}`;
566
+ }
567
+ } else if (predicate.result.status === "error" && predicate.result.message) {
568
+ msg += ` but got error: ${bold(predicate.result.message)}`;
569
+ }
570
+ return msg;
571
+ }
572
+
573
+ // src/check/check-summary.ts
574
+ var import_assert_never6 = require("assert-never");
575
+
576
+ // src/check/check-parser.ts
577
+ var import_ajv = __toESM(require("ajv"), 1);
578
+ var import_neverthrow = require("neverthrow");
579
+ var import_yaml = __toESM(require("yaml"), 1);
580
+
581
+ // src/check/check.schema.js
582
+ var check_schema_default = {
583
+ $schema: "http://json-schema.org/draft-07/schema#",
584
+ title: "Model Check Test",
585
+ type: "array",
586
+ description: "A group of tests.",
587
+ items: {
588
+ $ref: "#/$defs/group"
589
+ },
590
+ $defs: {
591
+ group: {
592
+ type: "object",
593
+ additionalProperties: false,
594
+ properties: {
595
+ describe: {
596
+ type: "string"
597
+ },
598
+ tests: {
599
+ type: "array",
600
+ items: {
601
+ $ref: "#/$defs/test"
602
+ }
603
+ }
604
+ },
605
+ required: ["describe", "tests"]
606
+ },
607
+ test: {
608
+ type: "object",
609
+ additionalProperties: false,
610
+ properties: {
611
+ it: {
612
+ type: "string"
613
+ },
614
+ scenarios: {
615
+ type: "array",
616
+ items: {
617
+ $ref: "#/$defs/scenario"
618
+ },
619
+ minItems: 1
620
+ },
621
+ datasets: {
622
+ type: "array",
623
+ items: {
624
+ $ref: "#/$defs/dataset"
625
+ },
626
+ minItems: 1
627
+ },
628
+ predicates: {
629
+ type: "array",
630
+ items: {
631
+ $ref: "#/$defs/predicate"
632
+ },
633
+ minItems: 1
634
+ }
635
+ },
636
+ required: ["it", "datasets", "predicates"]
637
+ },
638
+ scenario: {
639
+ oneOf: [
640
+ { $ref: "#/$defs/scenario_with_input_at_position" },
641
+ { $ref: "#/$defs/scenario_with_input_at_value" },
642
+ { $ref: "#/$defs/scenario_with_multiple_input_settings" },
643
+ { $ref: "#/$defs/scenario_with_inputs_in_preset_at_position" },
644
+ { $ref: "#/$defs/scenario_with_inputs_in_group_at_position" },
645
+ { $ref: "#/$defs/scenario_preset" },
646
+ { $ref: "#/$defs/scenario_expand_for_each_input_in_group" }
647
+ ]
648
+ },
649
+ scenario_position: {
650
+ type: "string",
651
+ enum: ["min", "max", "default"]
652
+ },
653
+ scenario_with_input_at_position: {
654
+ type: "object",
655
+ additionalProperties: false,
656
+ properties: {
657
+ with: {
658
+ type: "string"
659
+ },
660
+ at: {
661
+ $ref: "#/$defs/scenario_position"
662
+ }
663
+ },
664
+ required: ["with", "at"]
665
+ },
666
+ scenario_with_input_at_value: {
667
+ type: "object",
668
+ additionalProperties: false,
669
+ properties: {
670
+ with: {
671
+ type: "string"
672
+ },
673
+ at: {
674
+ type: "number"
675
+ }
676
+ },
677
+ required: ["with", "at"]
678
+ },
679
+ scenario_input_at_position: {
680
+ type: "object",
681
+ additionalProperties: false,
682
+ properties: {
683
+ input: {
684
+ type: "string"
685
+ },
686
+ at: {
687
+ $ref: "#/$defs/scenario_position"
688
+ }
689
+ },
690
+ required: ["input", "at"]
691
+ },
692
+ scenario_input_at_value: {
693
+ type: "object",
694
+ additionalProperties: false,
695
+ properties: {
696
+ input: {
697
+ type: "string"
698
+ },
699
+ at: {
700
+ type: "number"
701
+ }
702
+ },
703
+ required: ["input", "at"]
704
+ },
705
+ scenario_input_setting: {
706
+ oneOf: [{ $ref: "#/$defs/scenario_input_at_position" }, { $ref: "#/$defs/scenario_input_at_value" }]
707
+ },
708
+ scenario_input_setting_array: {
709
+ type: "array",
710
+ items: {
711
+ $ref: "#/$defs/scenario_input_setting"
712
+ },
713
+ minItems: 1
714
+ },
715
+ scenario_with_multiple_input_settings: {
716
+ type: "object",
717
+ additionalProperties: false,
718
+ properties: {
719
+ with: {
720
+ $ref: "#/$defs/scenario_input_setting_array"
721
+ }
722
+ },
723
+ required: ["with"]
724
+ },
725
+ scenario_with_inputs_in_preset_at_position: {
726
+ type: "object",
727
+ additionalProperties: false,
728
+ properties: {
729
+ with_inputs: {
730
+ type: "string",
731
+ enum: ["all"]
732
+ },
733
+ at: {
734
+ $ref: "#/$defs/scenario_position"
735
+ }
736
+ },
737
+ required: ["with_inputs", "at"]
738
+ },
739
+ scenario_with_inputs_in_group_at_position: {
740
+ type: "object",
741
+ additionalProperties: false,
742
+ properties: {
743
+ with_inputs_in: {
744
+ type: "string"
745
+ },
746
+ at: {
747
+ $ref: "#/$defs/scenario_position"
748
+ }
749
+ },
750
+ required: ["with_inputs_in", "at"]
751
+ },
752
+ scenario_preset: {
753
+ type: "object",
754
+ additionalProperties: false,
755
+ properties: {
756
+ preset: {
757
+ type: "string",
758
+ enum: ["matrix"]
759
+ }
760
+ },
761
+ required: ["preset"]
762
+ },
763
+ scenario_expand_for_each_input_in_group: {
764
+ type: "object",
765
+ additionalProperties: false,
766
+ properties: {
767
+ scenarios_for_each_input_in: {
768
+ type: "string"
769
+ },
770
+ at: {
771
+ $ref: "#/$defs/scenario_position"
772
+ }
773
+ },
774
+ required: ["scenarios_for_each_input_in", "at"]
775
+ },
776
+ dataset: {
777
+ oneOf: [{ $ref: "#/$defs/dataset_name" }, { $ref: "#/$defs/dataset_group" }, { $ref: "#/$defs/dataset_matching" }]
778
+ },
779
+ dataset_name: {
780
+ type: "object",
781
+ additionalProperties: false,
782
+ properties: {
783
+ name: {
784
+ type: "string"
785
+ },
786
+ source: {
787
+ type: "string"
788
+ }
789
+ },
790
+ required: ["name"]
791
+ },
792
+ dataset_group: {
793
+ type: "object",
794
+ additionalProperties: false,
795
+ properties: {
796
+ group: {
797
+ type: "string"
798
+ }
799
+ },
800
+ required: ["group"]
801
+ },
802
+ dataset_matching: {
803
+ type: "object",
804
+ additionalProperties: false,
805
+ properties: {
806
+ matching: {
807
+ type: "object",
808
+ additionalProperties: false,
809
+ properties: {
810
+ type: {
811
+ type: "string"
812
+ }
813
+ },
814
+ required: ["type"]
815
+ }
816
+ },
817
+ required: ["matching"]
818
+ },
819
+ predicate: {
820
+ type: "object",
821
+ oneOf: [
822
+ { $ref: "#/$defs/predicate_gt" },
823
+ { $ref: "#/$defs/predicate_gte" },
824
+ { $ref: "#/$defs/predicate_lt" },
825
+ { $ref: "#/$defs/predicate_lte" },
826
+ { $ref: "#/$defs/predicate_gt_lt" },
827
+ { $ref: "#/$defs/predicate_gt_lte" },
828
+ { $ref: "#/$defs/predicate_gte_lt" },
829
+ { $ref: "#/$defs/predicate_gte_lte" },
830
+ { $ref: "#/$defs/predicate_eq" },
831
+ { $ref: "#/$defs/predicate_approx" }
832
+ ]
833
+ },
834
+ predicate_gt: {
835
+ type: "object",
836
+ additionalProperties: false,
837
+ properties: {
838
+ gt: { $ref: "#/$defs/predicate_ref" },
839
+ time: { $ref: "#/$defs/predicate_time" }
840
+ },
841
+ required: ["gt"]
842
+ },
843
+ predicate_gte: {
844
+ type: "object",
845
+ additionalProperties: false,
846
+ properties: {
847
+ gte: { $ref: "#/$defs/predicate_ref" },
848
+ time: { $ref: "#/$defs/predicate_time" }
849
+ },
850
+ required: ["gte"]
851
+ },
852
+ predicate_lt: {
853
+ type: "object",
854
+ additionalProperties: false,
855
+ properties: {
856
+ lt: { $ref: "#/$defs/predicate_ref" },
857
+ time: { $ref: "#/$defs/predicate_time" }
858
+ },
859
+ required: ["lt"]
860
+ },
861
+ predicate_lte: {
862
+ type: "object",
863
+ additionalProperties: false,
864
+ properties: {
865
+ lte: { $ref: "#/$defs/predicate_ref" },
866
+ time: { $ref: "#/$defs/predicate_time" }
867
+ },
868
+ required: ["lte"]
869
+ },
870
+ predicate_gt_lt: {
871
+ type: "object",
872
+ additionalProperties: false,
873
+ properties: {
874
+ gt: { $ref: "#/$defs/predicate_ref" },
875
+ lt: { $ref: "#/$defs/predicate_ref" },
876
+ time: { $ref: "#/$defs/predicate_time" }
877
+ },
878
+ required: ["gt", "lt"]
879
+ },
880
+ predicate_gt_lte: {
881
+ type: "object",
882
+ additionalProperties: false,
883
+ properties: {
884
+ gt: { $ref: "#/$defs/predicate_ref" },
885
+ lte: { $ref: "#/$defs/predicate_ref" },
886
+ time: { $ref: "#/$defs/predicate_time" }
887
+ },
888
+ required: ["gt", "lte"]
889
+ },
890
+ predicate_gte_lt: {
891
+ type: "object",
892
+ additionalProperties: false,
893
+ properties: {
894
+ gte: { $ref: "#/$defs/predicate_ref" },
895
+ lt: { $ref: "#/$defs/predicate_ref" },
896
+ time: { $ref: "#/$defs/predicate_time" }
897
+ },
898
+ required: ["gte", "lt"]
899
+ },
900
+ predicate_gte_lte: {
901
+ type: "object",
902
+ additionalProperties: false,
903
+ properties: {
904
+ gte: { $ref: "#/$defs/predicate_ref" },
905
+ lte: { $ref: "#/$defs/predicate_ref" },
906
+ time: { $ref: "#/$defs/predicate_time" }
907
+ },
908
+ required: ["gte", "lte"]
909
+ },
910
+ predicate_eq: {
911
+ type: "object",
912
+ additionalProperties: false,
913
+ properties: {
914
+ eq: { $ref: "#/$defs/predicate_ref" },
915
+ time: { $ref: "#/$defs/predicate_time" }
916
+ },
917
+ required: ["eq"]
918
+ },
919
+ predicate_approx: {
920
+ type: "object",
921
+ additionalProperties: false,
922
+ properties: {
923
+ approx: { $ref: "#/$defs/predicate_ref" },
924
+ tolerance: { type: "number" },
925
+ time: { $ref: "#/$defs/predicate_time" }
926
+ },
927
+ required: ["approx"]
928
+ },
929
+ predicate_ref: {
930
+ oneOf: [{ $ref: "#/$defs/predicate_ref_constant" }, { $ref: "#/$defs/predicate_ref_data" }]
931
+ },
932
+ predicate_ref_constant: {
933
+ type: "number"
934
+ },
935
+ predicate_ref_data: {
936
+ type: "object",
937
+ additionalProperties: false,
938
+ properties: {
939
+ dataset: { $ref: "#/$defs/predicate_ref_data_dataset" },
940
+ scenario: { $ref: "#/$defs/predicate_ref_data_scenario" }
941
+ },
942
+ required: ["dataset"]
943
+ },
944
+ predicate_ref_data_dataset: {
945
+ oneOf: [{ $ref: "#/$defs/dataset_name" }, { $ref: "#/$defs/predicate_ref_data_dataset_special" }]
946
+ },
947
+ predicate_ref_data_dataset_special: {
948
+ type: "string",
949
+ enum: ["inherit"]
950
+ },
951
+ predicate_ref_data_scenario: {
952
+ oneOf: [
953
+ { $ref: "#/$defs/scenario_with_input_at_position" },
954
+ { $ref: "#/$defs/scenario_with_input_at_value" },
955
+ { $ref: "#/$defs/scenario_with_multiple_input_settings" },
956
+ { $ref: "#/$defs/scenario_with_inputs_in_preset_at_position" },
957
+ { $ref: "#/$defs/scenario_with_inputs_in_group_at_position" },
958
+ { $ref: "#/$defs/predicate_ref_data_scenario_special" }
959
+ ]
960
+ },
961
+ predicate_ref_data_scenario_special: {
962
+ type: "string",
963
+ enum: ["inherit"]
964
+ },
965
+ predicate_time: {
966
+ oneOf: [
967
+ { $ref: "#/$defs/predicate_time_single" },
968
+ { $ref: "#/$defs/predicate_time_pair" },
969
+ { $ref: "#/$defs/predicate_time_gt" },
970
+ { $ref: "#/$defs/predicate_time_gte" },
971
+ { $ref: "#/$defs/predicate_time_lt" },
972
+ { $ref: "#/$defs/predicate_time_lte" },
973
+ { $ref: "#/$defs/predicate_time_gt_lt" },
974
+ { $ref: "#/$defs/predicate_time_gt_lte" },
975
+ { $ref: "#/$defs/predicate_time_gte_lt" },
976
+ { $ref: "#/$defs/predicate_time_gte_lte" }
977
+ ]
978
+ },
979
+ predicate_time_single: {
980
+ type: "number"
981
+ },
982
+ predicate_time_pair: {
983
+ type: "array",
984
+ items: [{ type: "number" }, { type: "number" }],
985
+ minItems: 2,
986
+ maxItems: 2
987
+ },
988
+ predicate_time_gt: {
989
+ type: "object",
990
+ additionalProperties: false,
991
+ properties: {
992
+ after_excl: { type: "number" }
993
+ },
994
+ required: ["after_excl"]
995
+ },
996
+ predicate_time_gte: {
997
+ type: "object",
998
+ additionalProperties: false,
999
+ properties: {
1000
+ after_incl: { type: "number" }
1001
+ },
1002
+ required: ["after_incl"]
1003
+ },
1004
+ predicate_time_lt: {
1005
+ type: "object",
1006
+ additionalProperties: false,
1007
+ properties: {
1008
+ before_excl: { type: "number" }
1009
+ },
1010
+ required: ["before_excl"]
1011
+ },
1012
+ predicate_time_lte: {
1013
+ type: "object",
1014
+ additionalProperties: false,
1015
+ properties: {
1016
+ before_incl: { type: "number" }
1017
+ },
1018
+ required: ["before_incl"]
1019
+ },
1020
+ predicate_time_gt_lt: {
1021
+ type: "object",
1022
+ additionalProperties: false,
1023
+ properties: {
1024
+ after_excl: { type: "number" },
1025
+ before_excl: { type: "number" }
1026
+ },
1027
+ required: ["after_excl", "before_excl"]
1028
+ },
1029
+ predicate_time_gt_lte: {
1030
+ type: "object",
1031
+ additionalProperties: false,
1032
+ properties: {
1033
+ after_excl: { type: "number" },
1034
+ before_incl: { type: "number" }
1035
+ },
1036
+ required: ["after_excl", "before_incl"]
1037
+ },
1038
+ predicate_time_gte_lt: {
1039
+ type: "object",
1040
+ additionalProperties: false,
1041
+ properties: {
1042
+ after_incl: { type: "number" },
1043
+ before_excl: { type: "number" }
1044
+ },
1045
+ required: ["after_incl", "before_excl"]
1046
+ },
1047
+ predicate_time_gte_lte: {
1048
+ type: "object",
1049
+ additionalProperties: false,
1050
+ properties: {
1051
+ after_incl: { type: "number" },
1052
+ before_incl: { type: "number" }
1053
+ },
1054
+ required: ["after_incl", "before_incl"]
1055
+ }
1056
+ }
1057
+ };
1058
+
1059
+ // src/check/check-parser.ts
1060
+ function parseTestYaml(yamlStrings) {
1061
+ const groups = [];
1062
+ const ajv = new import_ajv.default();
1063
+ const validate = ajv.compile(check_schema_default);
1064
+ for (const yamlString of yamlStrings) {
1065
+ const parsed = import_yaml.default.parse(yamlString);
1066
+ if (validate(parsed)) {
1067
+ for (const group of parsed) {
1068
+ groups.push(group);
1069
+ }
1070
+ } else {
1071
+ let msg = "Failed to parse YAML tests";
1072
+ for (const error of validate.errors || []) {
1073
+ if (error.message) {
1074
+ msg += `
1075
+ ${error.message}`;
1076
+ }
1077
+ }
1078
+ return (0, import_neverthrow.err)(new Error(msg));
1079
+ }
1080
+ }
1081
+ const checkSpec = {
1082
+ groups
1083
+ };
1084
+ return (0, import_neverthrow.ok)(checkSpec);
1085
+ }
1086
+
1087
+ // src/check/check-planner.ts
1088
+ var import_assert_never5 = __toESM(require("assert-never"), 1);
1089
+
1090
+ // src/check/check-func.ts
1091
+ var passed = {
1092
+ status: "passed"
1093
+ };
1094
+ var gt = (a, b) => a > b;
1095
+ var gte = (a, b) => a >= b;
1096
+ var lt = (a, b) => a < b;
1097
+ var lte = (a, b) => a <= b;
1098
+ var eq = (a, b) => a === b;
1099
+ var approx = (tolerance) => {
1100
+ const f = (a, b) => {
1101
+ return a >= b - tolerance && a <= b + tolerance;
1102
+ };
1103
+ return f;
1104
+ };
1105
+ function checkFunc(spec) {
1106
+ function addCheckValueFunc(op, compareFunc) {
1107
+ const refSpec = spec[op];
1108
+ if (refSpec === void 0) {
1109
+ return;
1110
+ }
1111
+ if (typeof refSpec === "number") {
1112
+ checkValueFuncs.push((value, time) => {
1113
+ if (compareFunc(value, refSpec)) {
1114
+ return passed;
1115
+ } else {
1116
+ return {
1117
+ status: "failed",
1118
+ failValue: value,
1119
+ failTime: time
1120
+ };
1121
+ }
1122
+ });
1123
+ } else {
1124
+ checkValueFuncs.push((value, time, refDatasets) => {
1125
+ const refDataset = refDatasets == null ? void 0 : refDatasets.get(op);
1126
+ if (refDataset === void 0) {
1127
+ return {
1128
+ status: "error",
1129
+ message: "unhandled data reference"
1130
+ };
1131
+ }
1132
+ const refValue = refDataset.get(time);
1133
+ if (refValue !== void 0) {
1134
+ if (compareFunc(value, refValue)) {
1135
+ return passed;
1136
+ } else {
1137
+ return {
1138
+ status: "failed",
1139
+ failValue: value,
1140
+ failOp: op,
1141
+ failRefValue: refValue,
1142
+ failTime: time
1143
+ };
1144
+ }
1145
+ } else {
1146
+ return {
1147
+ status: "failed",
1148
+ message: "no reference value",
1149
+ failTime: time
1150
+ };
1151
+ }
1152
+ });
1153
+ }
1154
+ }
1155
+ const checkValueFuncs = [];
1156
+ addCheckValueFunc("gt", gt);
1157
+ addCheckValueFunc("gte", gte);
1158
+ addCheckValueFunc("lt", lt);
1159
+ addCheckValueFunc("lte", lte);
1160
+ addCheckValueFunc("eq", eq);
1161
+ if (spec.approx !== void 0) {
1162
+ const tolerance = spec.tolerance || 0.1;
1163
+ addCheckValueFunc("approx", approx(tolerance));
1164
+ }
1165
+ const checkValue = (value, time, refDatasets) => {
1166
+ for (const f of checkValueFuncs) {
1167
+ const result = f(value, time, refDatasets);
1168
+ if (result.status !== "passed") {
1169
+ return result;
1170
+ }
1171
+ }
1172
+ return passed;
1173
+ };
1174
+ if (spec.time !== void 0 && typeof spec.time === "number") {
1175
+ const time = spec.time;
1176
+ return (dataset, refDatasets) => {
1177
+ const value = dataset.get(time);
1178
+ if (value !== void 0) {
1179
+ return checkValue(value, time, refDatasets);
1180
+ } else {
1181
+ return {
1182
+ status: "failed",
1183
+ message: "no value",
1184
+ failTime: time
1185
+ };
1186
+ }
1187
+ };
1188
+ } else {
1189
+ let checkTime;
1190
+ if (spec.time !== void 0) {
1191
+ if (Array.isArray(spec.time)) {
1192
+ const timeSpec = spec.time;
1193
+ checkTime = (time) => time >= timeSpec[0] && time <= timeSpec[1];
1194
+ } else {
1195
+ const checkTimeFuncs = [];
1196
+ const timeSpec = spec.time;
1197
+ if (timeSpec.after_excl !== void 0) {
1198
+ checkTimeFuncs.push((time) => time > timeSpec.after_excl);
1199
+ }
1200
+ if (timeSpec.after_incl !== void 0) {
1201
+ checkTimeFuncs.push((time) => time >= timeSpec.after_incl);
1202
+ }
1203
+ if (timeSpec.before_excl !== void 0) {
1204
+ checkTimeFuncs.push((time) => time < timeSpec.before_excl);
1205
+ }
1206
+ if (timeSpec.before_incl !== void 0) {
1207
+ checkTimeFuncs.push((time) => time <= timeSpec.before_incl);
1208
+ }
1209
+ checkTime = (time) => {
1210
+ for (const f of checkTimeFuncs) {
1211
+ if (!f(time)) {
1212
+ return false;
1213
+ }
1214
+ }
1215
+ return true;
1216
+ };
1217
+ }
1218
+ } else {
1219
+ checkTime = () => true;
1220
+ }
1221
+ return (dataset, refDatasets) => {
1222
+ for (const [time, value] of dataset) {
1223
+ if (checkTime(time)) {
1224
+ const result = checkValue(value, time, refDatasets);
1225
+ if (result.status !== "passed") {
1226
+ return result;
1227
+ }
1228
+ }
1229
+ }
1230
+ return passed;
1231
+ };
1232
+ }
1233
+ }
1234
+
1235
+ // src/check/check-action.ts
1236
+ function actionForPredicate(predicateSpec) {
1237
+ return {
1238
+ predicateSpec,
1239
+ run: checkFunc(predicateSpec)
1240
+ };
1241
+ }
1242
+
1243
+ // src/_shared/combo.ts
1244
+ function cartesianProductOf(arr) {
1245
+ return arr.reduce((a, b) => {
1246
+ return a.map((x) => b.map((y) => x.concat([y]))).reduce((v, w) => v.concat(w), []);
1247
+ }, [[]]);
1248
+ }
1249
+
1250
+ // src/check/check-dataset.ts
1251
+ function expandDatasets(modelSpec, datasetSpec) {
1252
+ var _a;
1253
+ let result;
1254
+ if (datasetSpec.name) {
1255
+ result = matchByName(modelSpec, datasetSpec.name, datasetSpec.source);
1256
+ } else if (datasetSpec.group) {
1257
+ result = matchByGroup(modelSpec, datasetSpec.group);
1258
+ } else if ((_a = datasetSpec.matching) == null ? void 0 : _a.type) {
1259
+ result = matchByType(modelSpec, datasetSpec.matching.type);
1260
+ }
1261
+ if (result.error) {
1262
+ return [
1263
+ {
1264
+ name: result.error.name,
1265
+ error: result.error.kind
1266
+ }
1267
+ ];
1268
+ }
1269
+ const matches = result.matches;
1270
+ const checkDatasets = [];
1271
+ for (const match of matches) {
1272
+ if (match.outputVar) {
1273
+ checkDatasets.push({
1274
+ datasetKey: match.datasetKey,
1275
+ name: match.outputVar.varName
1276
+ });
1277
+ } else if (match.implVar) {
1278
+ const implVar = match.implVar;
1279
+ if (implVar.dimensions.length > 0) {
1280
+ const baseDatasetKey = match.datasetKey;
1281
+ const subscripts = [...implVar.dimensions.map((dim) => dim.subscripts)];
1282
+ const subscriptCombos = cartesianProductOf(subscripts);
1283
+ for (const subscriptCombo of subscriptCombos) {
1284
+ const subIdParts = subscriptCombo.map((sub) => `[${sub.id}]`).join("");
1285
+ const subNameParts = subscriptCombo.map((sub) => sub.name).join(",");
1286
+ checkDatasets.push({
1287
+ datasetKey: `${baseDatasetKey}${subIdParts}`,
1288
+ name: `${implVar.varName}[${subNameParts}]`
1289
+ });
1290
+ }
1291
+ } else {
1292
+ checkDatasets.push({
1293
+ datasetKey: match.datasetKey,
1294
+ name: implVar.varName
1295
+ });
1296
+ }
1297
+ }
1298
+ }
1299
+ return checkDatasets;
1300
+ }
1301
+ function matchByName(modelSpec, datasetName, datasetSource) {
1302
+ var _a;
1303
+ const varNameToMatch = datasetName.toLowerCase();
1304
+ const sourceToMatch = datasetSource == null ? void 0 : datasetSource.toLowerCase();
1305
+ for (const [datasetKey, outputVar] of modelSpec.outputVars) {
1306
+ if (((_a = outputVar.sourceName) == null ? void 0 : _a.toLowerCase()) === sourceToMatch && outputVar.varName.toLowerCase() === varNameToMatch) {
1307
+ return {
1308
+ matches: [
1309
+ {
1310
+ datasetKey,
1311
+ outputVar
1312
+ }
1313
+ ]
1314
+ };
1315
+ }
1316
+ }
1317
+ for (const [datasetKey, implVar] of modelSpec.implVars) {
1318
+ if (implVar.varName.toLowerCase() === varNameToMatch) {
1319
+ return {
1320
+ matches: [
1321
+ {
1322
+ datasetKey,
1323
+ implVar
1324
+ }
1325
+ ]
1326
+ };
1327
+ }
1328
+ }
1329
+ return {
1330
+ matches: [],
1331
+ error: {
1332
+ kind: "no-matches-for-dataset",
1333
+ name: datasetName
1334
+ }
1335
+ };
1336
+ }
1337
+ function matchByGroup(modelSpec, groupName) {
1338
+ const groupToMatch = groupName.toLowerCase();
1339
+ let matchedGroupName;
1340
+ let matchedGroupDatasetKeys;
1341
+ for (const [group, datasetKeys] of modelSpec.datasetGroups) {
1342
+ if (group.toLowerCase() === groupToMatch) {
1343
+ matchedGroupName = group;
1344
+ matchedGroupDatasetKeys = datasetKeys;
1345
+ break;
1346
+ }
1347
+ }
1348
+ if (matchedGroupName === void 0) {
1349
+ return {
1350
+ matches: [],
1351
+ error: {
1352
+ kind: "no-matches-for-group",
1353
+ name: groupName
1354
+ }
1355
+ };
1356
+ }
1357
+ const matches = [];
1358
+ for (const datasetKey of matchedGroupDatasetKeys) {
1359
+ const outputVar = modelSpec.outputVars.get(datasetKey);
1360
+ if (outputVar) {
1361
+ matches.push({
1362
+ datasetKey,
1363
+ outputVar
1364
+ });
1365
+ continue;
1366
+ }
1367
+ const implVar = modelSpec.implVars.get(datasetKey);
1368
+ if (implVar) {
1369
+ matches.push({
1370
+ datasetKey,
1371
+ implVar
1372
+ });
1373
+ continue;
1374
+ }
1375
+ return {
1376
+ matches: [],
1377
+ error: {
1378
+ kind: "no-matches-for-dataset",
1379
+ name: datasetKey
1380
+ }
1381
+ };
1382
+ }
1383
+ if (matches.length === 0) {
1384
+ return {
1385
+ matches: [],
1386
+ error: {
1387
+ kind: "no-matches-for-group",
1388
+ name: matchedGroupName
1389
+ }
1390
+ };
1391
+ }
1392
+ return {
1393
+ matches
1394
+ };
1395
+ }
1396
+ function matchByType(modelSpec, varTypeToMatch) {
1397
+ const matches = [];
1398
+ for (const [datasetKey, implVar] of modelSpec.implVars) {
1399
+ if (implVar.varType === varTypeToMatch) {
1400
+ matches.push({
1401
+ datasetKey,
1402
+ implVar
1403
+ });
1404
+ }
1405
+ }
1406
+ if (matches.length === 0) {
1407
+ return {
1408
+ matches: [],
1409
+ error: {
1410
+ kind: "no-matches-for-type",
1411
+ name: varTypeToMatch
1412
+ }
1413
+ };
1414
+ }
1415
+ return {
1416
+ matches
1417
+ };
1418
+ }
1419
+
1420
+ // src/check/check-scenario.ts
1421
+ var import_assert_never4 = __toESM(require("assert-never"), 1);
1422
+ function expandScenarios(modelSpec, scenarioSpecs, simplify) {
1423
+ if (scenarioSpecs.length === 0) {
1424
+ const scenarioSpec = {
1425
+ with_inputs: "all",
1426
+ at: "default"
1427
+ };
1428
+ return checkScenariosFromSpec(modelSpec, scenarioSpec, simplify);
1429
+ }
1430
+ const checkScenarios = [];
1431
+ for (const scenarioSpec of scenarioSpecs) {
1432
+ checkScenarios.push(...checkScenariosFromSpec(modelSpec, scenarioSpec, simplify));
1433
+ }
1434
+ return checkScenarios;
1435
+ }
1436
+ function inputPosition(position) {
1437
+ switch (position) {
1438
+ case "default":
1439
+ return "at-default";
1440
+ case "min":
1441
+ return "at-minimum";
1442
+ case "max":
1443
+ return "at-maximum";
1444
+ default:
1445
+ return void 0;
1446
+ }
1447
+ }
1448
+ function inputValueAtPosition(inputVar, position) {
1449
+ switch (position) {
1450
+ case "at-default":
1451
+ return inputVar.defaultValue;
1452
+ case "at-minimum":
1453
+ return inputVar.minValue;
1454
+ case "at-maximum":
1455
+ return inputVar.maxValue;
1456
+ default:
1457
+ (0, import_assert_never4.default)(position);
1458
+ }
1459
+ }
1460
+ function inputDescAtPosition(inputVar, position) {
1461
+ return {
1462
+ name: inputVar.varName,
1463
+ inputVar,
1464
+ position,
1465
+ value: inputValueAtPosition(inputVar, position)
1466
+ };
1467
+ }
1468
+ function inputDescAtValue(inputVar, value) {
1469
+ return {
1470
+ name: inputVar.varName,
1471
+ inputVar,
1472
+ value
1473
+ };
1474
+ }
1475
+ function inputDescForVar(inputVar, at) {
1476
+ if (typeof at === "number") {
1477
+ const value = at;
1478
+ return inputDescAtValue(inputVar, value);
1479
+ } else {
1480
+ const position = inputPosition(at);
1481
+ return inputDescAtPosition(inputVar, position);
1482
+ }
1483
+ }
1484
+ function inputDescForName(modelSpec, inputName, at) {
1485
+ const inputNameToMatch = inputName.toLowerCase();
1486
+ const inputVar = [...modelSpec.inputVars.values()].find((inputVar2) => {
1487
+ return inputVar2.varName.toLowerCase() === inputNameToMatch;
1488
+ });
1489
+ if (inputVar) {
1490
+ return inputDescForVar(inputVar, at);
1491
+ } else {
1492
+ return {
1493
+ name: inputName
1494
+ };
1495
+ }
1496
+ }
1497
+ function groupForName(modelSpec, groupName) {
1498
+ const groupToMatch = groupName.toLowerCase();
1499
+ for (const [group, inputVars] of modelSpec.inputGroups) {
1500
+ if (group.toLowerCase() === groupToMatch) {
1501
+ return [group, inputVars];
1502
+ }
1503
+ }
1504
+ return void 0;
1505
+ }
1506
+ function errorScenarioForInputGroup(kind, groupName) {
1507
+ return {
1508
+ inputDescs: [],
1509
+ error: {
1510
+ kind,
1511
+ name: groupName
1512
+ }
1513
+ };
1514
+ }
1515
+ function checkScenarioWithAllInputsAtPosition(position) {
1516
+ const scenario = allInputsAtPositionScenario(position);
1517
+ return {
1518
+ scenario,
1519
+ inputDescs: []
1520
+ };
1521
+ }
1522
+ function checkScenarioWithInputAtPosition(inputVar, position) {
1523
+ const varId = inputVar.varId;
1524
+ const scenario = inputAtPositionScenario(varId, varId, position);
1525
+ return {
1526
+ scenario,
1527
+ inputDescs: [inputDescAtPosition(inputVar, position)]
1528
+ };
1529
+ }
1530
+ function checkScenarioForInputDescs(groupName, inputDescs) {
1531
+ let scenario;
1532
+ if (inputDescs.every((desc) => desc.inputVar !== void 0)) {
1533
+ const settings = [];
1534
+ const keyParts = [];
1535
+ for (const inputDesc of inputDescs) {
1536
+ const varId = inputDesc.inputVar.varId;
1537
+ if (inputDesc.position) {
1538
+ settings.push(positionSetting(varId, inputDesc.position));
1539
+ keyParts.push(keyForInputAtPosition(varId, inputDesc.position));
1540
+ } else {
1541
+ settings.push(valueSetting(varId, inputDesc.value));
1542
+ keyParts.push(keyForInputAtValue(varId, inputDesc.value));
1543
+ }
1544
+ }
1545
+ if (settings.length === 1) {
1546
+ const scenarioKey = `input${keyParts[0]}`;
1547
+ const groupKey = settings[0].inputVarId;
1548
+ scenario = settingsScenario(scenarioKey, groupKey, settings);
1549
+ } else if (settings.length > 1) {
1550
+ let scenarioKey;
1551
+ let groupKey;
1552
+ if (groupName) {
1553
+ scenarioKey = `group_${groupName.toLowerCase().replace(/ /g, "_")}`;
1554
+ groupKey = scenarioKey;
1555
+ } else {
1556
+ scenarioKey = "multi" + keyParts.join("_");
1557
+ groupKey = scenarioKey;
1558
+ }
1559
+ scenario = settingsScenario(scenarioKey, groupKey, settings);
1560
+ }
1561
+ } else {
1562
+ scenario = void 0;
1563
+ }
1564
+ return {
1565
+ scenario,
1566
+ inputGroupName: groupName,
1567
+ inputDescs
1568
+ };
1569
+ }
1570
+ function checkScenarioForInputSpecs(modelSpec, inputSpecs) {
1571
+ const inputDescs = inputSpecs.map((inputSpec) => {
1572
+ return inputDescForName(modelSpec, inputSpec.input, inputSpec.at);
1573
+ });
1574
+ return checkScenarioForInputDescs(void 0, inputDescs);
1575
+ }
1576
+ function checkScenarioMatrix(modelSpec, simplify) {
1577
+ const checkScenarios = [];
1578
+ checkScenarios.push(checkScenarioWithAllInputsAtPosition("at-default"));
1579
+ if (!simplify) {
1580
+ checkScenarios.push(checkScenarioWithAllInputsAtPosition("at-minimum"));
1581
+ checkScenarios.push(checkScenarioWithAllInputsAtPosition("at-maximum"));
1582
+ for (const inputVar of modelSpec.inputVars.values()) {
1583
+ checkScenarios.push(checkScenarioWithInputAtPosition(inputVar, "at-minimum"));
1584
+ checkScenarios.push(checkScenarioWithInputAtPosition(inputVar, "at-maximum"));
1585
+ }
1586
+ }
1587
+ return checkScenarios;
1588
+ }
1589
+ function checkScenarioWithAllInputsInGroupAtPosition(modelSpec, groupName, position) {
1590
+ const result = groupForName(modelSpec, groupName);
1591
+ if (result === void 0) {
1592
+ return errorScenarioForInputGroup("unknown-input-group", groupName);
1593
+ }
1594
+ const [matchedGroupName, inputVars] = result;
1595
+ if (inputVars.length === 0) {
1596
+ return errorScenarioForInputGroup("empty-input-group", matchedGroupName);
1597
+ }
1598
+ const inputDescs = [];
1599
+ for (const inputVar of inputVars) {
1600
+ inputDescs.push(inputDescForVar(inputVar, position));
1601
+ }
1602
+ return checkScenarioForInputDescs(matchedGroupName, inputDescs);
1603
+ }
1604
+ function checkScenariosForEachInputInGroup(modelSpec, groupName, position) {
1605
+ const result = groupForName(modelSpec, groupName);
1606
+ if (result === void 0) {
1607
+ return [errorScenarioForInputGroup("unknown-input-group", groupName)];
1608
+ }
1609
+ const [matchedGroupName, inputVars] = result;
1610
+ if (inputVars.length === 0) {
1611
+ return [errorScenarioForInputGroup("empty-input-group", matchedGroupName)];
1612
+ }
1613
+ const checkScenarios = [];
1614
+ for (const inputVar of inputVars) {
1615
+ const inputDesc = inputDescForVar(inputVar, position);
1616
+ checkScenarios.push(checkScenarioForInputDescs(void 0, [inputDesc]));
1617
+ }
1618
+ return checkScenarios;
1619
+ }
1620
+ function checkScenariosFromSpec(modelSpec, scenarioSpec, simplify) {
1621
+ if (scenarioSpec.preset === "matrix") {
1622
+ return checkScenarioMatrix(modelSpec, simplify);
1623
+ }
1624
+ if (scenarioSpec.scenarios_for_each_input_in !== void 0) {
1625
+ const groupName = scenarioSpec.scenarios_for_each_input_in;
1626
+ const position = scenarioSpec.at;
1627
+ return checkScenariosForEachInputInGroup(modelSpec, groupName, position);
1628
+ }
1629
+ if (scenarioSpec.with !== void 0) {
1630
+ if (Array.isArray(scenarioSpec.with)) {
1631
+ const inputSpecs = scenarioSpec.with;
1632
+ return [checkScenarioForInputSpecs(modelSpec, inputSpecs)];
1633
+ } else {
1634
+ const inputSpec = {
1635
+ input: scenarioSpec.with,
1636
+ at: scenarioSpec.at
1637
+ };
1638
+ return [checkScenarioForInputSpecs(modelSpec, [inputSpec])];
1639
+ }
1640
+ }
1641
+ if (scenarioSpec.with_inputs === "all") {
1642
+ const position = inputPosition(scenarioSpec.at);
1643
+ return [checkScenarioWithAllInputsAtPosition(position)];
1644
+ }
1645
+ if (scenarioSpec.with_inputs_in !== void 0) {
1646
+ const groupName = scenarioSpec.with_inputs_in;
1647
+ const position = scenarioSpec.at;
1648
+ return [checkScenarioWithAllInputsInGroupAtPosition(modelSpec, groupName, position)];
1649
+ }
1650
+ throw new Error(`Unhandled scenario spec: ${JSON.stringify(scenarioSpec)}`);
1651
+ }
1652
+
1653
+ // src/check/check-planner.ts
1654
+ var CheckPlanner = class {
1655
+ constructor(modelSpec) {
1656
+ this.modelSpec = modelSpec;
1657
+ this.groups = [];
1658
+ this.tasks = /* @__PURE__ */ new Map();
1659
+ this.dataRefs = /* @__PURE__ */ new Map();
1660
+ this.checkKey = 1;
1661
+ }
1662
+ addAllChecks(checkSpec, simplifyScenarios) {
1663
+ for (const groupSpec of checkSpec.groups) {
1664
+ const groupName = groupSpec.describe;
1665
+ const planTests = [];
1666
+ for (const testSpec of groupSpec.tests) {
1667
+ const testName = testSpec.it;
1668
+ const checkScenarios = expandScenarios(this.modelSpec, testSpec.scenarios || [], simplifyScenarios);
1669
+ const checkDatasets = [];
1670
+ for (const datasetSpec of testSpec.datasets) {
1671
+ checkDatasets.push(...expandDatasets(this.modelSpec, datasetSpec));
1672
+ }
1673
+ const checkActions = [];
1674
+ for (const predicateSpec of testSpec.predicates) {
1675
+ checkActions.push(actionForPredicate(predicateSpec));
1676
+ }
1677
+ const planScenarios = [];
1678
+ for (const checkScenario of checkScenarios) {
1679
+ if (checkScenario.scenario === void 0) {
1680
+ planScenarios.push({
1681
+ checkScenario,
1682
+ datasets: []
1683
+ });
1684
+ continue;
1685
+ }
1686
+ const planDatasets = [];
1687
+ for (const checkDataset of checkDatasets) {
1688
+ if (checkDataset.datasetKey === void 0) {
1689
+ planDatasets.push({
1690
+ checkDataset,
1691
+ predicates: []
1692
+ });
1693
+ continue;
1694
+ }
1695
+ const planPredicates = [];
1696
+ for (const checkAction of checkActions) {
1697
+ const dataRefs = this.addDataRefs(checkAction.predicateSpec, checkScenario, checkDataset);
1698
+ const key = this.checkKey++;
1699
+ planPredicates.push({
1700
+ checkKey: key,
1701
+ action: checkAction,
1702
+ dataRefs
1703
+ });
1704
+ this.tasks.set(key, {
1705
+ scenario: checkScenario,
1706
+ dataset: checkDataset,
1707
+ action: checkAction,
1708
+ dataRefs
1709
+ });
1710
+ }
1711
+ planDatasets.push({
1712
+ checkDataset,
1713
+ predicates: planPredicates
1714
+ });
1715
+ }
1716
+ planScenarios.push({
1717
+ checkScenario,
1718
+ datasets: planDatasets
1719
+ });
1720
+ }
1721
+ planTests.push({
1722
+ name: testName,
1723
+ scenarios: planScenarios
1724
+ });
1725
+ }
1726
+ this.groups.push({
1727
+ name: groupName,
1728
+ tests: planTests
1729
+ });
1730
+ }
1731
+ }
1732
+ buildPlan() {
1733
+ return {
1734
+ groups: this.groups,
1735
+ tasks: this.tasks,
1736
+ dataRefs: this.dataRefs
1737
+ };
1738
+ }
1739
+ addDataRefs(predicateSpec, checkScenario, checkDataset) {
1740
+ let dataRefs;
1741
+ const addDataRef = (op) => {
1742
+ const predOp = predicateSpec[op];
1743
+ if (predOp === void 0 || typeof predOp === "number") {
1744
+ return;
1745
+ }
1746
+ let refDataset;
1747
+ if (typeof predOp.dataset === "string") {
1748
+ switch (predOp.dataset) {
1749
+ case "inherit":
1750
+ refDataset = checkDataset;
1751
+ break;
1752
+ default:
1753
+ (0, import_assert_never5.default)(predOp.dataset);
1754
+ }
1755
+ } else {
1756
+ const refDatasetSpec = { name: predOp.dataset.name };
1757
+ const matchedRefDatasets = expandDatasets(this.modelSpec, refDatasetSpec);
1758
+ if (matchedRefDatasets.length === 1) {
1759
+ refDataset = matchedRefDatasets[0];
1760
+ } else {
1761
+ refDataset = {
1762
+ name: predOp.dataset.name
1763
+ };
1764
+ }
1765
+ }
1766
+ let refScenario;
1767
+ if (typeof predOp.scenario === "string") {
1768
+ switch (predOp.scenario) {
1769
+ case "inherit":
1770
+ refScenario = checkScenario;
1771
+ break;
1772
+ default:
1773
+ (0, import_assert_never5.default)(predOp.scenario);
1774
+ }
1775
+ } else {
1776
+ const refScenarioSpecs = predOp.scenario ? [predOp.scenario] : [];
1777
+ const matchedRefScenarios = expandScenarios(this.modelSpec, refScenarioSpecs, true);
1778
+ if (matchedRefScenarios.length === 1) {
1779
+ refScenario = matchedRefScenarios[0];
1780
+ }
1781
+ if (refScenario === void 0) {
1782
+ refScenario = {
1783
+ inputDescs: []
1784
+ };
1785
+ }
1786
+ }
1787
+ let dataRefKey;
1788
+ if (refScenario.scenario && refDataset.datasetKey) {
1789
+ dataRefKey = `${refScenario.scenario.key}::${refDataset.datasetKey}`;
1790
+ }
1791
+ const dataRef = {
1792
+ key: dataRefKey,
1793
+ dataset: refDataset,
1794
+ scenario: refScenario
1795
+ };
1796
+ if (dataRefKey) {
1797
+ this.dataRefs.set(dataRefKey, dataRef);
1798
+ }
1799
+ if (dataRefs === void 0) {
1800
+ dataRefs = /* @__PURE__ */ new Map();
1801
+ }
1802
+ dataRefs.set(op, dataRef);
1803
+ };
1804
+ addDataRef("gt");
1805
+ addDataRef("gte");
1806
+ addDataRef("lt");
1807
+ addDataRef("lte");
1808
+ addDataRef("eq");
1809
+ addDataRef("approx");
1810
+ return dataRefs;
1811
+ }
1812
+ };
1813
+
1814
+ // src/check/check-summary.ts
1815
+ function checkSummaryFromReport(checkReport) {
1816
+ const predicateSummaries = [];
1817
+ for (const group of checkReport.groups) {
1818
+ for (const test of group.tests) {
1819
+ for (const scenario of test.scenarios) {
1820
+ for (const dataset of scenario.datasets) {
1821
+ for (const predicate of dataset.predicates) {
1822
+ switch (predicate.result.status) {
1823
+ case "passed":
1824
+ break;
1825
+ case "failed":
1826
+ case "error":
1827
+ predicateSummaries.push({
1828
+ checkKey: predicate.checkKey,
1829
+ result: predicate.result
1830
+ });
1831
+ break;
1832
+ default:
1833
+ (0, import_assert_never6.assertNever)(predicate.result.status);
1834
+ }
1835
+ }
1836
+ }
1837
+ }
1838
+ }
1839
+ }
1840
+ return {
1841
+ predicateSummaries
1842
+ };
1843
+ }
1844
+ function checkReportFromSummary(checkConfig, checkSummary, simplifyScenarios) {
1845
+ const checkSpecResult = parseTestYaml(checkConfig.tests);
1846
+ if (checkSpecResult.isErr()) {
1847
+ return void 0;
1848
+ }
1849
+ const checkSpec = checkSpecResult.value;
1850
+ const checkPlanner = new CheckPlanner(checkConfig.bundle.model.modelSpec);
1851
+ checkPlanner.addAllChecks(checkSpec, simplifyScenarios);
1852
+ const checkPlan = checkPlanner.buildPlan();
1853
+ const checkResults = /* @__PURE__ */ new Map();
1854
+ for (const predicateSummary of checkSummary.predicateSummaries) {
1855
+ checkResults.set(predicateSummary.checkKey, predicateSummary.result);
1856
+ }
1857
+ return buildCheckReport(checkPlan, checkResults);
1858
+ }
1859
+
1860
+ // src/compare/compare-data-coordinator.ts
1861
+ var import_assert_never7 = require("assert-never");
1862
+ var CompareDataCoordinator = class {
1863
+ constructor(bundleModelL, bundleModelR) {
1864
+ this.bundleModelL = bundleModelL;
1865
+ this.bundleModelR = bundleModelR;
1866
+ this.taskQueue = new TaskQueue({
1867
+ process: async (request) => {
1868
+ switch (request.kind) {
1869
+ case "dataset": {
1870
+ const [resultL, resultR] = await Promise.all([
1871
+ this.bundleModelL.getDatasetsForScenario(request.scenario, request.datasetKeys),
1872
+ this.bundleModelR.getDatasetsForScenario(request.scenario, request.datasetKeys)
1873
+ ]);
1874
+ return {
1875
+ kind: "dataset",
1876
+ datasetMapL: resultL.datasetMap,
1877
+ datasetMapR: resultR.datasetMap
1878
+ };
1879
+ }
1880
+ case "graph-data": {
1881
+ const bundleModel = request.bundle === "right" ? this.bundleModelR : this.bundleModelL;
1882
+ const graphData = await bundleModel.getGraphDataForScenario(request.scenario, request.graphId);
1883
+ return {
1884
+ kind: "graph-data",
1885
+ graphData
1886
+ };
1887
+ }
1888
+ default:
1889
+ (0, import_assert_never7.assertNever)(request);
1890
+ }
1891
+ }
1892
+ });
1893
+ }
1894
+ requestDatasetMaps(requestKey, scenario, datasetKeys, onResponse) {
1895
+ const request = {
1896
+ kind: "dataset",
1897
+ scenario,
1898
+ datasetKeys
1899
+ };
1900
+ this.taskQueue.addTask(requestKey, request, (response) => {
1901
+ if (response.kind === "dataset") {
1902
+ onResponse(response.datasetMapL, response.datasetMapR);
1903
+ }
1904
+ });
1905
+ }
1906
+ requestGraphData(requestKey, bundle, scenario, graphId, onResponse) {
1907
+ const request = {
1908
+ kind: "graph-data",
1909
+ bundle,
1910
+ scenario,
1911
+ graphId
1912
+ };
1913
+ this.taskQueue.addTask(requestKey, request, (response) => {
1914
+ if (response.kind === "graph-data") {
1915
+ onResponse(response.graphData);
1916
+ }
1917
+ });
1918
+ }
1919
+ cancelRequest(key) {
1920
+ this.taskQueue.cancelTask(key);
1921
+ }
1922
+ };
1923
+
1924
+ // src/compare/compare-datasets.ts
1925
+ function diffDatasets(datasetL, datasetR) {
1926
+ let minValueL = Number.MAX_VALUE;
1927
+ let maxValueL = Number.MIN_VALUE;
1928
+ let minValueR = Number.MAX_VALUE;
1929
+ let maxValueR = Number.MIN_VALUE;
1930
+ let minValue = Number.MAX_VALUE;
1931
+ let maxValue = Number.MIN_VALUE;
1932
+ let minRawDiff = Number.MAX_VALUE;
1933
+ let maxRawDiff = -1;
1934
+ let maxDiffPoint;
1935
+ let diffCount = 0;
1936
+ let totalRawDiff = 0;
1937
+ if (datasetL && datasetR) {
1938
+ const times = /* @__PURE__ */ new Set([...datasetL.keys(), ...datasetR.keys()]);
1939
+ for (const t of times) {
1940
+ const valueL = datasetL.get(t);
1941
+ if (valueL !== void 0) {
1942
+ if (valueL < minValueL)
1943
+ minValueL = valueL;
1944
+ if (valueL > maxValueL)
1945
+ maxValueL = valueL;
1946
+ if (valueL < minValue)
1947
+ minValue = valueL;
1948
+ if (valueL > maxValue)
1949
+ maxValue = valueL;
1950
+ }
1951
+ const valueR = datasetR.get(t);
1952
+ if (valueR !== void 0) {
1953
+ if (valueR < minValueR)
1954
+ minValueR = valueR;
1955
+ if (valueR > maxValueR)
1956
+ maxValueR = valueR;
1957
+ if (valueR < minValue)
1958
+ minValue = valueR;
1959
+ if (valueR > maxValue)
1960
+ maxValue = valueR;
1961
+ }
1962
+ if (valueL === void 0 || valueR === void 0) {
1963
+ continue;
1964
+ }
1965
+ const rawDiff = Math.abs(valueR - valueL);
1966
+ if (rawDiff < minRawDiff) {
1967
+ minRawDiff = rawDiff;
1968
+ }
1969
+ if (rawDiff > maxRawDiff) {
1970
+ maxRawDiff = rawDiff;
1971
+ maxDiffPoint = {
1972
+ time: t,
1973
+ valueL,
1974
+ valueR
1975
+ };
1976
+ }
1977
+ diffCount++;
1978
+ totalRawDiff += rawDiff;
1979
+ }
1980
+ }
1981
+ function pct(x) {
1982
+ return x * 100;
1983
+ }
1984
+ let minDiff;
1985
+ let maxDiff;
1986
+ let avgDiff;
1987
+ if (minValueL === maxValueL && minValueR === maxValueR) {
1988
+ const diff = pct(maxValueL !== 0 ? Math.abs((maxValueR - maxValueL) / maxValueL) : 1);
1989
+ minDiff = diff;
1990
+ maxDiff = diff;
1991
+ avgDiff = diff;
1992
+ } else {
1993
+ const spread = maxValue - minValue;
1994
+ minDiff = pct(spread > 0 ? minRawDiff / spread : 0);
1995
+ maxDiff = pct(spread > 0 ? maxRawDiff / spread : 0);
1996
+ const avgRawDiff = totalRawDiff / diffCount;
1997
+ avgDiff = pct(spread > 0 ? avgRawDiff / spread : 0);
1998
+ }
1999
+ let validity;
2000
+ if (datasetL && datasetR) {
2001
+ validity = "both";
2002
+ } else if (datasetL) {
2003
+ validity = "left-only";
2004
+ } else if (datasetR) {
2005
+ validity = "right-only";
2006
+ } else {
2007
+ validity = "neither";
2008
+ }
2009
+ return {
2010
+ validity,
2011
+ minValue,
2012
+ maxValue,
2013
+ avgDiff,
2014
+ minDiff,
2015
+ maxDiff,
2016
+ maxDiffPoint
2017
+ };
2018
+ }
2019
+ function compareDatasets(scenarioKey, datasetKey, datasetMapL, datasetMapR) {
2020
+ const datasetL = datasetMapL.get(datasetKey);
2021
+ const datasetR = datasetMapR.get(datasetKey);
2022
+ const diffReport = diffDatasets(datasetL, datasetR);
2023
+ return {
2024
+ scenarioKey,
2025
+ datasetKey,
2026
+ diffReport
2027
+ };
2028
+ }
2029
+
2030
+ // src/compare/compare-graphs.ts
2031
+ function diffGraphs(graphL, graphR, scenarioKey, datasetSummaries) {
2032
+ let inclusion;
2033
+ if (graphL && graphR) {
2034
+ inclusion = "both";
2035
+ } else if (graphL) {
2036
+ inclusion = "left-only";
2037
+ } else if (graphR) {
2038
+ inclusion = "right-only";
2039
+ } else {
2040
+ inclusion = "neither";
2041
+ }
2042
+ const metadataReports = [];
2043
+ if ((graphL == null ? void 0 : graphL.metadata) && (graphR == null ? void 0 : graphR.metadata)) {
2044
+ const metaKeys = /* @__PURE__ */ new Set();
2045
+ for (const key of graphL.metadata.keys()) {
2046
+ metaKeys.add(key);
2047
+ }
2048
+ for (const key of graphR.metadata.keys()) {
2049
+ metaKeys.add(key);
2050
+ }
2051
+ for (const key of metaKeys) {
2052
+ const valueL = graphL.metadata.get(key);
2053
+ const valueR = graphR.metadata.get(key);
2054
+ if (valueL !== valueR) {
2055
+ metadataReports.push({
2056
+ key,
2057
+ valueL,
2058
+ valueR
2059
+ });
2060
+ }
2061
+ }
2062
+ }
2063
+ const datasetReports = [];
2064
+ if (graphL && graphR) {
2065
+ const datasetKeys = /* @__PURE__ */ new Set();
2066
+ for (const dataset of graphL.datasets) {
2067
+ datasetKeys.add(dataset.datasetKey);
2068
+ }
2069
+ for (const dataset of graphR.datasets) {
2070
+ datasetKeys.add(dataset.datasetKey);
2071
+ }
2072
+ for (const datasetKey of datasetKeys) {
2073
+ const summary = datasetSummaries.find((summary2) => summary2.d === datasetKey && summary2.s === scenarioKey);
2074
+ const maxDiff = summary !== void 0 ? summary.md : void 0;
2075
+ datasetReports.push({
2076
+ datasetKey,
2077
+ maxDiff
2078
+ });
2079
+ }
2080
+ }
2081
+ return {
2082
+ inclusion,
2083
+ metadataReports,
2084
+ datasetReports
2085
+ };
2086
+ }
2087
+
2088
+ // src/compare/compare-summary.ts
2089
+ function compareSummaryFromReport(compareReport) {
2090
+ const datasetSummaries = [];
2091
+ for (const r of compareReport.datasetReports) {
2092
+ if (r.diffReport.validity === "both" && r.diffReport.maxDiff > 0) {
2093
+ datasetSummaries.push({
2094
+ s: r.scenarioKey,
2095
+ d: r.datasetKey,
2096
+ md: r.diffReport.maxDiff
2097
+ });
2098
+ }
2099
+ }
2100
+ return {
2101
+ datasetSummaries,
2102
+ perfReportL: compareReport.perfReportL,
2103
+ perfReportR: compareReport.perfReportR
2104
+ };
2105
+ }
2106
+
2107
+ // src/config/synchronized-model.ts
2108
+ function synchronizedBundleModel(sourceModel) {
2109
+ var _a;
2110
+ const promiseQueue = new PromiseQueue();
2111
+ return {
2112
+ modelSpec: sourceModel.modelSpec,
2113
+ getDatasetsForScenario: (scenario, datasetKeys) => {
2114
+ return promiseQueue.add(() => sourceModel.getDatasetsForScenario(scenario, datasetKeys));
2115
+ },
2116
+ getGraphsForDataset: (_a = sourceModel.getGraphsForDataset) == null ? void 0 : _a.bind(sourceModel),
2117
+ getGraphDataForScenario: (scenario, graphId) => {
2118
+ return promiseQueue.add(() => sourceModel.getGraphDataForScenario(scenario, graphId));
2119
+ },
2120
+ getGraphLinksForScenario: sourceModel.getGraphLinksForScenario.bind(sourceModel)
2121
+ };
2122
+ }
2123
+ var PromiseQueue = class {
2124
+ constructor() {
2125
+ this.tasks = [];
2126
+ this.runningCount = 0;
2127
+ }
2128
+ add(f) {
2129
+ return new Promise((resolve, reject) => {
2130
+ const run = async () => {
2131
+ this.runningCount++;
2132
+ const promise = f();
2133
+ try {
2134
+ const result = await promise;
2135
+ resolve(result);
2136
+ } catch (e) {
2137
+ reject(e);
2138
+ } finally {
2139
+ this.runningCount--;
2140
+ this.runNext();
2141
+ }
2142
+ };
2143
+ if (this.runningCount < 1) {
2144
+ run();
2145
+ } else {
2146
+ this.tasks.push(run);
2147
+ }
2148
+ });
2149
+ }
2150
+ runNext() {
2151
+ if (this.tasks.length > 0) {
2152
+ const task = this.tasks.shift();
2153
+ if (task) {
2154
+ task();
2155
+ }
2156
+ }
2157
+ }
2158
+ };
2159
+
2160
+ // src/config/config.ts
2161
+ async function createConfig(options) {
2162
+ var _a;
2163
+ const origCurrentBundle = await loadSynchronized(options.current);
2164
+ let currentBundle;
2165
+ let compareConfig;
2166
+ if (options.compare === void 0) {
2167
+ currentBundle = origCurrentBundle;
2168
+ } else {
2169
+ const baselineBundle = await loadSynchronized(options.compare.baseline);
2170
+ const renamedDatasetKeys = options.compare.datasets.renamedDatasetKeys;
2171
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
2172
+ renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
2173
+ invertedRenamedKeys.set(newKey, oldKey);
2174
+ });
2175
+ const rightKeyForLeftKey = (leftKey) => {
2176
+ return (renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.get(leftKey)) || leftKey;
2177
+ };
2178
+ const leftKeyForRightKey = (rightKey) => {
2179
+ return invertedRenamedKeys.get(rightKey) || rightKey;
2180
+ };
2181
+ const origBundleModelR = origCurrentBundle.model;
2182
+ const adjBundleModelR = {
2183
+ modelSpec: origBundleModelR.modelSpec,
2184
+ getDatasetsForScenario: async (scenario, datasetKeys) => {
2185
+ const rightKeys = datasetKeys.map(rightKeyForLeftKey);
2186
+ const result = await origBundleModelR.getDatasetsForScenario(scenario, rightKeys);
2187
+ const mapWithRightKeys = result.datasetMap;
2188
+ const mapWithLeftKeys = /* @__PURE__ */ new Map();
2189
+ for (const [rightKey, dataset] of mapWithRightKeys.entries()) {
2190
+ const leftKey = leftKeyForRightKey(rightKey);
2191
+ mapWithLeftKeys.set(leftKey, dataset);
2192
+ }
2193
+ return {
2194
+ datasetMap: mapWithLeftKeys,
2195
+ modelRunTime: result.modelRunTime
2196
+ };
2197
+ },
2198
+ getGraphsForDataset: (_a = origBundleModelR.getGraphsForDataset) == null ? void 0 : _a.bind(origBundleModelR),
2199
+ getGraphDataForScenario: origBundleModelR.getGraphDataForScenario.bind(origBundleModelR),
2200
+ getGraphLinksForScenario: origBundleModelR.getGraphLinksForScenario.bind(origBundleModelR)
2201
+ };
2202
+ currentBundle = __spreadProps(__spreadValues({}, origCurrentBundle), {
2203
+ model: adjBundleModelR
2204
+ });
2205
+ compareConfig = {
2206
+ bundleL: baselineBundle,
2207
+ bundleR: currentBundle,
2208
+ thresholds: options.compare.thresholds,
2209
+ scenarios: options.compare.scenarios,
2210
+ datasets: options.compare.datasets
2211
+ };
2212
+ }
2213
+ const checkConfig = {
2214
+ bundle: currentBundle,
2215
+ tests: options.check.tests
2216
+ };
2217
+ return {
2218
+ check: checkConfig,
2219
+ compare: compareConfig
2220
+ };
2221
+ }
2222
+ async function loadSynchronized(sourceBundle) {
2223
+ const sourceModel = await sourceBundle.bundle.initModel();
2224
+ const synchronizedModel = synchronizedBundleModel(sourceModel);
2225
+ return {
2226
+ name: sourceBundle.name,
2227
+ version: sourceBundle.bundle.version,
2228
+ model: synchronizedModel
2229
+ };
2230
+ }
2231
+
2232
+ // src/config/dataset-manager.ts
2233
+ var DatasetManager = class {
2234
+ constructor(bundleL, bundleR, renamedDatasetKeys) {
2235
+ this.bundleL = bundleL;
2236
+ this.bundleR = bundleR;
2237
+ this.renamedDatasetKeys = renamedDatasetKeys;
2238
+ const invertedRenamedKeys = /* @__PURE__ */ new Map();
2239
+ renamedDatasetKeys == null ? void 0 : renamedDatasetKeys.forEach((newKey, oldKey) => {
2240
+ invertedRenamedKeys.set(newKey, oldKey);
2241
+ });
2242
+ function leftKeyForRightKey(rightKey) {
2243
+ return invertedRenamedKeys.get(rightKey) || rightKey;
2244
+ }
2245
+ const allOutputVarKeysSet = /* @__PURE__ */ new Set();
2246
+ const modelOutputVarKeysSet = /* @__PURE__ */ new Set();
2247
+ function addOutputVars(outputVars, handleRenames) {
2248
+ outputVars.forEach((outputVar, key) => {
2249
+ const remappedKey = handleRenames ? leftKeyForRightKey(key) : key;
2250
+ allOutputVarKeysSet.add(remappedKey);
2251
+ if (outputVar.sourceName === void 0) {
2252
+ modelOutputVarKeysSet.add(remappedKey);
2253
+ }
2254
+ });
2255
+ }
2256
+ addOutputVars(bundleL.modelSpec.outputVars, false);
2257
+ addOutputVars(bundleR.modelSpec.outputVars, true);
2258
+ this.allOutputVarKeys = Array.from(allOutputVarKeysSet);
2259
+ this.modelOutputVarKeys = Array.from(modelOutputVarKeysSet);
2260
+ }
2261
+ getDatasetKeysForScenario(scenario) {
2262
+ if (scenario.kind === "all-inputs" && scenario.position === "at-default") {
2263
+ return this.allOutputVarKeys;
2264
+ } else {
2265
+ return this.modelOutputVarKeys;
2266
+ }
2267
+ }
2268
+ getDatasetInfo(datasetKey) {
2269
+ var _a;
2270
+ const modelSpecL = this.bundleL.modelSpec;
2271
+ const modelSpecR = this.bundleR.modelSpec;
2272
+ const datasetKeyL = datasetKey;
2273
+ const datasetKeyR = ((_a = this.renamedDatasetKeys) == null ? void 0 : _a.get(datasetKeyL)) || datasetKeyL;
2274
+ const outputVarL = modelSpecL.outputVars.get(datasetKeyL);
2275
+ const outputVarR = modelSpecR.outputVars.get(datasetKeyR);
2276
+ let varName;
2277
+ let newVarName;
2278
+ let sourceName;
2279
+ let newSourceName;
2280
+ if (outputVarL && outputVarR && outputVarL.varName != outputVarR.varName) {
2281
+ varName = outputVarL.varName;
2282
+ newVarName = outputVarR.varName;
2283
+ } else {
2284
+ const outputVar = outputVarR || outputVarL;
2285
+ varName = (outputVar == null ? void 0 : outputVar.varName) || "Unknown";
2286
+ }
2287
+ if (outputVarL && outputVarR && outputVarL.sourceName != outputVarR.sourceName) {
2288
+ sourceName = outputVarL.sourceName;
2289
+ newSourceName = outputVarR.sourceName;
2290
+ } else {
2291
+ const outputVar = outputVarR || outputVarL;
2292
+ sourceName = outputVar == null ? void 0 : outputVar.sourceName;
2293
+ }
2294
+ return {
2295
+ varName,
2296
+ newVarName,
2297
+ sourceName,
2298
+ newSourceName,
2299
+ relatedItems: (outputVarR == null ? void 0 : outputVarR.relatedItems) || []
2300
+ };
2301
+ }
2302
+ };
2303
+
2304
+ // src/config/scenario-manager.ts
2305
+ var import_assert_never8 = require("assert-never");
2306
+ var ScenarioManager = class {
2307
+ constructor(bundleL, bundleR) {
2308
+ this.bundleL = bundleL;
2309
+ this.bundleR = bundleR;
2310
+ this.scenarios = /* @__PURE__ */ new Map();
2311
+ this.scenarioInfo = /* @__PURE__ */ new Map();
2312
+ this.defaultInfoForGroup = /* @__PURE__ */ new Map();
2313
+ this.groupInfo = /* @__PURE__ */ new Map();
2314
+ }
2315
+ getScenarios() {
2316
+ return [...this.scenarios.values()];
2317
+ }
2318
+ getScenario(scenarioKey) {
2319
+ return this.scenarios.get(scenarioKey);
2320
+ }
2321
+ getScenarioGroupInfo(groupKey) {
2322
+ return this.groupInfo.get(groupKey);
2323
+ }
2324
+ getScenarioInfo(scenario, groupKey) {
2325
+ if (scenario.key === "all_inputs_at_default") {
2326
+ const defaultInfo = this.defaultInfoForGroup.get(groupKey);
2327
+ if (defaultInfo) {
2328
+ return defaultInfo;
2329
+ }
2330
+ }
2331
+ return this.scenarioInfo.get(scenario.key);
2332
+ }
2333
+ setDefaultScenarioInfoForGroup(groupKey, scenarioInfo) {
2334
+ this.defaultInfoForGroup.set(groupKey, scenarioInfo);
2335
+ }
2336
+ addScenario(scenario, scenarioInfo, groupInfo) {
2337
+ this.scenarios.set(scenario.key, scenario);
2338
+ if (!scenarioInfo) {
2339
+ scenarioInfo = this.getInfoForScenario(scenario);
2340
+ }
2341
+ this.scenarioInfo.set(scenario.key, scenarioInfo);
2342
+ if (!groupInfo) {
2343
+ groupInfo = this.getGroupInfoForScenario(scenario);
2344
+ }
2345
+ this.groupInfo.set(scenario.groupKey, groupInfo);
2346
+ }
2347
+ addScenarioMatrix() {
2348
+ const inputVarIdsL = Array.from(this.bundleL.modelSpec.inputVars.keys());
2349
+ const inputVarIdsR = Array.from(this.bundleR.modelSpec.inputVars.keys());
2350
+ const inputVarIds = /* @__PURE__ */ new Set([...inputVarIdsL, ...inputVarIdsR]);
2351
+ this.addScenario(allInputsAtPositionScenario("at-default"));
2352
+ this.addScenario(allInputsAtPositionScenario("at-minimum"));
2353
+ this.addScenario(allInputsAtPositionScenario("at-maximum"));
2354
+ for (const inputVarId of inputVarIds) {
2355
+ this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, "at-minimum"));
2356
+ this.addScenario(inputAtPositionScenario(inputVarId, inputVarId, "at-maximum"));
2357
+ }
2358
+ }
2359
+ getGroupInfoForScenario(scenario) {
2360
+ switch (scenario.kind) {
2361
+ case "all-inputs":
2362
+ return {
2363
+ title: "All Inputs",
2364
+ relatedItems: []
2365
+ };
2366
+ case "settings":
2367
+ if (scenario.settings.length === 1) {
2368
+ const inputVar = this.getInputVarForSetting(scenario.settings[0]);
2369
+ let relatedItems;
2370
+ let subtitle;
2371
+ if (inputVar == null ? void 0 : inputVar.relatedItem) {
2372
+ relatedItems = [inputVar.relatedItem];
2373
+ subtitle = inputVar.relatedItem.locationPath.join('&nbsp;<span class="related-sep">&gt;</span>&nbsp;');
2374
+ } else {
2375
+ relatedItems = [];
2376
+ subtitle = void 0;
2377
+ }
2378
+ return {
2379
+ title: (inputVar == null ? void 0 : inputVar.varName) || "Unknown Input",
2380
+ subtitle,
2381
+ relatedItems
2382
+ };
2383
+ } else {
2384
+ return {
2385
+ title: "Multiple Inputs",
2386
+ relatedItems: this.getRelatedItemsForSettings(scenario.settings)
2387
+ };
2388
+ }
2389
+ default:
2390
+ (0, import_assert_never8.assertNever)(scenario);
2391
+ }
2392
+ }
2393
+ getInfoForScenario(scenario) {
2394
+ switch (scenario.kind) {
2395
+ case "all-inputs":
2396
+ return this.getInfoForPositionSetting(void 0, scenario.position);
2397
+ case "settings":
2398
+ if (scenario.settings.length === 1 && scenario.settings[0].kind === "position") {
2399
+ const setting = scenario.settings[0];
2400
+ return this.getInfoForPositionSetting(setting.inputVarId, setting.position);
2401
+ } else {
2402
+ return {
2403
+ title: `PLACEHOLDER (scenario info not provided)`,
2404
+ position: 1
2405
+ };
2406
+ }
2407
+ default:
2408
+ (0, import_assert_never8.assertNever)(scenario);
2409
+ }
2410
+ }
2411
+ getInfoForPositionSetting(inputVarId, inputPosition2) {
2412
+ const title = inputPosition2.replace("-", " ");
2413
+ let subtitle;
2414
+ if (inputVarId) {
2415
+ const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId);
2416
+ const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId);
2417
+ const valueL = inputValue(inputVarL, inputPosition2);
2418
+ const valueR = inputValue(inputVarR, inputPosition2);
2419
+ if (valueL !== valueR) {
2420
+ let values = "";
2421
+ values += "(";
2422
+ values += `<span class='dataset-color-0'>${valueL}</span>`;
2423
+ values += "&nbsp;|&nbsp;";
2424
+ values += `<span class='dataset-color-1'>${valueR}</span>`;
2425
+ values += ")";
2426
+ subtitle = values;
2427
+ } else {
2428
+ subtitle = `(${valueL})`;
2429
+ }
2430
+ } else {
2431
+ subtitle = void 0;
2432
+ }
2433
+ let position;
2434
+ switch (inputPosition2) {
2435
+ case "at-default":
2436
+ position = 0;
2437
+ break;
2438
+ case "at-minimum":
2439
+ position = 1;
2440
+ break;
2441
+ case "at-maximum":
2442
+ position = 2;
2443
+ break;
2444
+ default:
2445
+ (0, import_assert_never8.assertNever)(inputPosition2);
2446
+ }
2447
+ return {
2448
+ title,
2449
+ subtitle,
2450
+ position
2451
+ };
2452
+ }
2453
+ getRelatedItemsForSettings(settings) {
2454
+ const relatedItems = [];
2455
+ for (const setting of settings) {
2456
+ const inputVar = this.getInputVarForSetting(setting);
2457
+ if (inputVar == null ? void 0 : inputVar.relatedItem) {
2458
+ relatedItems.push(inputVar.relatedItem);
2459
+ }
2460
+ }
2461
+ return relatedItems;
2462
+ }
2463
+ getInputVarForSetting(setting) {
2464
+ const inputVarId = setting.inputVarId;
2465
+ const inputVarL = this.bundleL.modelSpec.inputVars.get(inputVarId);
2466
+ const inputVarR = this.bundleR.modelSpec.inputVars.get(inputVarId);
2467
+ return inputVarR || inputVarL;
2468
+ }
2469
+ };
2470
+ function inputValue(inputVar, position) {
2471
+ if (inputVar) {
2472
+ switch (position) {
2473
+ case "at-default":
2474
+ return inputVar.defaultValue.toString();
2475
+ case "at-minimum":
2476
+ return inputVar.minValue.toString();
2477
+ case "at-maximum":
2478
+ return inputVar.maxValue.toString();
2479
+ default:
2480
+ (0, import_assert_never8.assertNever)(position);
2481
+ }
2482
+ } else {
2483
+ return "n/a";
2484
+ }
2485
+ }
2486
+
2487
+ // src/perf/perf-runner.ts
2488
+ var import_assert_never9 = require("assert-never");
2489
+
2490
+ // src/perf/perf-stats.ts
2491
+ var PerfStats = class {
2492
+ constructor() {
2493
+ this.times = [];
2494
+ }
2495
+ addRun(timeInMillis) {
2496
+ this.times.push(timeInMillis);
2497
+ }
2498
+ toReport() {
2499
+ if (this.times.length === 0) {
2500
+ return {
2501
+ minTime: 0,
2502
+ maxTime: 0,
2503
+ avgTime: 0,
2504
+ allTimes: []
2505
+ };
2506
+ }
2507
+ const minTime = Math.min(...this.times);
2508
+ const maxTime = Math.max(...this.times);
2509
+ const sortedTimes = this.times.sort();
2510
+ const minIndex = Math.floor(sortedTimes.length / 4);
2511
+ const maxIndex = minIndex + Math.ceil(sortedTimes.length / 2);
2512
+ const middleTimes = sortedTimes.slice(minIndex, maxIndex);
2513
+ const totalTime = middleTimes.reduce((a, b) => a + b, 0);
2514
+ const avgTime = totalTime / middleTimes.length;
2515
+ return {
2516
+ minTime,
2517
+ maxTime,
2518
+ avgTime,
2519
+ allTimes: sortedTimes
2520
+ };
2521
+ }
2522
+ };
2523
+
2524
+ // src/perf/perf-runner.ts
2525
+ var warmupCount = 5;
2526
+ var runCount = 100;
2527
+ var PerfRunner = class {
2528
+ constructor(bundleModelL, bundleModelR, mode = "serial") {
2529
+ this.bundleModelL = bundleModelL;
2530
+ this.bundleModelR = bundleModelR;
2531
+ this.mode = mode;
2532
+ const scenario = allInputsAtPositionScenario("at-default");
2533
+ this.taskQueue = new TaskQueue({
2534
+ process: async (request) => {
2535
+ switch (request.kind) {
2536
+ case "left": {
2537
+ const result = await bundleModelL.getDatasetsForScenario(scenario, []);
2538
+ return {
2539
+ runTimeL: result.modelRunTime
2540
+ };
2541
+ }
2542
+ case "right": {
2543
+ const result = await bundleModelR.getDatasetsForScenario(scenario, []);
2544
+ return {
2545
+ runTimeR: result.modelRunTime
2546
+ };
2547
+ }
2548
+ case "both": {
2549
+ const [resultL, resultR] = await Promise.all([
2550
+ bundleModelL.getDatasetsForScenario(scenario, []),
2551
+ bundleModelR.getDatasetsForScenario(scenario, [])
2552
+ ]);
2553
+ return {
2554
+ runTimeL: resultL.modelRunTime,
2555
+ runTimeR: resultR.modelRunTime
2556
+ };
2557
+ }
2558
+ default:
2559
+ (0, import_assert_never9.assertNever)(request.kind);
2560
+ }
2561
+ }
2562
+ });
2563
+ }
2564
+ start() {
2565
+ const statsL = new PerfStats();
2566
+ const statsR = new PerfStats();
2567
+ this.taskQueue.onIdle = (error) => {
2568
+ var _a;
2569
+ if (error) {
2570
+ this.onError(error);
2571
+ } else {
2572
+ (_a = this.onComplete) == null ? void 0 : _a.call(this, statsL.toReport(), statsR.toReport());
2573
+ }
2574
+ };
2575
+ const taskQueue = this.taskQueue;
2576
+ function addTask(index, warmup, kind) {
2577
+ const key = `${warmup ? "warmup-" : ""}${kind}-${index}`;
2578
+ const request = {
2579
+ kind
2580
+ };
2581
+ taskQueue.addTask(key, request, (response) => {
2582
+ if (!warmup && response.runTimeL !== void 0) {
2583
+ statsL.addRun(response.runTimeL);
2584
+ }
2585
+ if (!warmup && response.runTimeR !== void 0) {
2586
+ statsR.addRun(response.runTimeR);
2587
+ }
2588
+ });
2589
+ }
2590
+ function addTasks(kind) {
2591
+ for (let i = 0; i < warmupCount; i++) {
2592
+ addTask(i, true, kind);
2593
+ }
2594
+ for (let i = 0; i < runCount; i++) {
2595
+ addTask(i, false, kind);
2596
+ }
2597
+ }
2598
+ if (this.mode === "parallel") {
2599
+ addTasks("both");
2600
+ } else {
2601
+ addTasks("left");
2602
+ addTasks("right");
2603
+ }
2604
+ }
2605
+ };
2606
+
2607
+ // src/suite/suite-runner.ts
2608
+ var import_assert_never10 = __toESM(require("assert-never"), 1);
2609
+
2610
+ // src/data/data-planner.ts
2611
+ var ScenarioTaskSet = class {
2612
+ constructor(scenario) {
2613
+ this.scenario = scenario;
2614
+ this.modelTasks = /* @__PURE__ */ new Map();
2615
+ this.modelImplTasks = /* @__PURE__ */ new Map();
2616
+ this.requestKind = "check";
2617
+ }
2618
+ addTask(kind, datasetKey, dataFunc) {
2619
+ if (kind === "compare") {
2620
+ this.requestKind = "compare";
2621
+ }
2622
+ const dataTask = {
2623
+ datasetKey,
2624
+ dataFunc
2625
+ };
2626
+ let taskMap;
2627
+ if (datasetKey.startsWith("ModelImpl")) {
2628
+ taskMap = this.modelImplTasks;
2629
+ } else {
2630
+ taskMap = this.modelTasks;
2631
+ }
2632
+ let tasks = taskMap.get(datasetKey);
2633
+ if (!tasks) {
2634
+ tasks = [];
2635
+ taskMap.set(datasetKey, tasks);
2636
+ }
2637
+ tasks.push(dataTask);
2638
+ }
2639
+ buildRequests(batchSize) {
2640
+ const dataRequests = [];
2641
+ if (this.modelTasks.size > 0) {
2642
+ const dataTasks = [];
2643
+ this.modelTasks.forEach((tasks) => dataTasks.push(...tasks));
2644
+ dataRequests.push({
2645
+ kind: this.requestKind,
2646
+ scenario: this.scenario,
2647
+ dataTasks
2648
+ });
2649
+ }
2650
+ if (this.modelImplTasks.size > 0) {
2651
+ const allKeys = [...this.modelImplTasks.keys()];
2652
+ for (let i = 0; i < allKeys.length; i += batchSize) {
2653
+ const batchKeys = allKeys.slice(i, i + batchSize);
2654
+ const dataTasks = [];
2655
+ for (const datasetKey of batchKeys) {
2656
+ dataTasks.push(...this.modelImplTasks.get(datasetKey));
2657
+ }
2658
+ dataRequests.push({
2659
+ kind: this.requestKind,
2660
+ scenario: this.scenario,
2661
+ dataTasks
2662
+ });
2663
+ }
2664
+ }
2665
+ return dataRequests;
2666
+ }
2667
+ };
2668
+ var DataPlanner = class {
2669
+ constructor(batchSize) {
2670
+ this.batchSize = batchSize;
2671
+ this.scenarioTaskSets = /* @__PURE__ */ new Map();
2672
+ }
2673
+ addRequest(kind, scenario, datasetKey, dataFunc) {
2674
+ let scenarioTaskSet = this.scenarioTaskSets.get(scenario.key);
2675
+ if (!scenarioTaskSet) {
2676
+ scenarioTaskSet = new ScenarioTaskSet(scenario);
2677
+ this.scenarioTaskSets.set(scenario.key, scenarioTaskSet);
2678
+ }
2679
+ scenarioTaskSet.addTask(kind, datasetKey, dataFunc);
2680
+ }
2681
+ buildPlan() {
2682
+ const requests = [];
2683
+ for (const taskSet of this.scenarioTaskSets.values()) {
2684
+ requests.push(...taskSet.buildRequests(this.batchSize));
2685
+ }
2686
+ return {
2687
+ requests
2688
+ };
2689
+ }
2690
+ };
2691
+
2692
+ // src/check/check-runner.ts
2693
+ function runChecks(checkConfig, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios) {
2694
+ const modelSpec = checkConfig.bundle.model.modelSpec;
2695
+ const checkPlanner = new CheckPlanner(modelSpec);
2696
+ checkPlanner.addAllChecks(checkSpec, simplifyScenarios);
2697
+ const checkPlan = checkPlanner.buildPlan();
2698
+ const refDatasets = /* @__PURE__ */ new Map();
2699
+ for (const [dataRefKey, dataRef] of checkPlan.dataRefs.entries()) {
2700
+ refDataPlanner.addRequest("check", dataRef.scenario.scenario, dataRef.dataset.datasetKey, (datasets) => {
2701
+ const dataset = datasets.datasetR;
2702
+ if (dataset) {
2703
+ refDatasets.set(dataRefKey, dataset);
2704
+ }
2705
+ });
2706
+ }
2707
+ const checkResults = /* @__PURE__ */ new Map();
2708
+ for (const [checkKey, checkTask] of checkPlan.tasks.entries()) {
2709
+ dataPlanner.addRequest("check", checkTask.scenario.scenario, checkTask.dataset.datasetKey, (datasets) => {
2710
+ const dataset = datasets.datasetR;
2711
+ const checkResult = runCheck(checkTask, dataset, refDatasets);
2712
+ checkResults.set(checkKey, checkResult);
2713
+ });
2714
+ }
2715
+ return () => {
2716
+ return buildCheckReport(checkPlan, checkResults);
2717
+ };
2718
+ }
2719
+ function runCheck(checkTask, dataset, refDatasets) {
2720
+ if (dataset === void 0) {
2721
+ return {
2722
+ status: "error",
2723
+ message: "no data available"
2724
+ };
2725
+ }
2726
+ let opRefDatasets;
2727
+ if (checkTask.dataRefs) {
2728
+ opRefDatasets = /* @__PURE__ */ new Map();
2729
+ for (const [op, dataRef] of checkTask.dataRefs.entries()) {
2730
+ const refDataset = refDatasets == null ? void 0 : refDatasets.get(dataRef.key);
2731
+ if (refDataset === void 0) {
2732
+ if (dataRef.dataset.datasetKey === void 0) {
2733
+ return {
2734
+ status: "error",
2735
+ errorInfo: {
2736
+ kind: "unknown-dataset",
2737
+ name: dataRef.dataset.name
2738
+ }
2739
+ };
2740
+ } else if (dataRef.scenario.scenario === void 0) {
2741
+ if (dataRef.scenario.error) {
2742
+ return {
2743
+ status: "error",
2744
+ errorInfo: {
2745
+ kind: dataRef.scenario.error.kind,
2746
+ name: dataRef.scenario.error.name
2747
+ }
2748
+ };
2749
+ } else {
2750
+ let inputName;
2751
+ if (dataRef.scenario.inputDescs.length > 0) {
2752
+ inputName = dataRef.scenario.inputDescs[0].name;
2753
+ } else {
2754
+ inputName = "unknown";
2755
+ }
2756
+ return {
2757
+ status: "error",
2758
+ errorInfo: {
2759
+ kind: "unknown-input",
2760
+ name: inputName
2761
+ }
2762
+ };
2763
+ }
2764
+ } else {
2765
+ return {
2766
+ status: "error",
2767
+ message: "unresolved data reference"
2768
+ };
2769
+ }
2770
+ }
2771
+ opRefDatasets.set(op, refDataset);
2772
+ }
2773
+ }
2774
+ return checkTask.action.run(dataset, opRefDatasets);
2775
+ }
2776
+
2777
+ // src/compare/compare-runner.ts
2778
+ function runCompare(compareConfig, dataPlanner, simplifyScenarios) {
2779
+ const scenarios = simplifyScenarios ? [allInputsAtPositionScenario("at-default")] : compareConfig.scenarios.getScenarios();
2780
+ const datasetReports = [];
2781
+ for (const scenario of scenarios) {
2782
+ const datasetKeys = compareConfig.datasets.getDatasetKeysForScenario(scenario);
2783
+ for (const datasetKey of datasetKeys) {
2784
+ dataPlanner.addRequest("compare", scenario, datasetKey, (datasets) => {
2785
+ const diffReport = diffDatasets(datasets.datasetL, datasets.datasetR);
2786
+ datasetReports.push({
2787
+ scenarioKey: scenario.key,
2788
+ datasetKey,
2789
+ diffReport
2790
+ });
2791
+ });
2792
+ }
2793
+ }
2794
+ return () => {
2795
+ return datasetReports;
2796
+ };
2797
+ }
2798
+
2799
+ // src/suite/suite-runner.ts
2800
+ var SuiteRunner = class {
2801
+ constructor(config, callbacks) {
2802
+ this.config = config;
2803
+ this.callbacks = callbacks;
2804
+ this.perfStatsL = new PerfStats();
2805
+ this.perfStatsR = new PerfStats();
2806
+ this.stopped = false;
2807
+ this.taskQueue = new TaskQueue({
2808
+ process: (request) => {
2809
+ return this.processRequest(request);
2810
+ }
2811
+ });
2812
+ }
2813
+ cancel() {
2814
+ if (!this.stopped) {
2815
+ this.stopped = true;
2816
+ this.taskQueue.shutdown();
2817
+ }
2818
+ }
2819
+ start(options) {
2820
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2821
+ (_b = (_a = this.callbacks).onProgress) == null ? void 0 : _b.call(_a, 0);
2822
+ const modelSpec = this.config.check.bundle.model.modelSpec;
2823
+ const dataPlanner = new DataPlanner(modelSpec.outputVars.size);
2824
+ const refDataPlanner = new DataPlanner(modelSpec.outputVars.size);
2825
+ const checkSpecResult = parseTestYaml(this.config.check.tests);
2826
+ if (checkSpecResult.isErr()) {
2827
+ (_d = (_c = this.callbacks).onError) == null ? void 0 : _d.call(_c, checkSpecResult.error);
2828
+ return;
2829
+ }
2830
+ const checkSpec = checkSpecResult.value;
2831
+ const simplifyScenarios = (options == null ? void 0 : options.simplifyScenarios) === true;
2832
+ const buildCheckReport2 = runChecks(this.config.check, checkSpec, dataPlanner, refDataPlanner, simplifyScenarios);
2833
+ let buildCompareDatasetReports;
2834
+ if (this.config.compare) {
2835
+ buildCompareDatasetReports = runCompare(this.config.compare, dataPlanner, simplifyScenarios);
2836
+ }
2837
+ this.taskQueue.onIdle = (error) => {
2838
+ var _a2, _b2, _c2, _d2;
2839
+ if (this.stopped) {
2840
+ return;
2841
+ }
2842
+ if (error) {
2843
+ (_b2 = (_a2 = this.callbacks).onError) == null ? void 0 : _b2.call(_a2, error);
2844
+ } else {
2845
+ const checkReport = buildCheckReport2();
2846
+ let compareReport;
2847
+ if (this.config.compare) {
2848
+ compareReport = {
2849
+ datasetReports: buildCompareDatasetReports(),
2850
+ perfReportL: this.perfStatsL.toReport(),
2851
+ perfReportR: this.perfStatsR.toReport()
2852
+ };
2853
+ }
2854
+ (_d2 = (_c2 = this.callbacks).onComplete) == null ? void 0 : _d2.call(_c2, {
2855
+ checkReport,
2856
+ compareReport
2857
+ });
2858
+ }
2859
+ };
2860
+ const refDataPlan = refDataPlanner.buildPlan();
2861
+ const dataPlan = dataPlanner.buildPlan();
2862
+ const dataRequests = [...refDataPlan.requests, ...dataPlan.requests];
2863
+ const taskCount = dataRequests.length;
2864
+ if (taskCount === 0) {
2865
+ let compareReport;
2866
+ if (this.config.compare) {
2867
+ compareReport = {
2868
+ datasetReports: [],
2869
+ perfReportL: this.perfStatsL.toReport(),
2870
+ perfReportR: this.perfStatsR.toReport()
2871
+ };
2872
+ }
2873
+ this.cancel();
2874
+ (_f = (_e = this.callbacks).onProgress) == null ? void 0 : _f.call(_e, 1);
2875
+ (_h = (_g = this.callbacks).onComplete) == null ? void 0 : _h.call(_g, {
2876
+ checkReport: {
2877
+ groups: []
2878
+ },
2879
+ compareReport
2880
+ });
2881
+ return;
2882
+ }
2883
+ let tasksCompleted = 0;
2884
+ let dataTaskId = 1;
2885
+ for (const dataRequest of dataRequests) {
2886
+ this.taskQueue.addTask(`data${dataTaskId++}`, dataRequest, () => {
2887
+ var _a2, _b2;
2888
+ tasksCompleted++;
2889
+ (_b2 = (_a2 = this.callbacks).onProgress) == null ? void 0 : _b2.call(_a2, tasksCompleted / taskCount);
2890
+ });
2891
+ }
2892
+ }
2893
+ async processRequest(request) {
2894
+ const datasetKeySet = /* @__PURE__ */ new Set();
2895
+ for (const dataTask of request.dataTasks) {
2896
+ datasetKeySet.add(dataTask.datasetKey);
2897
+ }
2898
+ const datasetKeys = [...datasetKeySet];
2899
+ const scenario = request.scenario;
2900
+ let datasetsResultL;
2901
+ let datasetsResultR;
2902
+ switch (request.kind) {
2903
+ case "check": {
2904
+ const bundleModel = this.config.check.bundle.model;
2905
+ datasetsResultR = await bundleModel.getDatasetsForScenario(scenario, datasetKeys);
2906
+ break;
2907
+ }
2908
+ case "compare": {
2909
+ const bundleModelL = this.config.compare.bundleL.model;
2910
+ const bundleModelR = this.config.compare.bundleR.model;
2911
+ const [resultL, resultR] = await Promise.all([
2912
+ bundleModelL.getDatasetsForScenario(scenario, datasetKeys),
2913
+ bundleModelR.getDatasetsForScenario(scenario, datasetKeys)
2914
+ ]);
2915
+ datasetsResultL = resultL;
2916
+ datasetsResultR = resultR;
2917
+ break;
2918
+ }
2919
+ default:
2920
+ (0, import_assert_never10.default)(request.kind);
2921
+ }
2922
+ if (datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime) {
2923
+ this.perfStatsL.addRun(datasetsResultL == null ? void 0 : datasetsResultL.modelRunTime);
2924
+ }
2925
+ if (datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime) {
2926
+ this.perfStatsR.addRun(datasetsResultR == null ? void 0 : datasetsResultR.modelRunTime);
2927
+ }
2928
+ const datasetMapL = datasetsResultL == null ? void 0 : datasetsResultL.datasetMap;
2929
+ const datasetMapR = datasetsResultR == null ? void 0 : datasetsResultR.datasetMap;
2930
+ for (const dataTask of request.dataTasks) {
2931
+ const datasetL = datasetMapL == null ? void 0 : datasetMapL.get(dataTask.datasetKey);
2932
+ const datasetR = datasetMapR == null ? void 0 : datasetMapR.get(dataTask.datasetKey);
2933
+ dataTask.dataFunc({
2934
+ datasetL,
2935
+ datasetR
2936
+ });
2937
+ }
2938
+ }
2939
+ };
2940
+ function runSuite(config, callbacks, options) {
2941
+ const suiteRunner = new SuiteRunner(config, callbacks);
2942
+ suiteRunner.start(options);
2943
+ return () => {
2944
+ suiteRunner.cancel();
2945
+ };
2946
+ }
2947
+
2948
+ // src/suite/suite-summary.ts
2949
+ function suiteSummaryFromReport(suiteReport) {
2950
+ const checkSummary = checkSummaryFromReport(suiteReport.checkReport);
2951
+ let compareSummary;
2952
+ if (suiteReport.compareReport) {
2953
+ compareSummary = compareSummaryFromReport(suiteReport.compareReport);
2954
+ }
2955
+ return {
2956
+ checkSummary,
2957
+ compareSummary
2958
+ };
2959
+ }
2960
+ // Annotate the CommonJS export names for ESM import in node:
2961
+ 0 && (module.exports = {
2962
+ CheckDataCoordinator,
2963
+ CompareDataCoordinator,
2964
+ DatasetManager,
2965
+ PerfRunner,
2966
+ PerfStats,
2967
+ ScenarioManager,
2968
+ allInputsAtPositionScenario,
2969
+ checkReportFromSummary,
2970
+ checkSummaryFromReport,
2971
+ compareDatasets,
2972
+ compareSummaryFromReport,
2973
+ createConfig,
2974
+ datasetMessage,
2975
+ diffDatasets,
2976
+ diffGraphs,
2977
+ inputAtPositionScenario,
2978
+ inputAtValueScenario,
2979
+ matrixScenarios,
2980
+ positionSetting,
2981
+ predicateMessage,
2982
+ runSuite,
2983
+ scenarioMessage,
2984
+ settingsScenario,
2985
+ suiteSummaryFromReport,
2986
+ valueSetting
2987
+ });
2988
+ //# sourceMappingURL=index.cjs.map