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