@fairfox/polly 0.85.1 → 0.86.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/cli/polly.js.map +1 -1
- package/dist/gallery/gallery.css +1 -1
- package/dist/gallery/gallery.js +2 -27
- package/dist/scripts/build-extension.js.map +1 -1
- package/dist/src/actions/index.js +34 -32
- package/dist/src/actions/index.js.map +2 -2
- package/dist/src/background/index.js +24 -22
- package/dist/src/background/index.js.map +2 -2
- package/dist/src/background/message-router.js +21 -19
- package/dist/src/background/message-router.js.map +2 -2
- package/dist/src/client/index.js +9 -7
- package/dist/src/client/index.js.map +3 -3
- package/dist/src/client/wrapper.d.ts +3 -3
- package/dist/src/elysia/index.d.ts +1 -1
- package/dist/src/elysia/index.js +25 -23
- package/dist/src/elysia/index.js.map +2 -2
- package/dist/src/elysia/types.d.ts +11 -2
- package/dist/src/index.js +68 -64
- package/dist/src/index.js.map +3 -3
- package/dist/src/mesh-node.js +6 -6
- package/dist/src/mesh-node.js.map +1 -1
- package/dist/src/mesh.js +92 -90
- package/dist/src/mesh.js.map +2 -2
- package/dist/src/peer.js +83 -52
- package/dist/src/peer.js.map +3 -3
- package/dist/src/polly-ui/index.css +1 -1
- package/dist/src/polly-ui/index.js +37 -35
- package/dist/src/polly-ui/index.js.map +2 -2
- package/dist/src/polly-ui/markdown.js +233 -96
- package/dist/src/polly-ui/markdown.js.map +4 -4
- package/dist/src/polly-ui/styles.css +1 -1
- package/dist/src/shared/adapters/index.js +30 -28
- package/dist/src/shared/adapters/index.js.map +2 -2
- package/dist/src/shared/lib/context-helpers.js +23 -21
- package/dist/src/shared/lib/context-helpers.js.map +2 -2
- package/dist/src/shared/lib/errors.js +16 -14
- package/dist/src/shared/lib/errors.js.map +2 -2
- package/dist/src/shared/lib/guards.js +11 -9
- package/dist/src/shared/lib/guards.js.map +2 -2
- package/dist/src/shared/lib/message-bus.js +24 -22
- package/dist/src/shared/lib/message-bus.js.map +2 -2
- package/dist/src/shared/lib/peer-relay-adapter.d.ts +6 -0
- package/dist/src/shared/lib/resource.js +19 -15
- package/dist/src/shared/lib/resource.js.map +3 -3
- package/dist/src/shared/lib/state.js +26 -22
- package/dist/src/shared/lib/state.js.map +3 -3
- package/dist/src/shared/lib/test-helpers.js +11 -9
- package/dist/src/shared/lib/test-helpers.js.map +2 -2
- package/dist/src/shared/state/app-state.js +22 -18
- package/dist/src/shared/state/app-state.js.map +3 -3
- package/dist/src/shared/types/messages.js +11 -9
- package/dist/src/shared/types/messages.js.map +2 -2
- package/dist/tools/bdd/src/cli.js +44 -1
- package/dist/tools/bdd/src/cli.js.map +4 -4
- package/dist/tools/bdd/src/index.js +54 -11
- package/dist/tools/bdd/src/index.js.map +4 -4
- package/dist/tools/gallery/src/cli.js +1 -1
- package/dist/tools/gallery/src/cli.js.map +2 -2
- package/dist/tools/init/src/cli.js +1 -1
- package/dist/tools/init/src/cli.js.map +2 -2
- package/dist/tools/mutate/src/cli.js +1 -1
- package/dist/tools/mutate/src/cli.js.map +2 -2
- package/dist/tools/mutate/src/index.js +8 -8
- package/dist/tools/mutate/src/index.js.map +2 -2
- package/dist/tools/quality/src/cli.js +3 -2
- package/dist/tools/quality/src/cli.js.map +3 -3
- package/dist/tools/quality/src/index.js +37 -36
- package/dist/tools/quality/src/index.js.map +3 -3
- package/dist/tools/test/src/adapters/index.js +19 -17
- package/dist/tools/test/src/adapters/index.js.map +2 -2
- package/dist/tools/test/src/browser/index.js +15 -13
- package/dist/tools/test/src/browser/index.js.map +2 -2
- package/dist/tools/test/src/browser/run.js +12 -3
- package/dist/tools/test/src/browser/run.js.map +5 -4
- package/dist/tools/test/src/contrast/index.js +14 -12
- package/dist/tools/test/src/contrast/index.js.map +2 -2
- package/dist/tools/test/src/coverage-policy/cli.js +5 -5
- package/dist/tools/test/src/coverage-policy/cli.js.map +2 -2
- package/dist/tools/test/src/coverage-policy/index.js +12 -12
- package/dist/tools/test/src/coverage-policy/index.js.map +1 -1
- package/dist/tools/test/src/e2e-mesh/index.js +88 -50
- package/dist/tools/test/src/e2e-mesh/index.js.map +6 -5
- package/dist/tools/test/src/e2e-mesh/with-relay.d.ts +1 -1
- package/dist/tools/test/src/e2e-relay/index.js +3 -3
- package/dist/tools/test/src/e2e-relay/index.js.map +2 -2
- package/dist/tools/test/src/e2e-shared/ephemeral-port.d.ts +73 -0
- package/dist/tools/test/src/e2e-shared/index.d.ts +1 -0
- package/dist/tools/test/src/index.d.ts +1 -0
- package/dist/tools/test/src/index.js +68 -24
- package/dist/tools/test/src/index.js.map +4 -3
- package/dist/tools/test/src/test-utils.js +14 -12
- package/dist/tools/test/src/test-utils.js.map +2 -2
- package/dist/tools/test/src/tiers/budget.d.ts +32 -0
- package/dist/tools/test/src/tiers/cli.js +35 -5
- package/dist/tools/test/src/tiers/cli.js.map +6 -5
- package/dist/tools/test/src/tiers/index.d.ts +1 -0
- package/dist/tools/test/src/tiers/reporter.d.ts +0 -5
- package/dist/tools/test/src/tiers/types.d.ts +9 -0
- package/dist/tools/test/src/tiers/worker.js +1 -1
- package/dist/tools/test/src/tiers/worker.js.map +1 -1
- package/dist/tools/test/src/visual/index.js +121 -119
- package/dist/tools/test/src/visual/index.js.map +2 -2
- package/dist/tools/verify/specs/README.md +18 -0
- package/dist/tools/verify/specs/tla/MeshState.cfg +1 -1
- package/dist/tools/verify/specs/tla/MeshState.tla +7 -0
- package/dist/tools/verify/specs/tla/MessageRouter.cfg +4 -2
- package/dist/tools/verify/specs/tla/MessageRouter.tla +45 -10
- package/dist/tools/verify/specs/tla/PeerState.cfg +1 -1
- package/dist/tools/verify/specs/tla/PeerState.tla +7 -0
- package/dist/tools/verify/specs/tla/README.md +1 -1
- package/dist/tools/verify/src/cli.js +258 -202
- package/dist/tools/verify/src/cli.js.map +12 -11
- package/dist/tools/verify/src/config.d.ts +25 -0
- package/dist/tools/verify/src/config.js +18 -16
- package/dist/tools/verify/src/config.js.map +3 -3
- package/dist/tools/verify/src/stryker/index.js +6 -6
- package/dist/tools/verify/src/stryker/index.js.map +1 -1
- package/dist/tools/visualize/src/cli.js +439 -47
- package/dist/tools/visualize/src/cli.js.map +7 -5
- package/package.json +2 -2
|
@@ -252,8 +252,8 @@ var init_expression_validator = __esm(() => {
|
|
|
252
252
|
// tools/verify/src/analysis/model-coverage.ts
|
|
253
253
|
var exports_model_coverage = {};
|
|
254
254
|
__export(exports_model_coverage, {
|
|
255
|
-
|
|
256
|
-
|
|
255
|
+
computeModelCoverage: () => computeModelCoverage,
|
|
256
|
+
strictCoverageReasons: () => strictCoverageReasons
|
|
257
257
|
});
|
|
258
258
|
function norm(field) {
|
|
259
259
|
return field.replace(/_/g, ".");
|
|
@@ -504,150 +504,11 @@ var init_precondition_locality = __esm(() => {
|
|
|
504
504
|
});
|
|
505
505
|
|
|
506
506
|
// tools/verify/src/codegen/invariants.ts
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
class InvariantExtractor {
|
|
510
|
-
warnings = [];
|
|
511
|
-
extractInvariants(projectPath) {
|
|
512
|
-
const project = new Project3({
|
|
513
|
-
tsConfigFilePath: `${projectPath}/tsconfig.json`,
|
|
514
|
-
skipAddingFilesFromTsConfig: false
|
|
515
|
-
});
|
|
516
|
-
const invariants = [];
|
|
517
|
-
for (const sourceFile of project.getSourceFiles()) {
|
|
518
|
-
if (sourceFile.getFilePath().includes("node_modules") || sourceFile.getFilePath().includes(".test.") || sourceFile.getFilePath().includes(".spec.")) {
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
invariants.push(...this.extractFromSourceFile(sourceFile));
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
invariants,
|
|
525
|
-
warnings: this.warnings
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
extractFromAnalysis(_analysis) {
|
|
529
|
-
return {
|
|
530
|
-
invariants: [],
|
|
531
|
-
warnings: []
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
extractFromSourceFile(sourceFile) {
|
|
535
|
-
const invariants = [];
|
|
536
|
-
sourceFile.forEachDescendant((node) => {
|
|
537
|
-
this.processNodeJSDocs(node, sourceFile, invariants);
|
|
538
|
-
});
|
|
539
|
-
return invariants;
|
|
540
|
-
}
|
|
541
|
-
processNodeJSDocs(node, sourceFile, invariants) {
|
|
542
|
-
const jsDocs = this.getJsDocComments(node);
|
|
543
|
-
for (const jsDoc of jsDocs) {
|
|
544
|
-
this.processJSDocTags(jsDoc, sourceFile, node, invariants);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
processJSDocTags(jsDoc, sourceFile, node, invariants) {
|
|
548
|
-
for (const tag of jsDoc.getTags()) {
|
|
549
|
-
this.processJSDocTag(tag, sourceFile, node, invariants);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
processJSDocTag(tag, sourceFile, node, invariants) {
|
|
553
|
-
const tagName = tag.getTagName();
|
|
554
|
-
const text = tag.getComment();
|
|
555
|
-
if (typeof text !== "string" || !text.trim()) {
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
const trimmedText = text.trim();
|
|
559
|
-
if (tagName === "invariant") {
|
|
560
|
-
invariants.push(this.createInvariant(trimmedText, "invariant", sourceFile, node));
|
|
561
|
-
} else if (tagName === "ensures") {
|
|
562
|
-
invariants.push(this.createInvariant(trimmedText, "post-condition", sourceFile, node));
|
|
563
|
-
} else if (tagName === "requires") {
|
|
564
|
-
invariants.push(this.createInvariant(trimmedText, "pre-condition", sourceFile, node));
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
getJsDocComments(node) {
|
|
568
|
-
if (Node3.isJSDocable(node)) {
|
|
569
|
-
return node.getJsDocs();
|
|
570
|
-
}
|
|
507
|
+
function describe(description) {
|
|
508
|
+
if (!description?.trim())
|
|
571
509
|
return [];
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const description = this.extractDescription(node, expression);
|
|
575
|
-
const name = this.generateInvariantName(expression, type);
|
|
576
|
-
return {
|
|
577
|
-
name,
|
|
578
|
-
description,
|
|
579
|
-
expression,
|
|
580
|
-
location: {
|
|
581
|
-
file: sourceFile.getFilePath(),
|
|
582
|
-
line: node.getStartLineNumber()
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
extractDescription(node, expression) {
|
|
587
|
-
if (Node3.isJSDocable(node)) {
|
|
588
|
-
const jsDocs = node.getJsDocs();
|
|
589
|
-
for (const jsDoc of jsDocs) {
|
|
590
|
-
const desc = jsDoc.getDescription();
|
|
591
|
-
if (desc?.trim()) {
|
|
592
|
-
return desc.trim();
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
return `Invariant: ${expression}`;
|
|
597
|
-
}
|
|
598
|
-
generateInvariantName(expression, type) {
|
|
599
|
-
const fieldMatches = expression.match(/state\.(\w+)/g);
|
|
600
|
-
if (!fieldMatches || fieldMatches.length === 0) {
|
|
601
|
-
return this.generateGenericInvariantName(type);
|
|
602
|
-
}
|
|
603
|
-
const fieldName = fieldMatches[0]?.replace("state.", "");
|
|
604
|
-
const constraintSuffix = this.determineConstraintSuffix(expression);
|
|
605
|
-
const capitalizedField = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
|
|
606
|
-
const prefix = this.getInvariantPrefix(type);
|
|
607
|
-
return `${prefix}${capitalizedField}${constraintSuffix}`;
|
|
608
|
-
}
|
|
609
|
-
generateGenericInvariantName(type) {
|
|
610
|
-
const prefix = this.getInvariantPrefix(type);
|
|
611
|
-
return `${prefix}Invariant${Math.random().toString(36).substring(2, 7)}`;
|
|
612
|
-
}
|
|
613
|
-
determineConstraintSuffix(expression) {
|
|
614
|
-
if (expression.includes("<="))
|
|
615
|
-
return "MaxValue";
|
|
616
|
-
if (expression.includes(">="))
|
|
617
|
-
return "MinValue";
|
|
618
|
-
if (expression.includes("===") || expression.includes("=="))
|
|
619
|
-
return "Equals";
|
|
620
|
-
if (expression.includes("!==") || expression.includes("!="))
|
|
621
|
-
return "NotEquals";
|
|
622
|
-
if (expression.includes("<"))
|
|
623
|
-
return "LessThan";
|
|
624
|
-
if (expression.includes(">"))
|
|
625
|
-
return "GreaterThan";
|
|
626
|
-
return "Check";
|
|
627
|
-
}
|
|
628
|
-
getInvariantPrefix(type) {
|
|
629
|
-
if (type === "pre-condition")
|
|
630
|
-
return "Pre";
|
|
631
|
-
if (type === "post-condition")
|
|
632
|
-
return "Post";
|
|
633
|
-
return "";
|
|
634
|
-
}
|
|
635
|
-
validateExpression(expression) {
|
|
636
|
-
if (expression.includes("eval(")) {
|
|
637
|
-
return { valid: false, error: "eval() not allowed in invariants" };
|
|
638
|
-
}
|
|
639
|
-
const openParens = (expression.match(/\(/g) || []).length;
|
|
640
|
-
const closeParens = (expression.match(/\)/g) || []).length;
|
|
641
|
-
if (openParens !== closeParens) {
|
|
642
|
-
return { valid: false, error: "Unbalanced parentheses" };
|
|
643
|
-
}
|
|
644
|
-
const openBrackets = (expression.match(/\[/g) || []).length;
|
|
645
|
-
const closeBrackets = (expression.match(/\]/g) || []).length;
|
|
646
|
-
if (openBrackets !== closeBrackets) {
|
|
647
|
-
return { valid: false, error: "Unbalanced brackets" };
|
|
648
|
-
}
|
|
649
|
-
return { valid: true };
|
|
650
|
-
}
|
|
510
|
+
return description.split(`
|
|
511
|
+
`).map((line) => `\\* ${line.trimEnd()}`);
|
|
651
512
|
}
|
|
652
513
|
|
|
653
514
|
class InvariantGenerator {
|
|
@@ -660,8 +521,8 @@ class InvariantGenerator {
|
|
|
660
521
|
}
|
|
661
522
|
generateSingleInvariant(invariant, tsExpressionToTLA) {
|
|
662
523
|
const lines = [];
|
|
663
|
-
|
|
664
|
-
lines.push(
|
|
524
|
+
for (const line of describe(invariant.description)) {
|
|
525
|
+
lines.push(line);
|
|
665
526
|
}
|
|
666
527
|
const tlaExpr = tsExpressionToTLA(invariant.expression);
|
|
667
528
|
lines.push(`${invariant.name} ==`);
|
|
@@ -673,7 +534,6 @@ class InvariantGenerator {
|
|
|
673
534
|
return invariants.map((inv) => `INVARIANT ${inv.name}`);
|
|
674
535
|
}
|
|
675
536
|
}
|
|
676
|
-
var init_invariants = () => {};
|
|
677
537
|
|
|
678
538
|
// tools/verify/src/codegen/temporal.ts
|
|
679
539
|
class TemporalPropertyGenerator {
|
|
@@ -773,8 +633,8 @@ class TemporalTLAGenerator {
|
|
|
773
633
|
}
|
|
774
634
|
generateSingleProperty(prop) {
|
|
775
635
|
const lines = [];
|
|
776
|
-
|
|
777
|
-
lines.push(
|
|
636
|
+
for (const line of describe(prop.description)) {
|
|
637
|
+
lines.push(line);
|
|
778
638
|
}
|
|
779
639
|
switch (prop.type) {
|
|
780
640
|
case "eventually":
|
|
@@ -829,14 +689,15 @@ class TemporalTLAGenerator {
|
|
|
829
689
|
`);
|
|
830
690
|
}
|
|
831
691
|
}
|
|
692
|
+
var init_temporal = () => {};
|
|
832
693
|
|
|
833
694
|
// tools/verify/src/codegen/tla.ts
|
|
834
695
|
var exports_tla = {};
|
|
835
696
|
__export(exports_tla, {
|
|
836
|
-
|
|
837
|
-
generateSubsystemTLA: () => generateSubsystemTLA,
|
|
697
|
+
TLAGenerator: () => TLAGenerator,
|
|
838
698
|
TLAValidationError: () => TLAValidationError,
|
|
839
|
-
|
|
699
|
+
generateSubsystemTLA: () => generateSubsystemTLA,
|
|
700
|
+
generateTLA: () => generateTLA
|
|
840
701
|
});
|
|
841
702
|
import * as fs2 from "node:fs";
|
|
842
703
|
import * as os from "node:os";
|
|
@@ -890,6 +751,7 @@ async function generateSubsystemTLA(_subsystemName, subsystem, config, analysis)
|
|
|
890
751
|
...config,
|
|
891
752
|
state: filteredState,
|
|
892
753
|
messages: filteredMessages,
|
|
754
|
+
liveness: subsystem.liveness ?? config.liveness,
|
|
893
755
|
subsystems: undefined
|
|
894
756
|
};
|
|
895
757
|
if (filteredConfig.tier2?.temporalConstraints) {
|
|
@@ -917,7 +779,7 @@ async function generateSubsystemTLA(_subsystemName, subsystem, config, analysis)
|
|
|
917
779
|
}
|
|
918
780
|
var TLAValidationError, TLAGenerator;
|
|
919
781
|
var init_tla = __esm(() => {
|
|
920
|
-
|
|
782
|
+
init_temporal();
|
|
921
783
|
TLAValidationError = class TLAValidationError extends Error {
|
|
922
784
|
report;
|
|
923
785
|
constructor(message, report) {
|
|
@@ -932,6 +794,7 @@ var init_tla = __esm(() => {
|
|
|
932
794
|
indent = 0;
|
|
933
795
|
extractedInvariants = [];
|
|
934
796
|
temporalProperties = [];
|
|
797
|
+
livenessPropertyNames = [];
|
|
935
798
|
symmetrySets = [];
|
|
936
799
|
resolvedActionNames = new Map;
|
|
937
800
|
paramDomains = new Map;
|
|
@@ -970,10 +833,10 @@ var init_tla = __esm(() => {
|
|
|
970
833
|
}
|
|
971
834
|
}
|
|
972
835
|
this.validateInputs(config, analysis);
|
|
973
|
-
this.extractInvariantsIfEnabled();
|
|
974
836
|
this.generateTemporalPropertiesIfEnabled(analysis);
|
|
975
837
|
this.lines = [];
|
|
976
838
|
this.indent = 0;
|
|
839
|
+
this.livenessPropertyNames = [];
|
|
977
840
|
const spec = this.generateSpec(config, analysis);
|
|
978
841
|
const cfg = this.generateConfig(config);
|
|
979
842
|
if (!this.hasValidators()) {
|
|
@@ -985,19 +848,6 @@ var init_tla = __esm(() => {
|
|
|
985
848
|
hasValidators() {
|
|
986
849
|
return !!(this.options?.validator || this.options?.sanyRunner || this.options?.roundTripValidator);
|
|
987
850
|
}
|
|
988
|
-
extractInvariantsIfEnabled() {
|
|
989
|
-
if (this.options?.enableInvariants && this.options.projectPath) {
|
|
990
|
-
const extractor = new InvariantExtractor;
|
|
991
|
-
const result = extractor.extractInvariants(this.options.projectPath);
|
|
992
|
-
this.extractedInvariants = result.invariants;
|
|
993
|
-
if (result.warnings.length > 0 && process.env["POLLY_DEBUG"]) {
|
|
994
|
-
console.log("[DEBUG] Invariant extraction warnings:");
|
|
995
|
-
for (const warning of result.warnings) {
|
|
996
|
-
console.log(` - ${warning}`);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
851
|
generateTemporalPropertiesIfEnabled(analysis) {
|
|
1002
852
|
if (this.options?.enableTemporalProperties) {
|
|
1003
853
|
const generator = new TemporalPropertyGenerator;
|
|
@@ -1129,11 +979,12 @@ var init_tla = __esm(() => {
|
|
|
1129
979
|
this.addActions(config, analysis);
|
|
1130
980
|
this.addRouteWithHandlers(config, analysis);
|
|
1131
981
|
this.addNext(config, analysis);
|
|
1132
|
-
this.addSpec();
|
|
982
|
+
this.addSpec(config, analysis);
|
|
1133
983
|
this.addInvariants(config, analysis);
|
|
1134
984
|
if (this.temporalProperties.length > 0) {
|
|
1135
985
|
this.addTemporalProperties();
|
|
1136
986
|
}
|
|
987
|
+
this.addLivenessProperties(config, analysis);
|
|
1137
988
|
this.line("=============================================================================");
|
|
1138
989
|
return this.lines.join(`
|
|
1139
990
|
`);
|
|
@@ -1230,7 +1081,7 @@ var init_tla = __esm(() => {
|
|
|
1230
1081
|
}
|
|
1231
1082
|
}
|
|
1232
1083
|
addTemporalPropertiesSection(lines) {
|
|
1233
|
-
if (this.temporalProperties.length === 0)
|
|
1084
|
+
if (this.temporalProperties.length === 0 && this.livenessPropertyNames.length === 0)
|
|
1234
1085
|
return;
|
|
1235
1086
|
lines.push("");
|
|
1236
1087
|
lines.push("\\* Temporal properties to check");
|
|
@@ -1238,6 +1089,9 @@ var init_tla = __esm(() => {
|
|
|
1238
1089
|
for (const prop of this.temporalProperties) {
|
|
1239
1090
|
lines.push(` ${prop.name}`);
|
|
1240
1091
|
}
|
|
1092
|
+
for (const name of this.livenessPropertyNames) {
|
|
1093
|
+
lines.push(` ${name}`);
|
|
1094
|
+
}
|
|
1241
1095
|
}
|
|
1242
1096
|
addConstraintSection(lines) {
|
|
1243
1097
|
lines.push("");
|
|
@@ -2742,6 +2596,7 @@ var init_tla = __esm(() => {
|
|
|
2742
2596
|
this.line(" /\\ routingDepth < 5");
|
|
2743
2597
|
this.line(" /\\ \\E target \\in msg.targets :");
|
|
2744
2598
|
this.line(' /\\ IF target \\in Contexts /\\ ports[target] = "connected"');
|
|
2599
|
+
this.line(" /\\ ENABLED StateTransition(target, msg.msgType)");
|
|
2745
2600
|
this.line(" THEN \\* Successful delivery - route AND invoke handler");
|
|
2746
2601
|
this.line(` /\\ messages' = [messages EXCEPT ![msgIndex].status = "delivered",`);
|
|
2747
2602
|
this.line(" ![msgIndex].deliveredTo = target]");
|
|
@@ -2751,7 +2606,7 @@ var init_tla = __esm(() => {
|
|
|
2751
2606
|
this.line(" /\\ time' = time + 1");
|
|
2752
2607
|
this.line(" /\\ \\E p \\in PayloadType : payload' = p \\* Non-deterministic payload");
|
|
2753
2608
|
this.line(" /\\ StateTransition(target, msg.msgType)");
|
|
2754
|
-
this.line(" ELSE \\* Port not connected
|
|
2609
|
+
this.line(" ELSE \\* Port not connected, or the handler refused it");
|
|
2755
2610
|
this.line(` /\\ messages' = [messages EXCEPT ![msgIndex].status = "failed"]`);
|
|
2756
2611
|
this.line(" /\\ pendingRequests' = [id \\in DOMAIN pendingRequests \\ {msg.id} |->");
|
|
2757
2612
|
this.line(" pendingRequests[id]]");
|
|
@@ -2802,9 +2657,50 @@ var init_tla = __esm(() => {
|
|
|
2802
2657
|
this.indent--;
|
|
2803
2658
|
this.line("");
|
|
2804
2659
|
}
|
|
2805
|
-
|
|
2660
|
+
livenessEnabled(config) {
|
|
2661
|
+
return config.liveness === true;
|
|
2662
|
+
}
|
|
2663
|
+
routingIsReachable(analysis) {
|
|
2664
|
+
return analysis.handlers.some((h) => this.canRepresentAsTLAAction(h.messageType));
|
|
2665
|
+
}
|
|
2666
|
+
addSpec(config, analysis) {
|
|
2806
2667
|
this.line("\\* Specification");
|
|
2807
|
-
this.
|
|
2668
|
+
const liveness = this.livenessEnabled(config) && this.routingIsReachable(analysis);
|
|
2669
|
+
if (!liveness) {
|
|
2670
|
+
this.line("UserSpec == UserInit /\\ [][UserNext]_allVars /\\ WF_allVars(UserNext)");
|
|
2671
|
+
this.line("");
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
this.line("UserSpec ==");
|
|
2675
|
+
this.indent++;
|
|
2676
|
+
this.line("/\\ UserInit");
|
|
2677
|
+
this.line("/\\ [][UserNext]_allVars");
|
|
2678
|
+
this.line("/\\ WF_allVars(UserNext)");
|
|
2679
|
+
this.line("/\\ \\A i \\in 1..MaxMessages : WF_allVars(UserRouteMessage(i))");
|
|
2680
|
+
this.indent--;
|
|
2681
|
+
this.line("");
|
|
2682
|
+
}
|
|
2683
|
+
addLivenessProperties(config, analysis) {
|
|
2684
|
+
if (!this.livenessEnabled(config) || !this.routingIsReachable(analysis))
|
|
2685
|
+
return;
|
|
2686
|
+
const name = "NoMessageStaysPending";
|
|
2687
|
+
this.livenessPropertyNames.push(name);
|
|
2688
|
+
this.line("\\* =============================================================================");
|
|
2689
|
+
this.line("\\* Liveness (polly#171)");
|
|
2690
|
+
this.line("\\* =============================================================================");
|
|
2691
|
+
this.line("");
|
|
2692
|
+
this.line("\\* Every message that reaches the router is eventually resolved.");
|
|
2693
|
+
this.line("\\* A requires() that does not hold marks the message failed rather than");
|
|
2694
|
+
this.line("\\* leaving it pending for ever, so this is checkable.");
|
|
2695
|
+
this.line(`${name} ==`);
|
|
2696
|
+
this.indent++;
|
|
2697
|
+
this.line("\\A i \\in 1..MaxMessages :");
|
|
2698
|
+
this.indent++;
|
|
2699
|
+
this.line('(Len(messages) >= i /\\ messages[i].status = "pending")');
|
|
2700
|
+
this.line("~> (Len(messages) >= i");
|
|
2701
|
+
this.line(' /\\ messages[i].status \\in {"delivered", "failed", "timeout"})');
|
|
2702
|
+
this.indent--;
|
|
2703
|
+
this.indent--;
|
|
2808
2704
|
this.line("");
|
|
2809
2705
|
}
|
|
2810
2706
|
addInvariants(config, _analysis) {
|
|
@@ -2827,7 +2723,7 @@ var init_tla = __esm(() => {
|
|
|
2827
2723
|
this.addCapabilityInvariants(config.capabilities);
|
|
2828
2724
|
}
|
|
2829
2725
|
if (this.extractedInvariants.length > 0) {
|
|
2830
|
-
this.line("\\*
|
|
2726
|
+
this.line("\\* Synthesised invariants");
|
|
2831
2727
|
this.line("");
|
|
2832
2728
|
const invGenerator = new InvariantGenerator;
|
|
2833
2729
|
const tlaInvariants = invGenerator.generateTLAInvariants(this.extractedInvariants, (expr) => this.tsExpressionToTLA(expr, false));
|
|
@@ -3390,19 +3286,19 @@ var init_witness = __esm(() => {
|
|
|
3390
3286
|
// tools/verify/src/codegen/witness.ts
|
|
3391
3287
|
var exports_witness2 = {};
|
|
3392
3288
|
__export(exports_witness2, {
|
|
3393
|
-
|
|
3394
|
-
witnessSpecLocation: () => witnessSpecLocation,
|
|
3395
|
-
witnessPolarity: () => witnessPolarity,
|
|
3396
|
-
routeWitness: () => routeWitness,
|
|
3397
|
-
parseModuleName: () => parseModuleName,
|
|
3398
|
-
buildWitnessModule: () => buildWitnessModule,
|
|
3399
|
-
buildWitnessInvariantBare: () => buildWitnessInvariantBare,
|
|
3400
|
-
buildWitnessInvariant: () => buildWitnessInvariant,
|
|
3401
|
-
buildWitnessCfg: () => buildWitnessCfg,
|
|
3402
|
-
bddPredicateToTLA: () => bddPredicateToTLA,
|
|
3403
|
-
bareFieldRenderer: () => bareFieldRenderer,
|
|
3289
|
+
WITNESS_INVARIANT: () => WITNESS_INVARIANT,
|
|
3404
3290
|
WitnessTranslationError: () => WitnessTranslationError,
|
|
3405
|
-
|
|
3291
|
+
bareFieldRenderer: () => bareFieldRenderer,
|
|
3292
|
+
bddPredicateToTLA: () => bddPredicateToTLA,
|
|
3293
|
+
buildWitnessCfg: () => buildWitnessCfg,
|
|
3294
|
+
buildWitnessInvariant: () => buildWitnessInvariant,
|
|
3295
|
+
buildWitnessInvariantBare: () => buildWitnessInvariantBare,
|
|
3296
|
+
buildWitnessModule: () => buildWitnessModule,
|
|
3297
|
+
parseModuleName: () => parseModuleName,
|
|
3298
|
+
routeWitness: () => routeWitness,
|
|
3299
|
+
witnessPolarity: () => witnessPolarity,
|
|
3300
|
+
witnessSpecLocation: () => witnessSpecLocation,
|
|
3301
|
+
witnessVerdict: () => witnessVerdict
|
|
3406
3302
|
});
|
|
3407
3303
|
import * as path3 from "node:path";
|
|
3408
3304
|
function witnessPolarity(tags) {
|
|
@@ -3552,9 +3448,12 @@ function constantBody(cfg) {
|
|
|
3552
3448
|
function headerLine(cfg, header) {
|
|
3553
3449
|
for (const line of cfg.split(`
|
|
3554
3450
|
`)) {
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3451
|
+
if (!line.startsWith(header))
|
|
3452
|
+
continue;
|
|
3453
|
+
const next = line.charAt(header.length);
|
|
3454
|
+
if (next !== "" && /\w/.test(next))
|
|
3455
|
+
continue;
|
|
3456
|
+
return line.trimEnd();
|
|
3558
3457
|
}
|
|
3559
3458
|
return null;
|
|
3560
3459
|
}
|
|
@@ -3609,14 +3508,62 @@ var init_witness2 = __esm(() => {
|
|
|
3609
3508
|
];
|
|
3610
3509
|
});
|
|
3611
3510
|
|
|
3511
|
+
// tools/verify/src/runner/container-name.ts
|
|
3512
|
+
function mintContainerName(kind) {
|
|
3513
|
+
counter += 1;
|
|
3514
|
+
const unique = crypto.randomUUID().slice(0, 8);
|
|
3515
|
+
return `${CONTAINER_NAME_PREFIX}${sanitizeKind(kind)}-${process.pid}-${counter}-${unique}`;
|
|
3516
|
+
}
|
|
3517
|
+
function sanitizeKind(kind) {
|
|
3518
|
+
const cleaned = kind.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
3519
|
+
return cleaned.length > 0 ? cleaned : "run";
|
|
3520
|
+
}
|
|
3521
|
+
var CONTAINER_NAME_PREFIX = "polly-tla-", counter = 0;
|
|
3522
|
+
|
|
3612
3523
|
// tools/verify/src/runner/docker.ts
|
|
3613
3524
|
var exports_docker = {};
|
|
3614
3525
|
__export(exports_docker, {
|
|
3615
|
-
DockerRunner: () => DockerRunner
|
|
3526
|
+
DockerRunner: () => DockerRunner,
|
|
3527
|
+
removeContainer: () => removeContainer,
|
|
3528
|
+
sweepOrphanedContainers: () => sweepOrphanedContainers,
|
|
3529
|
+
sweepOrphanedContainersOnce: () => sweepOrphanedContainersOnce
|
|
3616
3530
|
});
|
|
3617
|
-
import { spawn } from "node:child_process";
|
|
3531
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3618
3532
|
import * as fs3 from "node:fs";
|
|
3619
3533
|
import * as path4 from "node:path";
|
|
3534
|
+
function removeContainer(name) {
|
|
3535
|
+
try {
|
|
3536
|
+
spawnSync("docker", ["rm", "-f", name], { timeout: 1e4, stdio: "ignore" });
|
|
3537
|
+
} catch {}
|
|
3538
|
+
}
|
|
3539
|
+
function sweepOrphanedContainersOnce() {
|
|
3540
|
+
if (swept)
|
|
3541
|
+
return [];
|
|
3542
|
+
swept = true;
|
|
3543
|
+
return sweepOrphanedContainers();
|
|
3544
|
+
}
|
|
3545
|
+
function sweepOrphanedContainers() {
|
|
3546
|
+
try {
|
|
3547
|
+
const listed = spawnSync("docker", [
|
|
3548
|
+
"ps",
|
|
3549
|
+
"--all",
|
|
3550
|
+
"--filter",
|
|
3551
|
+
"status=created",
|
|
3552
|
+
"--filter",
|
|
3553
|
+
`name=${CONTAINER_NAME_PREFIX}`,
|
|
3554
|
+
"--format",
|
|
3555
|
+
"{{.Names}}"
|
|
3556
|
+
], { timeout: 1e4, encoding: "utf8" });
|
|
3557
|
+
const names = (listed.stdout ?? "").split(`
|
|
3558
|
+
`).map((line) => line.trim()).filter((line) => line.startsWith(CONTAINER_NAME_PREFIX));
|
|
3559
|
+
for (const name of names) {
|
|
3560
|
+
removeContainer(name);
|
|
3561
|
+
}
|
|
3562
|
+
return names;
|
|
3563
|
+
} catch {
|
|
3564
|
+
return [];
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3620
3567
|
|
|
3621
3568
|
class DockerRunner {
|
|
3622
3569
|
IMAGE_NAME = "polly-tla:latest";
|
|
@@ -3668,9 +3615,13 @@ class DockerRunner {
|
|
|
3668
3615
|
if (fs3.existsSync(statesDir)) {
|
|
3669
3616
|
fs3.rmSync(statesDir, { recursive: true, force: true });
|
|
3670
3617
|
}
|
|
3618
|
+
const containerName = mintContainerName("tlc");
|
|
3619
|
+
sweepOrphanedContainersOnce();
|
|
3671
3620
|
const args = [
|
|
3672
3621
|
"run",
|
|
3673
3622
|
"--rm",
|
|
3623
|
+
"--name",
|
|
3624
|
+
containerName,
|
|
3674
3625
|
"-v",
|
|
3675
3626
|
`${specDir}:/work`,
|
|
3676
3627
|
this.IMAGE_NAME,
|
|
@@ -3686,7 +3637,8 @@ class DockerRunner {
|
|
|
3686
3637
|
}
|
|
3687
3638
|
args.push(`${specName}.tla`);
|
|
3688
3639
|
const result = await this.runCommand("docker", args, {
|
|
3689
|
-
timeout: options?.timeout
|
|
3640
|
+
timeout: options?.timeout,
|
|
3641
|
+
containerName
|
|
3690
3642
|
});
|
|
3691
3643
|
return this.parseTLCOutput(result);
|
|
3692
3644
|
}
|
|
@@ -3769,7 +3721,10 @@ class DockerRunner {
|
|
|
3769
3721
|
});
|
|
3770
3722
|
const timeoutValue = options?.timeout ?? 0;
|
|
3771
3723
|
const timeout = timeoutValue > 0 ? setTimeout(() => {
|
|
3772
|
-
proc.kill();
|
|
3724
|
+
proc.kill("SIGKILL");
|
|
3725
|
+
if (options?.containerName) {
|
|
3726
|
+
removeContainer(options.containerName);
|
|
3727
|
+
}
|
|
3773
3728
|
reject(new Error(`Command timed out after ${Math.floor(timeoutValue / 1000)}s. TLC was still making progress. Consider increasing the timeout or setting timeout: 0 for no timeout.`));
|
|
3774
3729
|
}, timeoutValue) : null;
|
|
3775
3730
|
proc.on("close", (exitCode) => {
|
|
@@ -3834,7 +3789,7 @@ class DockerRunner {
|
|
|
3834
3789
|
});
|
|
3835
3790
|
}
|
|
3836
3791
|
}
|
|
3837
|
-
var __dirname = "/Users/AJT/projects/polly/packages/polly/tools/verify/src/runner";
|
|
3792
|
+
var __dirname = "/Users/AJT/projects/polly/packages/polly/tools/verify/src/runner", swept = false;
|
|
3838
3793
|
var init_docker = () => {};
|
|
3839
3794
|
|
|
3840
3795
|
// tools/verify/src/cli.ts
|
|
@@ -5161,7 +5116,8 @@ import { Project as Project2 } from "ts-morph";
|
|
|
5161
5116
|
import {
|
|
5162
5117
|
Node as Node2,
|
|
5163
5118
|
Project,
|
|
5164
|
-
SyntaxKind
|
|
5119
|
+
SyntaxKind,
|
|
5120
|
+
VariableDeclarationKind
|
|
5165
5121
|
} from "ts-morph";
|
|
5166
5122
|
|
|
5167
5123
|
// tools/analysis/src/extract/relationships.ts
|
|
@@ -6547,16 +6503,116 @@ class HandlerExtractor {
|
|
|
6547
6503
|
return;
|
|
6548
6504
|
}
|
|
6549
6505
|
if (this.isTranslatableInitializer(initializer)) {
|
|
6550
|
-
|
|
6506
|
+
const captured = this.resolveSignalAliases(this.unwrapParentheses(initializer));
|
|
6507
|
+
assignments.push({ field, value: `EXPR:${captured}` });
|
|
6551
6508
|
return;
|
|
6552
6509
|
}
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6510
|
+
const on = signalName ? ` on ${signalName}.value` : "";
|
|
6511
|
+
console.log(`[WARN] dropped object-literal property '${name}'${on} — initializer \`${initializer.getText().replace(/\s+/g, " ").slice(0, 80)}\` (${initializer.getKindName()}) is not translatable to TLA+`);
|
|
6512
|
+
}
|
|
6513
|
+
resolveSignalAliases(initializer) {
|
|
6514
|
+
const aliases = this.collectSignalAliases(initializer);
|
|
6515
|
+
if (aliases.size === 0)
|
|
6516
|
+
return initializer.getText();
|
|
6517
|
+
const base = initializer.getStart();
|
|
6518
|
+
const edits = [];
|
|
6519
|
+
const visit = (node) => {
|
|
6520
|
+
if (!Node2.isPropertyAccessExpression(node))
|
|
6521
|
+
return;
|
|
6522
|
+
const target = node.getExpression();
|
|
6523
|
+
if (!Node2.isIdentifier(target))
|
|
6524
|
+
return;
|
|
6525
|
+
const signal = aliases.get(target.getText());
|
|
6526
|
+
if (!signal)
|
|
6527
|
+
return;
|
|
6528
|
+
edits.push({
|
|
6529
|
+
start: target.getStart() - base,
|
|
6530
|
+
end: target.getEnd() - base,
|
|
6531
|
+
text: `${signal}.value`
|
|
6532
|
+
});
|
|
6533
|
+
};
|
|
6534
|
+
visit(initializer);
|
|
6535
|
+
initializer.forEachDescendant(visit);
|
|
6536
|
+
if (edits.length === 0)
|
|
6537
|
+
return initializer.getText();
|
|
6538
|
+
let text = initializer.getText();
|
|
6539
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
6540
|
+
text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
|
|
6541
|
+
}
|
|
6542
|
+
return text;
|
|
6543
|
+
}
|
|
6544
|
+
collectSignalAliases(node) {
|
|
6545
|
+
const aliases = new Map;
|
|
6546
|
+
const fn = node.getFirstAncestor((a) => Node2.isArrowFunction(a) || Node2.isFunctionExpression(a) || Node2.isFunctionDeclaration(a));
|
|
6547
|
+
if (!fn)
|
|
6548
|
+
return aliases;
|
|
6549
|
+
const reassigned = this.collectReassignedIdentifiers(fn);
|
|
6550
|
+
fn.forEachDescendant((inner) => {
|
|
6551
|
+
if (!Node2.isVariableDeclaration(inner))
|
|
6552
|
+
return;
|
|
6553
|
+
const binding = this.readSignalAliasBinding(inner);
|
|
6554
|
+
if (!binding)
|
|
6555
|
+
return;
|
|
6556
|
+
if (aliases.has(binding.signal))
|
|
6557
|
+
return;
|
|
6558
|
+
if (reassigned.has(binding.alias))
|
|
6559
|
+
return;
|
|
6560
|
+
aliases.set(binding.alias, binding.signal);
|
|
6561
|
+
});
|
|
6562
|
+
return aliases;
|
|
6563
|
+
}
|
|
6564
|
+
collectReassignedIdentifiers(fn) {
|
|
6565
|
+
const reassigned = new Set;
|
|
6566
|
+
fn.forEachDescendant((inner) => {
|
|
6567
|
+
if (!Node2.isBinaryExpression(inner))
|
|
6568
|
+
return;
|
|
6569
|
+
if (inner.getOperatorToken().getText() !== "=")
|
|
6570
|
+
return;
|
|
6571
|
+
const left = inner.getLeft();
|
|
6572
|
+
if (Node2.isIdentifier(left))
|
|
6573
|
+
reassigned.add(left.getText());
|
|
6574
|
+
});
|
|
6575
|
+
return reassigned;
|
|
6576
|
+
}
|
|
6577
|
+
readSignalAliasBinding(decl) {
|
|
6578
|
+
const list = decl.getParent();
|
|
6579
|
+
if (!Node2.isVariableDeclarationList(list))
|
|
6580
|
+
return null;
|
|
6581
|
+
if (list.getDeclarationKind() !== VariableDeclarationKind.Const)
|
|
6582
|
+
return null;
|
|
6583
|
+
const nameNode = decl.getNameNode();
|
|
6584
|
+
if (!Node2.isIdentifier(nameNode))
|
|
6585
|
+
return null;
|
|
6586
|
+
const init = decl.getInitializer();
|
|
6587
|
+
if (!init || !Node2.isPropertyAccessExpression(init))
|
|
6588
|
+
return null;
|
|
6589
|
+
if (init.getName() !== "value")
|
|
6590
|
+
return null;
|
|
6591
|
+
const target = init.getExpression();
|
|
6592
|
+
if (!Node2.isIdentifier(target))
|
|
6593
|
+
return null;
|
|
6594
|
+
return { alias: nameNode.getText(), signal: target.getText() };
|
|
6557
6595
|
}
|
|
6558
6596
|
isTranslatableInitializer(node) {
|
|
6559
|
-
|
|
6597
|
+
const kindAllowed = Node2.isBinaryExpression(node) || Node2.isPropertyAccessExpression(node) || Node2.isElementAccessExpression(node) || Node2.isPrefixUnaryExpression(node) || Node2.isPostfixUnaryExpression(node) || Node2.isParenthesizedExpression(node) || Node2.isConditionalExpression(node);
|
|
6598
|
+
if (!kindAllowed)
|
|
6599
|
+
return false;
|
|
6600
|
+
if (this.containsComment(node))
|
|
6601
|
+
return false;
|
|
6602
|
+
if (node.getFirstDescendantByKind(SyntaxKind.CallExpression))
|
|
6603
|
+
return false;
|
|
6604
|
+
return this.unwrapParentheses(node).getFirstDescendantByKind(SyntaxKind.ConditionalExpression) === undefined;
|
|
6605
|
+
}
|
|
6606
|
+
unwrapParentheses(node) {
|
|
6607
|
+
let current = node;
|
|
6608
|
+
while (Node2.isParenthesizedExpression(current)) {
|
|
6609
|
+
current = current.getExpression();
|
|
6610
|
+
}
|
|
6611
|
+
return current;
|
|
6612
|
+
}
|
|
6613
|
+
containsComment(node) {
|
|
6614
|
+
const text = node.getText();
|
|
6615
|
+
return text.includes("//") || text.includes("/*");
|
|
6560
6616
|
}
|
|
6561
6617
|
extractPayloadPropertyParam(initializer) {
|
|
6562
6618
|
if (!Node2.isPropertyAccessExpression(initializer))
|
|
@@ -9584,4 +9640,4 @@ main().catch((error) => {
|
|
|
9584
9640
|
process.exit(1);
|
|
9585
9641
|
});
|
|
9586
9642
|
|
|
9587
|
-
//# debugId=
|
|
9643
|
+
//# debugId=D4D47405397BD18D64756E2164756E21
|