@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
|
@@ -20,8 +20,8 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
20
20
|
// tools/analysis/src/extract/project-detector.ts
|
|
21
21
|
var exports_project_detector = {};
|
|
22
22
|
__export(exports_project_detector, {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
ProjectDetector: () => ProjectDetector,
|
|
24
|
+
detectProjectConfig: () => detectProjectConfig
|
|
25
25
|
});
|
|
26
26
|
import * as fs3 from "node:fs";
|
|
27
27
|
import * as path3 from "node:path";
|
|
@@ -666,9 +666,293 @@ function detectProjectConfig(projectRoot) {
|
|
|
666
666
|
}
|
|
667
667
|
var init_project_detector = () => {};
|
|
668
668
|
|
|
669
|
+
// tools/verify/src/runner/container-name.ts
|
|
670
|
+
function mintContainerName(kind) {
|
|
671
|
+
counter += 1;
|
|
672
|
+
const unique = crypto.randomUUID().slice(0, 8);
|
|
673
|
+
return `${CONTAINER_NAME_PREFIX}${sanitizeKind(kind)}-${process.pid}-${counter}-${unique}`;
|
|
674
|
+
}
|
|
675
|
+
function sanitizeKind(kind) {
|
|
676
|
+
const cleaned = kind.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
677
|
+
return cleaned.length > 0 ? cleaned : "run";
|
|
678
|
+
}
|
|
679
|
+
var CONTAINER_NAME_PREFIX = "polly-tla-", counter = 0;
|
|
680
|
+
|
|
681
|
+
// tools/verify/src/runner/docker.ts
|
|
682
|
+
var exports_docker = {};
|
|
683
|
+
__export(exports_docker, {
|
|
684
|
+
DockerRunner: () => DockerRunner,
|
|
685
|
+
removeContainer: () => removeContainer,
|
|
686
|
+
sweepOrphanedContainers: () => sweepOrphanedContainers,
|
|
687
|
+
sweepOrphanedContainersOnce: () => sweepOrphanedContainersOnce
|
|
688
|
+
});
|
|
689
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
690
|
+
import * as fs6 from "node:fs";
|
|
691
|
+
import * as path5 from "node:path";
|
|
692
|
+
function removeContainer(name) {
|
|
693
|
+
try {
|
|
694
|
+
spawnSync("docker", ["rm", "-f", name], { timeout: 1e4, stdio: "ignore" });
|
|
695
|
+
} catch {}
|
|
696
|
+
}
|
|
697
|
+
function sweepOrphanedContainersOnce() {
|
|
698
|
+
if (swept)
|
|
699
|
+
return [];
|
|
700
|
+
swept = true;
|
|
701
|
+
return sweepOrphanedContainers();
|
|
702
|
+
}
|
|
703
|
+
function sweepOrphanedContainers() {
|
|
704
|
+
try {
|
|
705
|
+
const listed = spawnSync("docker", [
|
|
706
|
+
"ps",
|
|
707
|
+
"--all",
|
|
708
|
+
"--filter",
|
|
709
|
+
"status=created",
|
|
710
|
+
"--filter",
|
|
711
|
+
`name=${CONTAINER_NAME_PREFIX}`,
|
|
712
|
+
"--format",
|
|
713
|
+
"{{.Names}}"
|
|
714
|
+
], { timeout: 1e4, encoding: "utf8" });
|
|
715
|
+
const names = (listed.stdout ?? "").split(`
|
|
716
|
+
`).map((line) => line.trim()).filter((line) => line.startsWith(CONTAINER_NAME_PREFIX));
|
|
717
|
+
for (const name of names) {
|
|
718
|
+
removeContainer(name);
|
|
719
|
+
}
|
|
720
|
+
return names;
|
|
721
|
+
} catch {
|
|
722
|
+
return [];
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
class DockerRunner {
|
|
727
|
+
IMAGE_NAME = "polly-tla:latest";
|
|
728
|
+
DOCKERFILE_PATH = path5.join(__dirname, "../../Dockerfile");
|
|
729
|
+
async isDockerAvailable() {
|
|
730
|
+
try {
|
|
731
|
+
const result = await this.runCommand("docker", ["info"], {
|
|
732
|
+
timeout: 5000
|
|
733
|
+
});
|
|
734
|
+
return result.exitCode === 0;
|
|
735
|
+
} catch (error) {
|
|
736
|
+
if (error instanceof Error && error.message.includes("timed out")) {
|
|
737
|
+
throw error;
|
|
738
|
+
}
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async hasImage() {
|
|
743
|
+
try {
|
|
744
|
+
const result = await this.runCommand("docker", ["images", "-q", this.IMAGE_NAME], {
|
|
745
|
+
timeout: 1e4
|
|
746
|
+
});
|
|
747
|
+
return result.stdout.trim().length > 0;
|
|
748
|
+
} catch (error) {
|
|
749
|
+
if (error instanceof Error && error.message.includes("timed out")) {
|
|
750
|
+
throw error;
|
|
751
|
+
}
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
async buildImage(onProgress) {
|
|
756
|
+
const dockerfileDir = path5.dirname(this.DOCKERFILE_PATH);
|
|
757
|
+
await this.runCommandStreaming("docker", ["build", "-f", this.DOCKERFILE_PATH, "-t", this.IMAGE_NAME, dockerfileDir], onProgress, 300000);
|
|
758
|
+
}
|
|
759
|
+
async pullImage(onProgress) {
|
|
760
|
+
await this.buildImage(onProgress);
|
|
761
|
+
}
|
|
762
|
+
async runTLC(specPath, options) {
|
|
763
|
+
if (!fs6.existsSync(specPath)) {
|
|
764
|
+
throw new Error(`Spec file not found: ${specPath}`);
|
|
765
|
+
}
|
|
766
|
+
const specDir = path5.dirname(specPath);
|
|
767
|
+
const specName = path5.basename(specPath, ".tla");
|
|
768
|
+
const cfgPath = path5.join(specDir, `${specName}.cfg`);
|
|
769
|
+
if (!fs6.existsSync(cfgPath)) {
|
|
770
|
+
throw new Error(`Config file not found: ${cfgPath}`);
|
|
771
|
+
}
|
|
772
|
+
const statesDir = path5.join(specDir, "states");
|
|
773
|
+
if (fs6.existsSync(statesDir)) {
|
|
774
|
+
fs6.rmSync(statesDir, { recursive: true, force: true });
|
|
775
|
+
}
|
|
776
|
+
const containerName = mintContainerName("tlc");
|
|
777
|
+
sweepOrphanedContainersOnce();
|
|
778
|
+
const args = [
|
|
779
|
+
"run",
|
|
780
|
+
"--rm",
|
|
781
|
+
"--name",
|
|
782
|
+
containerName,
|
|
783
|
+
"-v",
|
|
784
|
+
`${specDir}:/work`,
|
|
785
|
+
this.IMAGE_NAME,
|
|
786
|
+
"tlc",
|
|
787
|
+
"-workers",
|
|
788
|
+
`${options?.workers || 1}`,
|
|
789
|
+
"-cleanup",
|
|
790
|
+
"-metadir",
|
|
791
|
+
"/tmp/states"
|
|
792
|
+
];
|
|
793
|
+
if (options?.maxDepth !== undefined && options.maxDepth > 0) {
|
|
794
|
+
args.push("-depth", `${options.maxDepth}`);
|
|
795
|
+
}
|
|
796
|
+
args.push(`${specName}.tla`);
|
|
797
|
+
const result = await this.runCommand("docker", args, {
|
|
798
|
+
timeout: options?.timeout,
|
|
799
|
+
containerName
|
|
800
|
+
});
|
|
801
|
+
return this.parseTLCOutput(result);
|
|
802
|
+
}
|
|
803
|
+
parseTLCOutput(result) {
|
|
804
|
+
const output = result.stdout + result.stderr;
|
|
805
|
+
const violationMatch = output.match(/Error: Invariant (.*?) is violated/);
|
|
806
|
+
if (violationMatch) {
|
|
807
|
+
return {
|
|
808
|
+
success: false,
|
|
809
|
+
violation: {
|
|
810
|
+
type: "invariant",
|
|
811
|
+
name: violationMatch[1],
|
|
812
|
+
trace: this.extractTrace(output)
|
|
813
|
+
},
|
|
814
|
+
output
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
if (result.exitCode !== 0 || output.includes("Error:")) {
|
|
818
|
+
return {
|
|
819
|
+
success: false,
|
|
820
|
+
error: this.extractError(output),
|
|
821
|
+
output
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
const statesMatch = output.match(/(\d+) states generated/);
|
|
825
|
+
const distinctMatch = output.match(/(\d+) distinct states/);
|
|
826
|
+
return {
|
|
827
|
+
success: true,
|
|
828
|
+
stats: {
|
|
829
|
+
statesGenerated: statesMatch?.[1] ? Number.parseInt(statesMatch[1], 10) : 0,
|
|
830
|
+
distinctStates: distinctMatch?.[1] ? Number.parseInt(distinctMatch[1], 10) : 0
|
|
831
|
+
},
|
|
832
|
+
output
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
extractTrace(output) {
|
|
836
|
+
const lines = output.split(`
|
|
837
|
+
`);
|
|
838
|
+
const trace = [];
|
|
839
|
+
let inTrace = false;
|
|
840
|
+
for (const line of lines) {
|
|
841
|
+
if (line.includes("State ") && line.includes(":")) {
|
|
842
|
+
inTrace = true;
|
|
843
|
+
trace.push(line);
|
|
844
|
+
} else if (inTrace) {
|
|
845
|
+
if (line.trim() === "" || line.startsWith("Error:")) {
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
trace.push(line);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
return trace;
|
|
852
|
+
}
|
|
853
|
+
extractError(output) {
|
|
854
|
+
if (/StatePool\w*\.run/.test(output) && /states\/.*No such file or directory/.test(output)) {
|
|
855
|
+
return "TLC state-pool IO failure (states/.../<n>: No such file or directory). This is a Docker filesystem flake, not a spec error — re-run the verification. See issue #152.";
|
|
856
|
+
}
|
|
857
|
+
const errorMatch = output.match(/Error: (.*?)(?:\n|$)/);
|
|
858
|
+
if (errorMatch?.[1]) {
|
|
859
|
+
return errorMatch[1];
|
|
860
|
+
}
|
|
861
|
+
if (output.includes("Parse Error")) {
|
|
862
|
+
return "TLA+ syntax error in specification";
|
|
863
|
+
}
|
|
864
|
+
if (output.includes("Semantic Error")) {
|
|
865
|
+
return "Semantic error in specification";
|
|
866
|
+
}
|
|
867
|
+
return "Unknown error occurred during model checking";
|
|
868
|
+
}
|
|
869
|
+
runCommand(command, args, options) {
|
|
870
|
+
return new Promise((resolve3, reject) => {
|
|
871
|
+
const proc = spawn(command, args);
|
|
872
|
+
let stdout = "";
|
|
873
|
+
let stderr = "";
|
|
874
|
+
proc.stdout.on("data", (data) => {
|
|
875
|
+
stdout += data.toString();
|
|
876
|
+
});
|
|
877
|
+
proc.stderr.on("data", (data) => {
|
|
878
|
+
stderr += data.toString();
|
|
879
|
+
});
|
|
880
|
+
const timeoutValue = options?.timeout ?? 0;
|
|
881
|
+
const timeout = timeoutValue > 0 ? setTimeout(() => {
|
|
882
|
+
proc.kill("SIGKILL");
|
|
883
|
+
if (options?.containerName) {
|
|
884
|
+
removeContainer(options.containerName);
|
|
885
|
+
}
|
|
886
|
+
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.`));
|
|
887
|
+
}, timeoutValue) : null;
|
|
888
|
+
proc.on("close", (exitCode) => {
|
|
889
|
+
if (timeout)
|
|
890
|
+
clearTimeout(timeout);
|
|
891
|
+
resolve3({
|
|
892
|
+
exitCode: exitCode || 0,
|
|
893
|
+
stdout,
|
|
894
|
+
stderr
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
proc.on("error", (error) => {
|
|
898
|
+
if (timeout)
|
|
899
|
+
clearTimeout(timeout);
|
|
900
|
+
reject(error);
|
|
901
|
+
});
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
runCommandStreaming(command, args, onOutput, timeout) {
|
|
905
|
+
return new Promise((resolve3, reject) => {
|
|
906
|
+
const proc = spawn(command, args);
|
|
907
|
+
const timeoutHandle = timeout && timeout > 0 ? setTimeout(() => {
|
|
908
|
+
proc.kill();
|
|
909
|
+
reject(new Error(`Command timed out after ${Math.floor(timeout / 1000)}s. Docker may be unresponsive.`));
|
|
910
|
+
}, timeout) : null;
|
|
911
|
+
proc.stdout.on("data", (data) => {
|
|
912
|
+
if (onOutput) {
|
|
913
|
+
const lines = data.toString().split(`
|
|
914
|
+
`);
|
|
915
|
+
for (const line of lines) {
|
|
916
|
+
if (line.trim()) {
|
|
917
|
+
onOutput(line.trim());
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
proc.stderr.on("data", (data) => {
|
|
923
|
+
if (onOutput) {
|
|
924
|
+
const lines = data.toString().split(`
|
|
925
|
+
`);
|
|
926
|
+
for (const line of lines) {
|
|
927
|
+
if (line.trim()) {
|
|
928
|
+
onOutput(line.trim());
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
});
|
|
933
|
+
proc.on("close", (exitCode) => {
|
|
934
|
+
if (timeoutHandle)
|
|
935
|
+
clearTimeout(timeoutHandle);
|
|
936
|
+
if (exitCode === 0) {
|
|
937
|
+
resolve3();
|
|
938
|
+
} else {
|
|
939
|
+
reject(new Error(`Command failed with exit code ${exitCode}`));
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
proc.on("error", (error) => {
|
|
943
|
+
if (timeoutHandle)
|
|
944
|
+
clearTimeout(timeoutHandle);
|
|
945
|
+
reject(error);
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
var __dirname = "/Users/AJT/projects/polly/packages/polly/tools/verify/src/runner", swept = false;
|
|
951
|
+
var init_docker = () => {};
|
|
952
|
+
|
|
669
953
|
// tools/visualize/src/cli.ts
|
|
670
|
-
import * as
|
|
671
|
-
import * as
|
|
954
|
+
import * as fs8 from "node:fs";
|
|
955
|
+
import * as path7 from "node:path";
|
|
672
956
|
|
|
673
957
|
// tools/analysis/src/extract/architecture.ts
|
|
674
958
|
import * as fs4 from "node:fs";
|
|
@@ -1326,7 +1610,8 @@ class FlowAnalyzer {
|
|
|
1326
1610
|
import {
|
|
1327
1611
|
Node as Node4,
|
|
1328
1612
|
Project as Project3,
|
|
1329
|
-
SyntaxKind
|
|
1613
|
+
SyntaxKind,
|
|
1614
|
+
VariableDeclarationKind
|
|
1330
1615
|
} from "ts-morph";
|
|
1331
1616
|
|
|
1332
1617
|
// tools/analysis/src/extract/relationships.ts
|
|
@@ -2712,16 +2997,116 @@ class HandlerExtractor {
|
|
|
2712
2997
|
return;
|
|
2713
2998
|
}
|
|
2714
2999
|
if (this.isTranslatableInitializer(initializer)) {
|
|
2715
|
-
|
|
3000
|
+
const captured = this.resolveSignalAliases(this.unwrapParentheses(initializer));
|
|
3001
|
+
assignments.push({ field, value: `EXPR:${captured}` });
|
|
2716
3002
|
return;
|
|
2717
3003
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
3004
|
+
const on = signalName ? ` on ${signalName}.value` : "";
|
|
3005
|
+
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+`);
|
|
3006
|
+
}
|
|
3007
|
+
resolveSignalAliases(initializer) {
|
|
3008
|
+
const aliases = this.collectSignalAliases(initializer);
|
|
3009
|
+
if (aliases.size === 0)
|
|
3010
|
+
return initializer.getText();
|
|
3011
|
+
const base = initializer.getStart();
|
|
3012
|
+
const edits = [];
|
|
3013
|
+
const visit = (node) => {
|
|
3014
|
+
if (!Node4.isPropertyAccessExpression(node))
|
|
3015
|
+
return;
|
|
3016
|
+
const target = node.getExpression();
|
|
3017
|
+
if (!Node4.isIdentifier(target))
|
|
3018
|
+
return;
|
|
3019
|
+
const signal = aliases.get(target.getText());
|
|
3020
|
+
if (!signal)
|
|
3021
|
+
return;
|
|
3022
|
+
edits.push({
|
|
3023
|
+
start: target.getStart() - base,
|
|
3024
|
+
end: target.getEnd() - base,
|
|
3025
|
+
text: `${signal}.value`
|
|
3026
|
+
});
|
|
3027
|
+
};
|
|
3028
|
+
visit(initializer);
|
|
3029
|
+
initializer.forEachDescendant(visit);
|
|
3030
|
+
if (edits.length === 0)
|
|
3031
|
+
return initializer.getText();
|
|
3032
|
+
let text = initializer.getText();
|
|
3033
|
+
for (const edit of edits.sort((a, b) => b.start - a.start)) {
|
|
3034
|
+
text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
|
|
3035
|
+
}
|
|
3036
|
+
return text;
|
|
3037
|
+
}
|
|
3038
|
+
collectSignalAliases(node) {
|
|
3039
|
+
const aliases = new Map;
|
|
3040
|
+
const fn = node.getFirstAncestor((a) => Node4.isArrowFunction(a) || Node4.isFunctionExpression(a) || Node4.isFunctionDeclaration(a));
|
|
3041
|
+
if (!fn)
|
|
3042
|
+
return aliases;
|
|
3043
|
+
const reassigned = this.collectReassignedIdentifiers(fn);
|
|
3044
|
+
fn.forEachDescendant((inner) => {
|
|
3045
|
+
if (!Node4.isVariableDeclaration(inner))
|
|
3046
|
+
return;
|
|
3047
|
+
const binding = this.readSignalAliasBinding(inner);
|
|
3048
|
+
if (!binding)
|
|
3049
|
+
return;
|
|
3050
|
+
if (aliases.has(binding.signal))
|
|
3051
|
+
return;
|
|
3052
|
+
if (reassigned.has(binding.alias))
|
|
3053
|
+
return;
|
|
3054
|
+
aliases.set(binding.alias, binding.signal);
|
|
3055
|
+
});
|
|
3056
|
+
return aliases;
|
|
3057
|
+
}
|
|
3058
|
+
collectReassignedIdentifiers(fn) {
|
|
3059
|
+
const reassigned = new Set;
|
|
3060
|
+
fn.forEachDescendant((inner) => {
|
|
3061
|
+
if (!Node4.isBinaryExpression(inner))
|
|
3062
|
+
return;
|
|
3063
|
+
if (inner.getOperatorToken().getText() !== "=")
|
|
3064
|
+
return;
|
|
3065
|
+
const left = inner.getLeft();
|
|
3066
|
+
if (Node4.isIdentifier(left))
|
|
3067
|
+
reassigned.add(left.getText());
|
|
3068
|
+
});
|
|
3069
|
+
return reassigned;
|
|
3070
|
+
}
|
|
3071
|
+
readSignalAliasBinding(decl) {
|
|
3072
|
+
const list = decl.getParent();
|
|
3073
|
+
if (!Node4.isVariableDeclarationList(list))
|
|
3074
|
+
return null;
|
|
3075
|
+
if (list.getDeclarationKind() !== VariableDeclarationKind.Const)
|
|
3076
|
+
return null;
|
|
3077
|
+
const nameNode = decl.getNameNode();
|
|
3078
|
+
if (!Node4.isIdentifier(nameNode))
|
|
3079
|
+
return null;
|
|
3080
|
+
const init = decl.getInitializer();
|
|
3081
|
+
if (!init || !Node4.isPropertyAccessExpression(init))
|
|
3082
|
+
return null;
|
|
3083
|
+
if (init.getName() !== "value")
|
|
3084
|
+
return null;
|
|
3085
|
+
const target = init.getExpression();
|
|
3086
|
+
if (!Node4.isIdentifier(target))
|
|
3087
|
+
return null;
|
|
3088
|
+
return { alias: nameNode.getText(), signal: target.getText() };
|
|
2722
3089
|
}
|
|
2723
3090
|
isTranslatableInitializer(node) {
|
|
2724
|
-
|
|
3091
|
+
const kindAllowed = Node4.isBinaryExpression(node) || Node4.isPropertyAccessExpression(node) || Node4.isElementAccessExpression(node) || Node4.isPrefixUnaryExpression(node) || Node4.isPostfixUnaryExpression(node) || Node4.isParenthesizedExpression(node) || Node4.isConditionalExpression(node);
|
|
3092
|
+
if (!kindAllowed)
|
|
3093
|
+
return false;
|
|
3094
|
+
if (this.containsComment(node))
|
|
3095
|
+
return false;
|
|
3096
|
+
if (node.getFirstDescendantByKind(SyntaxKind.CallExpression))
|
|
3097
|
+
return false;
|
|
3098
|
+
return this.unwrapParentheses(node).getFirstDescendantByKind(SyntaxKind.ConditionalExpression) === undefined;
|
|
3099
|
+
}
|
|
3100
|
+
unwrapParentheses(node) {
|
|
3101
|
+
let current = node;
|
|
3102
|
+
while (Node4.isParenthesizedExpression(current)) {
|
|
3103
|
+
current = current.getExpression();
|
|
3104
|
+
}
|
|
3105
|
+
return current;
|
|
3106
|
+
}
|
|
3107
|
+
containsComment(node) {
|
|
3108
|
+
const text = node.getText();
|
|
3109
|
+
return text.includes("//") || text.includes("/*");
|
|
2725
3110
|
}
|
|
2726
3111
|
extractPayloadPropertyParam(initializer) {
|
|
2727
3112
|
if (!Node4.isPropertyAccessExpression(initializer))
|
|
@@ -6486,24 +6871,25 @@ function generateStructurizrDSL(analysis, options) {
|
|
|
6486
6871
|
}
|
|
6487
6872
|
|
|
6488
6873
|
// tools/visualize/src/runner/export.ts
|
|
6489
|
-
|
|
6490
|
-
import
|
|
6491
|
-
import * as
|
|
6874
|
+
init_docker();
|
|
6875
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
6876
|
+
import * as fs7 from "node:fs";
|
|
6877
|
+
import * as path6 from "node:path";
|
|
6492
6878
|
|
|
6493
6879
|
class DiagramExporter {
|
|
6494
6880
|
static DOCKER_IMAGE = "structurizr/structurizr:latest";
|
|
6495
6881
|
static DEFAULT_TIMEOUT = 120000;
|
|
6496
6882
|
async export(options) {
|
|
6497
6883
|
const { dslPath, outputDir, timeout = DiagramExporter.DEFAULT_TIMEOUT } = options;
|
|
6498
|
-
if (!
|
|
6884
|
+
if (!fs7.existsSync(dslPath)) {
|
|
6499
6885
|
return {
|
|
6500
6886
|
success: false,
|
|
6501
6887
|
siteDir: "",
|
|
6502
6888
|
error: `DSL file not found: ${dslPath}`
|
|
6503
6889
|
};
|
|
6504
6890
|
}
|
|
6505
|
-
if (!
|
|
6506
|
-
|
|
6891
|
+
if (!fs7.existsSync(outputDir)) {
|
|
6892
|
+
fs7.mkdirSync(outputDir, { recursive: true });
|
|
6507
6893
|
}
|
|
6508
6894
|
const dockerAvailable = await this.isDockerAvailable();
|
|
6509
6895
|
if (!dockerAvailable) {
|
|
@@ -6536,13 +6922,16 @@ class DiagramExporter {
|
|
|
6536
6922
|
}
|
|
6537
6923
|
}
|
|
6538
6924
|
async exportStaticSite(dslPath, outputDir, timeout) {
|
|
6539
|
-
const dslDir =
|
|
6540
|
-
const dslFileName =
|
|
6925
|
+
const dslDir = path6.dirname(dslPath);
|
|
6926
|
+
const dslFileName = path6.basename(dslPath);
|
|
6541
6927
|
const workspaceMount = `${dslDir}:/usr/local/structurizr`;
|
|
6542
6928
|
const outputMount = `${outputDir}:/output`;
|
|
6929
|
+
const containerName = mintContainerName("structurizr");
|
|
6543
6930
|
const args = [
|
|
6544
6931
|
"run",
|
|
6545
6932
|
"--rm",
|
|
6933
|
+
"--name",
|
|
6934
|
+
containerName,
|
|
6546
6935
|
"-v",
|
|
6547
6936
|
workspaceMount,
|
|
6548
6937
|
"-v",
|
|
@@ -6556,7 +6945,7 @@ class DiagramExporter {
|
|
|
6556
6945
|
"-o",
|
|
6557
6946
|
"/output"
|
|
6558
6947
|
];
|
|
6559
|
-
await this.runDocker(args, timeout);
|
|
6948
|
+
await this.runDocker(args, timeout, containerName);
|
|
6560
6949
|
}
|
|
6561
6950
|
async isDockerAvailable() {
|
|
6562
6951
|
try {
|
|
@@ -6576,7 +6965,7 @@ class DiagramExporter {
|
|
|
6576
6965
|
}
|
|
6577
6966
|
async pullImage(onProgress) {
|
|
6578
6967
|
return new Promise((resolve3, reject) => {
|
|
6579
|
-
const proc =
|
|
6968
|
+
const proc = spawn2("docker", ["pull", DiagramExporter.DOCKER_IMAGE]);
|
|
6580
6969
|
proc.stdout.on("data", (data) => {
|
|
6581
6970
|
onProgress?.(data.toString().trim());
|
|
6582
6971
|
});
|
|
@@ -6595,16 +6984,19 @@ class DiagramExporter {
|
|
|
6595
6984
|
});
|
|
6596
6985
|
});
|
|
6597
6986
|
}
|
|
6598
|
-
async runDocker(args, timeout) {
|
|
6599
|
-
return this.runCommand("docker", args, timeout);
|
|
6987
|
+
async runDocker(args, timeout, containerName) {
|
|
6988
|
+
return this.runCommand("docker", args, timeout, containerName);
|
|
6600
6989
|
}
|
|
6601
|
-
async runCommand(command, args, timeout) {
|
|
6990
|
+
async runCommand(command, args, timeout, containerName) {
|
|
6602
6991
|
return new Promise((resolve3, reject) => {
|
|
6603
|
-
const proc =
|
|
6992
|
+
const proc = spawn2(command, args);
|
|
6604
6993
|
let stdout = "";
|
|
6605
6994
|
let stderr = "";
|
|
6606
6995
|
const timer = setTimeout(() => {
|
|
6607
|
-
proc.kill();
|
|
6996
|
+
proc.kill("SIGKILL");
|
|
6997
|
+
if (containerName) {
|
|
6998
|
+
removeContainer(containerName);
|
|
6999
|
+
}
|
|
6608
7000
|
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
6609
7001
|
}, timeout);
|
|
6610
7002
|
proc.stdout.on("data", (data) => {
|
|
@@ -6741,7 +7133,7 @@ function findAndDisplayProjectConfig() {
|
|
|
6741
7133
|
return { tsConfigPath, projectRoot };
|
|
6742
7134
|
}
|
|
6743
7135
|
function displayProjectType(projectRoot) {
|
|
6744
|
-
const hasManifest =
|
|
7136
|
+
const hasManifest = fs8.existsSync(path7.join(projectRoot, "manifest.json"));
|
|
6745
7137
|
const projectType = hasManifest ? "Chrome Extension" : "Detecting from project structure...";
|
|
6746
7138
|
console.log(color(` Type: ${projectType}`, COLORS.gray));
|
|
6747
7139
|
}
|
|
@@ -6786,16 +7178,16 @@ function generateAndWriteDSL(analysis, snapshot) {
|
|
|
6786
7178
|
componentDiagramContexts: contextTypes.length > 0 ? contextTypes : ["background"],
|
|
6787
7179
|
snapshot
|
|
6788
7180
|
});
|
|
6789
|
-
const outputDir =
|
|
6790
|
-
if (!
|
|
6791
|
-
|
|
7181
|
+
const outputDir = path7.join(process.cwd(), "docs");
|
|
7182
|
+
if (!fs8.existsSync(outputDir)) {
|
|
7183
|
+
fs8.mkdirSync(outputDir, { recursive: true });
|
|
6792
7184
|
}
|
|
6793
|
-
const dslPath =
|
|
6794
|
-
|
|
7185
|
+
const dslPath = path7.join(outputDir, "architecture.dsl");
|
|
7186
|
+
fs8.writeFileSync(dslPath, dsl, "utf-8");
|
|
6795
7187
|
return dslPath;
|
|
6796
7188
|
}
|
|
6797
7189
|
function displayNextSteps(dslPath) {
|
|
6798
|
-
const outputDir =
|
|
7190
|
+
const outputDir = path7.join(process.cwd(), "docs");
|
|
6799
7191
|
console.log(color(`✅ Architecture documentation generated!
|
|
6800
7192
|
`, COLORS.green));
|
|
6801
7193
|
console.log(` File: ${color(dslPath, COLORS.blue)}`);
|
|
@@ -6826,11 +7218,11 @@ async function exportCommand(_args) {
|
|
|
6826
7218
|
\uD83D\uDCE4 Generating static site...
|
|
6827
7219
|
`, COLORS.blue));
|
|
6828
7220
|
try {
|
|
6829
|
-
const dslPath =
|
|
6830
|
-
if (!
|
|
7221
|
+
const dslPath = path7.join(process.cwd(), "docs", "architecture.dsl");
|
|
7222
|
+
if (!fs8.existsSync(dslPath)) {
|
|
6831
7223
|
process.exit(1);
|
|
6832
7224
|
}
|
|
6833
|
-
const outputDir =
|
|
7225
|
+
const outputDir = path7.join(process.cwd(), "docs", "site");
|
|
6834
7226
|
console.log(color(` DSL: ${dslPath}`, COLORS.gray));
|
|
6835
7227
|
console.log(color(` Output: ${outputDir}`, COLORS.gray));
|
|
6836
7228
|
console.log();
|
|
@@ -6863,9 +7255,9 @@ async function serveCommand(args) {
|
|
|
6863
7255
|
\uD83C\uDF10 Starting static site server...
|
|
6864
7256
|
`, COLORS.blue));
|
|
6865
7257
|
try {
|
|
6866
|
-
const siteDir =
|
|
6867
|
-
const indexPath =
|
|
6868
|
-
if (!
|
|
7258
|
+
const siteDir = path7.join(process.cwd(), "docs", "site");
|
|
7259
|
+
const indexPath = path7.join(siteDir, "index.html");
|
|
7260
|
+
if (!fs8.existsSync(indexPath)) {
|
|
6869
7261
|
process.exit(1);
|
|
6870
7262
|
}
|
|
6871
7263
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
|
@@ -6881,8 +7273,8 @@ async function serveCommand(args) {
|
|
|
6881
7273
|
port,
|
|
6882
7274
|
fetch(req) {
|
|
6883
7275
|
const url = new URL(req.url);
|
|
6884
|
-
const filePath =
|
|
6885
|
-
if (
|
|
7276
|
+
const filePath = path7.join(siteDir, url.pathname === "/" ? "index.html" : url.pathname);
|
|
7277
|
+
if (fs8.existsSync(filePath) && fs8.statSync(filePath).isFile()) {
|
|
6886
7278
|
const file = BunGlobal.file(filePath);
|
|
6887
7279
|
return new Response(file);
|
|
6888
7280
|
}
|
|
@@ -6966,20 +7358,20 @@ ${color("Learn More:", COLORS.blue)}
|
|
|
6966
7358
|
}
|
|
6967
7359
|
function findTsConfig() {
|
|
6968
7360
|
const locations = [
|
|
6969
|
-
|
|
6970
|
-
|
|
7361
|
+
path7.join(process.cwd(), "tsconfig.json"),
|
|
7362
|
+
path7.join(process.cwd(), "..", "tsconfig.json")
|
|
6971
7363
|
];
|
|
6972
7364
|
for (const loc of locations) {
|
|
6973
|
-
if (
|
|
7365
|
+
if (fs8.existsSync(loc)) {
|
|
6974
7366
|
return loc;
|
|
6975
7367
|
}
|
|
6976
7368
|
}
|
|
6977
7369
|
return null;
|
|
6978
7370
|
}
|
|
6979
7371
|
function findProjectRoot() {
|
|
6980
|
-
const locations = [process.cwd(),
|
|
7372
|
+
const locations = [process.cwd(), path7.join(process.cwd(), "..")];
|
|
6981
7373
|
for (const loc of locations) {
|
|
6982
|
-
if (
|
|
7374
|
+
if (fs8.existsSync(path7.join(loc, "manifest.json")) || fs8.existsSync(path7.join(loc, "package.json")) || fs8.existsSync(path7.join(loc, "tsconfig.json"))) {
|
|
6983
7375
|
return loc;
|
|
6984
7376
|
}
|
|
6985
7377
|
}
|
|
@@ -6989,4 +7381,4 @@ main().catch((_error) => {
|
|
|
6989
7381
|
process.exit(1);
|
|
6990
7382
|
});
|
|
6991
7383
|
|
|
6992
|
-
//# debugId=
|
|
7384
|
+
//# debugId=B0F08E74B7BC706B64756E2164756E21
|