@fairfox/polly 0.73.0 → 0.74.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.
@@ -667,7 +667,7 @@ function detectProjectConfig(projectRoot) {
667
667
  var init_project_detector = () => {};
668
668
 
669
669
  // tools/visualize/src/cli.ts
670
- import * as fs6 from "node:fs";
670
+ import * as fs7 from "node:fs";
671
671
  import * as path6 from "node:path";
672
672
 
673
673
  // tools/analysis/src/extract/architecture.ts
@@ -1864,14 +1864,37 @@ class HandlerExtractor {
1864
1864
  return null;
1865
1865
  const key = keyArg.getLiteralValue();
1866
1866
  const variableName = this.getVariableNameFromParent(node) || key;
1867
+ const access = this.extractMeshAccess(args[2]);
1867
1868
  return {
1868
1869
  kind,
1869
1870
  key,
1870
1871
  variableName,
1871
1872
  filePath,
1872
- line: node.getStartLineNumber()
1873
+ line: node.getStartLineNumber(),
1874
+ ...access ? { access } : {}
1873
1875
  };
1874
1876
  }
1877
+ extractMeshAccess(optionsArg) {
1878
+ if (!optionsArg || !Node4.isObjectLiteralExpression(optionsArg))
1879
+ return;
1880
+ const accessProp = optionsArg.getProperty("access");
1881
+ if (!accessProp || !Node4.isPropertyAssignment(accessProp))
1882
+ return;
1883
+ const accessObj = accessProp.getInitializer();
1884
+ if (!accessObj || !Node4.isObjectLiteralExpression(accessObj))
1885
+ return;
1886
+ const predicateText = (name) => {
1887
+ const prop = accessObj.getProperty(name);
1888
+ if (!prop || !Node4.isPropertyAssignment(prop))
1889
+ return;
1890
+ return prop.getInitializer()?.getText().replace(/\s+/g, " ").trim();
1891
+ };
1892
+ const read = predicateText("read");
1893
+ const write = predicateText("write");
1894
+ if (read === undefined && write === undefined)
1895
+ return;
1896
+ return { read: read ?? "unset", write: write ?? "unset" };
1897
+ }
1875
1898
  analyzeFileAndImports(sourceFile, handlers, messageTypes, invalidMessageTypes, stateConstraints, globalStateConstraints, verifiedStates, resources) {
1876
1899
  const filePath = sourceFile.getFilePath();
1877
1900
  if (this.analyzedFiles.has(filePath)) {
@@ -4567,6 +4590,122 @@ async function analyzeArchitecture(options) {
4567
4590
  return analyzer.analyze();
4568
4591
  }
4569
4592
 
4593
+ // src/shared/lib/derive-document-id.ts
4594
+ import {
4595
+ interpretAsDocumentId
4596
+ } from "@automerge/automerge-repo/slim";
4597
+ import nacl from "tweetnacl";
4598
+ var DOC_ID_DOMAIN = "polly/meshState/v1";
4599
+ var keyEncoder = new TextEncoder;
4600
+ function deriveDocumentId(key) {
4601
+ const digest = nacl.hash(keyEncoder.encode(`${DOC_ID_DOMAIN}:${key}`));
4602
+ const bytes = digest.slice(0, 16);
4603
+ return interpretAsDocumentId(bytes);
4604
+ }
4605
+
4606
+ // tools/visualize/src/mesh-snapshot.ts
4607
+ import * as fs5 from "node:fs";
4608
+
4609
+ class MeshSnapshotError extends Error {
4610
+ constructor(message) {
4611
+ super(message);
4612
+ this.name = "MeshSnapshotError";
4613
+ }
4614
+ }
4615
+ function isObject(value) {
4616
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4617
+ }
4618
+ function requireStringArray(value, path5) {
4619
+ if (!Array.isArray(value) || value.some((v) => typeof v !== "string")) {
4620
+ throw new MeshSnapshotError(`${path5} must be an array of strings`);
4621
+ }
4622
+ return value;
4623
+ }
4624
+ function validateHandle(value, path5) {
4625
+ if (!isObject(value)) {
4626
+ throw new MeshSnapshotError(`${path5} must be an object`);
4627
+ }
4628
+ if (typeof value["docSynchronizerExists"] !== "boolean") {
4629
+ throw new MeshSnapshotError(`${path5}.docSynchronizerExists must be a boolean`);
4630
+ }
4631
+ const knowsPeer = value["docSynchronizerKnowsPeer"];
4632
+ if (knowsPeer !== undefined && typeof knowsPeer !== "boolean") {
4633
+ throw new MeshSnapshotError(`${path5}.docSynchronizerKnowsPeer must be a boolean or absent`);
4634
+ }
4635
+ const status = value["peerDocumentStatus"];
4636
+ if (status !== undefined && typeof status !== "string") {
4637
+ throw new MeshSnapshotError(`${path5}.peerDocumentStatus must be a string or absent`);
4638
+ }
4639
+ return value;
4640
+ }
4641
+ function validatePeer(value, path5) {
4642
+ if (!isObject(value)) {
4643
+ throw new MeshSnapshotError(`${path5} must be an object`);
4644
+ }
4645
+ if (typeof value["peerId"] !== "string") {
4646
+ throw new MeshSnapshotError(`${path5}.peerId must be a string`);
4647
+ }
4648
+ const slot = value["slot"];
4649
+ if (slot !== undefined && slot !== null) {
4650
+ if (!isObject(slot)) {
4651
+ throw new MeshSnapshotError(`${path5}.slot must be an object or absent`);
4652
+ }
4653
+ const handles = slot["handles"];
4654
+ if (!isObject(handles)) {
4655
+ throw new MeshSnapshotError(`${path5}.slot.handles must be an object`);
4656
+ }
4657
+ for (const [docId, handle] of Object.entries(handles)) {
4658
+ validateHandle(handle, `${path5}.slot.handles[${docId}]`);
4659
+ }
4660
+ }
4661
+ return value;
4662
+ }
4663
+ function validateMeshSnapshot(data) {
4664
+ if (!isObject(data)) {
4665
+ throw new MeshSnapshotError("snapshot must be a JSON object");
4666
+ }
4667
+ if (typeof data["localPeerId"] !== "string") {
4668
+ throw new MeshSnapshotError("snapshot.localPeerId must be a string");
4669
+ }
4670
+ requireStringArray(data["knownPeerIds"], "snapshot.knownPeerIds");
4671
+ requireStringArray(data["presentPeerIds"], "snapshot.presentPeerIds");
4672
+ const peers = data["peers"];
4673
+ if (!Array.isArray(peers)) {
4674
+ throw new MeshSnapshotError("snapshot.peers must be an array");
4675
+ }
4676
+ peers.forEach((peer, i) => {
4677
+ validatePeer(peer, `snapshot.peers[${i}]`);
4678
+ });
4679
+ return data;
4680
+ }
4681
+ function loadMeshSnapshot(filePath) {
4682
+ let raw;
4683
+ try {
4684
+ raw = fs5.readFileSync(filePath, "utf-8");
4685
+ } catch (error) {
4686
+ const reason = error instanceof Error ? error.message : String(error);
4687
+ throw new MeshSnapshotError(`cannot read snapshot file '${filePath}': ${reason}`);
4688
+ }
4689
+ let parsed;
4690
+ try {
4691
+ parsed = JSON.parse(raw);
4692
+ } catch (error) {
4693
+ const reason = error instanceof Error ? error.message : String(error);
4694
+ throw new MeshSnapshotError(`snapshot file '${filePath}' is not valid JSON: ${reason}`);
4695
+ }
4696
+ return validateMeshSnapshot(parsed);
4697
+ }
4698
+ function collectSnapshotPeerIds(snapshot) {
4699
+ const ids = new Set([snapshot.localPeerId]);
4700
+ for (const id of snapshot.knownPeerIds)
4701
+ ids.add(id);
4702
+ for (const id of snapshot.presentPeerIds)
4703
+ ids.add(id);
4704
+ for (const peer of snapshot.peers)
4705
+ ids.add(peer.peerId);
4706
+ return [...ids];
4707
+ }
4708
+
4570
4709
  // tools/visualize/src/types/structurizr.ts
4571
4710
  var DEFAULT_COLORS = {
4572
4711
  messageHandler: "#1168bd",
@@ -4639,6 +4778,11 @@ var DEFAULT_ELEMENT_STYLES = {
4639
4778
  shape: "Cylinder",
4640
4779
  background: DEFAULT_COLORS.queryHandler,
4641
4780
  color: DEFAULT_COLORS.textDark
4781
+ },
4782
+ "Mesh Transport": {
4783
+ shape: "Pipe",
4784
+ background: DEFAULT_COLORS.service,
4785
+ color: DEFAULT_COLORS.textLight
4642
4786
  }
4643
4787
  };
4644
4788
  var DEFAULT_RELATIONSHIP_STYLES = {
@@ -4662,12 +4806,83 @@ var DEFAULT_RELATIONSHIP_STYLES = {
4662
4806
  color: DEFAULT_COLORS.database
4663
4807
  }
4664
4808
  };
4809
+ var MESH_OVERLAY_ELEMENT_STYLES = {
4810
+ "Mesh Peer": {
4811
+ shape: "Person",
4812
+ background: "#495057",
4813
+ color: DEFAULT_COLORS.textLight
4814
+ },
4815
+ "Local Mesh Peer": {
4816
+ shape: "Person",
4817
+ background: DEFAULT_COLORS.messageHandler,
4818
+ color: DEFAULT_COLORS.textLight
4819
+ },
4820
+ "Snapshot Document": {
4821
+ shape: "Cylinder",
4822
+ background: "#adb5bd",
4823
+ color: DEFAULT_COLORS.textDark,
4824
+ border: "Dashed"
4825
+ }
4826
+ };
4827
+ var MESH_OVERLAY_RELATIONSHIP_STYLES = {
4828
+ "sync:has": {
4829
+ color: "#2f9e44",
4830
+ style: "Solid",
4831
+ thickness: 3
4832
+ },
4833
+ "sync:wants": {
4834
+ color: "#f08c00",
4835
+ style: "Dashed",
4836
+ thickness: 2
4837
+ },
4838
+ "sync:unavailable": {
4839
+ color: "#e03131",
4840
+ style: "Dashed",
4841
+ thickness: 2
4842
+ },
4843
+ "sync:unknown": {
4844
+ color: "#868e96",
4845
+ style: "Dotted",
4846
+ thickness: 2
4847
+ }
4848
+ };
4665
4849
  var DEFAULT_THEME = "https://static.structurizr.com/themes/default/theme.json";
4666
4850
 
4667
4851
  // tools/visualize/src/codegen/structurizr.ts
4852
+ var MESH_TRANSPORT_NODES = [
4853
+ {
4854
+ id: "mesh_net_adapter",
4855
+ name: "MeshNetworkAdapter",
4856
+ description: "Signs and encrypts every mesh operation"
4857
+ },
4858
+ {
4859
+ id: "mesh_webrtc_adapter",
4860
+ name: "MeshWebRTCAdapter",
4861
+ description: "Peer-to-peer WebRTC data channels"
4862
+ },
4863
+ {
4864
+ id: "mesh_signaling_client",
4865
+ name: "MeshSignalingClient",
4866
+ description: "WebRTC offer/answer signalling"
4867
+ },
4868
+ {
4869
+ id: "mesh_signaling_endpoint",
4870
+ name: "Signalling endpoint",
4871
+ description: "The rendezvous server peers exchange WebRTC offers through"
4872
+ }
4873
+ ];
4874
+ function syncStatusOf(handle) {
4875
+ const status = handle.peerDocumentStatus;
4876
+ if (status === "has" || status === "wants" || status === "unavailable") {
4877
+ return status;
4878
+ }
4879
+ return "unknown";
4880
+ }
4881
+
4668
4882
  class StructurizrDSLGenerator {
4669
4883
  analysis;
4670
4884
  options;
4885
+ overlayPlanCache;
4671
4886
  constructor(analysis, options = {}) {
4672
4887
  this.analysis = analysis;
4673
4888
  this.options = {
@@ -4686,10 +4901,12 @@ class StructurizrDSLGenerator {
4686
4901
  theme: options.styles?.theme || DEFAULT_THEME,
4687
4902
  elements: {
4688
4903
  ...DEFAULT_ELEMENT_STYLES,
4904
+ ...options.snapshot ? MESH_OVERLAY_ELEMENT_STYLES : {},
4689
4905
  ...options.styles?.elements
4690
4906
  },
4691
4907
  relationships: {
4692
4908
  ...DEFAULT_RELATIONSHIP_STYLES,
4909
+ ...options.snapshot ? MESH_OVERLAY_RELATIONSHIP_STYLES : {},
4693
4910
  ...options.styles?.relationships
4694
4911
  }
4695
4912
  }
@@ -4723,6 +4940,9 @@ class StructurizrDSLGenerator {
4723
4940
  const parts = [];
4724
4941
  parts.push(" model {");
4725
4942
  parts.push(this.generatePeople());
4943
+ const meshPeers = this.generateMeshPeers();
4944
+ if (meshPeers)
4945
+ parts.push(meshPeers);
4726
4946
  parts.push(this.generateExternalSystems());
4727
4947
  parts.push(this.generateMainSystem());
4728
4948
  if (this.options.deploymentNodes && this.options.deploymentNodes.length > 0) {
@@ -4763,6 +4983,12 @@ class StructurizrDSLGenerator {
4763
4983
  const meshDocs = this.generateMeshDocuments();
4764
4984
  if (meshDocs)
4765
4985
  parts.push(meshDocs);
4986
+ const snapshotDocs = this.generateSnapshotOnlyDocuments();
4987
+ if (snapshotDocs)
4988
+ parts.push(snapshotDocs);
4989
+ const meshTransport = this.generateMeshTransport();
4990
+ if (meshTransport)
4991
+ parts.push(meshTransport);
4766
4992
  parts.push(this.generateContainerRelationships());
4767
4993
  parts.push(" }");
4768
4994
  return parts.join(`
@@ -4794,7 +5020,8 @@ class StructurizrDSLGenerator {
4794
5020
  continue;
4795
5021
  seen.add(sig.key);
4796
5022
  const kindLabel = sig.kind === "mesh" ? "Mesh Document" : "Peer Document";
4797
- const description = `${kindLabel} — deriveDocumentId('${sig.key}')`;
5023
+ const access = sig.access ? ` · access read=${this.clip(sig.access.read)} write=${this.clip(sig.access.write)}` : "";
5024
+ const description = `${kindLabel} — deriveDocumentId('${sig.key}')${access}`;
4798
5025
  parts.push(` ${this.meshDocId(sig.key)} = container "${this.escape(sig.key)}" "${this.escape(description)}" "$${sig.kind}State" {`);
4799
5026
  parts.push(` tags "${kindLabel}"`);
4800
5027
  parts.push(" }");
@@ -4805,6 +5032,9 @@ class StructurizrDSLGenerator {
4805
5032
  meshDocId(key) {
4806
5033
  return `mesh_doc_${this.toId(key)}`;
4807
5034
  }
5035
+ clip(text, max = 32) {
5036
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
5037
+ }
4808
5038
  contextForFilePath(filePath) {
4809
5039
  const contextKeys = Object.keys(this.analysis.contexts);
4810
5040
  if (contextKeys.length === 1)
@@ -5143,6 +5373,8 @@ class StructurizrDSLGenerator {
5143
5373
  parts.push(...this.generateMessageFlowRelationships());
5144
5374
  parts.push(...this.generateExternalAPIRelationships());
5145
5375
  parts.push(...this.generateMeshRelationships());
5376
+ parts.push(...this.generateMeshTransportRelationships());
5377
+ parts.push(...this.generateMeshOverlayRelationships());
5146
5378
  return parts.join(`
5147
5379
  `);
5148
5380
  }
@@ -5162,6 +5394,177 @@ class StructurizrDSLGenerator {
5162
5394
  }
5163
5395
  return parts;
5164
5396
  }
5397
+ meshSignalKinds() {
5398
+ const signals = this.analysis.meshOrPeerSignals ?? [];
5399
+ return {
5400
+ mesh: signals.some((s) => s.kind === "mesh"),
5401
+ peer: signals.some((s) => s.kind === "peer")
5402
+ };
5403
+ }
5404
+ generateMeshTransport() {
5405
+ const { mesh, peer } = this.meshSignalKinds();
5406
+ if (!mesh && !peer)
5407
+ return "";
5408
+ const parts = [];
5409
+ if (mesh) {
5410
+ for (const node of MESH_TRANSPORT_NODES) {
5411
+ parts.push(` ${node.id} = container "${node.name}" "${this.escape(node.description)}" "Mesh Transport" {`);
5412
+ parts.push(' tags "Mesh Transport"');
5413
+ parts.push(" }");
5414
+ }
5415
+ }
5416
+ if (peer) {
5417
+ parts.push(` peer_relay = container "Peer relay" "${this.escape("The always-on relay $peerState syncs through")}" "Peer Transport" {`);
5418
+ parts.push(' tags "Mesh Transport"');
5419
+ parts.push(" }");
5420
+ }
5421
+ return parts.join(`
5422
+ `);
5423
+ }
5424
+ generateMeshTransportRelationships() {
5425
+ const { mesh, peer } = this.meshSignalKinds();
5426
+ const parts = [];
5427
+ if (mesh) {
5428
+ parts.push(' extension.mesh_net_adapter -> extension.mesh_webrtc_adapter "wraps"');
5429
+ parts.push(' extension.mesh_webrtc_adapter -> extension.mesh_signaling_client "negotiates via"');
5430
+ parts.push(' extension.mesh_signaling_client -> extension.mesh_signaling_endpoint "connects to"');
5431
+ }
5432
+ if (!mesh && !peer)
5433
+ return parts;
5434
+ const seen = new Set;
5435
+ for (const sig of this.analysis.meshOrPeerSignals ?? []) {
5436
+ if (seen.has(sig.key))
5437
+ continue;
5438
+ seen.add(sig.key);
5439
+ const target = sig.kind === "mesh" ? "mesh_net_adapter" : "peer_relay";
5440
+ parts.push(` extension.${this.meshDocId(sig.key)} -> extension.${target} "syncs through"`);
5441
+ }
5442
+ return parts;
5443
+ }
5444
+ getOverlayPlan() {
5445
+ const snapshot = this.options.snapshot;
5446
+ if (!snapshot)
5447
+ return;
5448
+ if (!this.overlayPlanCache) {
5449
+ this.overlayPlanCache = this.buildOverlayPlan(snapshot);
5450
+ }
5451
+ return this.overlayPlanCache;
5452
+ }
5453
+ buildOverlayPlan(snapshot) {
5454
+ const docIdToNode = this.buildDocIdNodeMap();
5455
+ const { peers, peerDslById } = this.buildOverlayPeers(snapshot);
5456
+ const { edges, snapshotOnlyDocs } = this.buildOverlayEdges(snapshot, docIdToNode, peerDslById);
5457
+ return { peers, edges, snapshotOnlyDocs };
5458
+ }
5459
+ buildDocIdNodeMap() {
5460
+ const docIdToNode = new Map;
5461
+ const seenKeys = new Set;
5462
+ for (const sig of this.analysis.meshOrPeerSignals ?? []) {
5463
+ if (seenKeys.has(sig.key))
5464
+ continue;
5465
+ seenKeys.add(sig.key);
5466
+ docIdToNode.set(String(deriveDocumentId(sig.key)), this.meshDocId(sig.key));
5467
+ }
5468
+ return docIdToNode;
5469
+ }
5470
+ buildOverlayPeers(snapshot) {
5471
+ const peers = [];
5472
+ const peerDslById = new Map;
5473
+ const usedPeerIds = new Set;
5474
+ for (const peerId of collectSnapshotPeerIds(snapshot)) {
5475
+ const base = `peer_${this.toId(peerId)}`;
5476
+ let id = base;
5477
+ let suffix = 2;
5478
+ while (usedPeerIds.has(id))
5479
+ id = `${base}_${suffix++}`;
5480
+ usedPeerIds.add(id);
5481
+ peerDslById.set(peerId, id);
5482
+ peers.push({ id, peerId, isLocal: peerId === snapshot.localPeerId });
5483
+ }
5484
+ return { peers, peerDslById };
5485
+ }
5486
+ buildOverlayEdges(snapshot, docIdToNode, peerDslById) {
5487
+ const edges = [];
5488
+ const snapshotOnlyDocs = [];
5489
+ const snapshotOnlyById = new Map;
5490
+ for (const peer of snapshot.peers) {
5491
+ const handles = peer.slot?.handles;
5492
+ const fromId = peerDslById.get(peer.peerId);
5493
+ if (!handles || !fromId)
5494
+ continue;
5495
+ for (const [docId, handle] of Object.entries(handles)) {
5496
+ const nodeId = this.resolveOverlayDocNode(docId, docIdToNode, snapshotOnlyById, snapshotOnlyDocs);
5497
+ const status = syncStatusOf(handle);
5498
+ edges.push({
5499
+ fromId,
5500
+ toRef: `extension.${nodeId}`,
5501
+ status,
5502
+ label: this.overlayEdgeLabel(status, handle)
5503
+ });
5504
+ }
5505
+ }
5506
+ return { edges, snapshotOnlyDocs };
5507
+ }
5508
+ resolveOverlayDocNode(docId, docIdToNode, snapshotOnlyById, snapshotOnlyDocs) {
5509
+ const staticNode = docIdToNode.get(docId);
5510
+ if (staticNode)
5511
+ return staticNode;
5512
+ const existing = snapshotOnlyById.get(docId);
5513
+ if (existing)
5514
+ return existing;
5515
+ const nodeId = `mesh_doc_${this.toId(docId)}`;
5516
+ snapshotOnlyById.set(docId, nodeId);
5517
+ snapshotOnlyDocs.push({ id: nodeId, docId });
5518
+ return nodeId;
5519
+ }
5520
+ overlayEdgeLabel(status, handle) {
5521
+ if (!handle.docSynchronizerExists)
5522
+ return `${status} · no synchronizer`;
5523
+ if (handle.docSynchronizerKnowsPeer === false)
5524
+ return `${status} · peer not added`;
5525
+ return status;
5526
+ }
5527
+ generateMeshPeers() {
5528
+ const plan = this.getOverlayPlan();
5529
+ if (!plan || plan.peers.length === 0)
5530
+ return "";
5531
+ const parts = [];
5532
+ for (const peer of plan.peers) {
5533
+ const tag = peer.isLocal ? "Local Mesh Peer" : "Mesh Peer";
5534
+ const description = peer.isLocal ? "Local mesh peer (this node)" : "Runtime mesh peer";
5535
+ parts.push(` ${peer.id} = person "${this.escape(peer.peerId)}" "${description}" {`);
5536
+ parts.push(` tags "${tag}"`);
5537
+ parts.push(" }");
5538
+ }
5539
+ return parts.join(`
5540
+ `);
5541
+ }
5542
+ generateSnapshotOnlyDocuments() {
5543
+ const plan = this.getOverlayPlan();
5544
+ if (!plan || plan.snapshotOnlyDocs.length === 0)
5545
+ return "";
5546
+ const parts = [];
5547
+ for (const doc of plan.snapshotOnlyDocs) {
5548
+ const description = `Mesh document seen only in the runtime snapshot — docId ${doc.docId}`;
5549
+ parts.push(` ${doc.id} = container "${this.escape(doc.docId)}" "${this.escape(description)}" "snapshot" {`);
5550
+ parts.push(' tags "Snapshot Document"');
5551
+ parts.push(" }");
5552
+ }
5553
+ return parts.join(`
5554
+ `);
5555
+ }
5556
+ generateMeshOverlayRelationships() {
5557
+ const plan = this.getOverlayPlan();
5558
+ if (!plan)
5559
+ return [];
5560
+ const parts = [];
5561
+ for (const edge of plan.edges) {
5562
+ parts.push(` ${edge.fromId} -> ${edge.toRef} "${this.escape(edge.label)}" {`);
5563
+ parts.push(` tags "sync:${edge.status}"`);
5564
+ parts.push(" }");
5565
+ }
5566
+ return parts;
5567
+ }
5165
5568
  generateUserRelationships() {
5166
5569
  const parts = [];
5167
5570
  const uiContexts = ["popup", "options", "devtools"];
@@ -5909,7 +6312,7 @@ function generateStructurizrDSL(analysis, options) {
5909
6312
 
5910
6313
  // tools/visualize/src/runner/export.ts
5911
6314
  import { spawn } from "node:child_process";
5912
- import * as fs5 from "node:fs";
6315
+ import * as fs6 from "node:fs";
5913
6316
  import * as path5 from "node:path";
5914
6317
 
5915
6318
  class DiagramExporter {
@@ -5917,15 +6320,15 @@ class DiagramExporter {
5917
6320
  static DEFAULT_TIMEOUT = 120000;
5918
6321
  async export(options) {
5919
6322
  const { dslPath, outputDir, timeout = DiagramExporter.DEFAULT_TIMEOUT } = options;
5920
- if (!fs5.existsSync(dslPath)) {
6323
+ if (!fs6.existsSync(dslPath)) {
5921
6324
  return {
5922
6325
  success: false,
5923
6326
  siteDir: "",
5924
6327
  error: `DSL file not found: ${dslPath}`
5925
6328
  };
5926
6329
  }
5927
- if (!fs5.existsSync(outputDir)) {
5928
- fs5.mkdirSync(outputDir, { recursive: true });
6330
+ if (!fs6.existsSync(outputDir)) {
6331
+ fs6.mkdirSync(outputDir, { recursive: true });
5929
6332
  }
5930
6333
  const dockerAvailable = await this.isDockerAvailable();
5931
6334
  if (!dockerAvailable) {
@@ -6073,7 +6476,7 @@ async function main() {
6073
6476
  switch (command) {
6074
6477
  case "--generate":
6075
6478
  case "generate":
6076
- await generateCommand();
6479
+ await generateCommand(args.slice(1));
6077
6480
  break;
6078
6481
  case "--export":
6079
6482
  case "export":
@@ -6090,22 +6493,64 @@ async function main() {
6090
6493
  showHelp();
6091
6494
  break;
6092
6495
  default:
6093
- await generateCommand();
6496
+ await generateCommand(args);
6094
6497
  }
6095
6498
  }
6096
- async function generateCommand() {
6499
+ async function generateCommand(args) {
6097
6500
  console.log(color(`
6098
6501
  \uD83D\uDCCA Analyzing architecture...
6099
6502
  `, COLORS.blue));
6100
6503
  try {
6504
+ const { snapshotPath } = parseGenerateArgs(args);
6505
+ const snapshot = snapshotPath ? loadAndReportSnapshot(snapshotPath) : undefined;
6101
6506
  const { tsConfigPath, projectRoot } = findAndDisplayProjectConfig();
6102
6507
  const analysis = await analyzeAndDisplayResults(tsConfigPath, projectRoot);
6103
- const dslPath = generateAndWriteDSL(analysis);
6508
+ const dslPath = generateAndWriteDSL(analysis, snapshot);
6104
6509
  displayNextSteps(dslPath);
6105
6510
  } catch (_error) {
6106
6511
  process.exit(1);
6107
6512
  }
6108
6513
  }
6514
+ function parseGenerateArgs(args) {
6515
+ const result = {};
6516
+ for (let i = 0;i < args.length; i++) {
6517
+ const arg = args[i];
6518
+ if (arg === "--snapshot") {
6519
+ const next = args[i + 1];
6520
+ if (!next || next.startsWith("--")) {
6521
+ console.log(color(`
6522
+ ✗ --snapshot requires a file path
6523
+ `, COLORS.red));
6524
+ process.exit(1);
6525
+ }
6526
+ result.snapshotPath = next;
6527
+ i++;
6528
+ } else if (arg?.startsWith("--snapshot=")) {
6529
+ result.snapshotPath = arg.slice("--snapshot=".length);
6530
+ }
6531
+ }
6532
+ return result;
6533
+ }
6534
+ function loadAndReportSnapshot(snapshotPath) {
6535
+ let snapshot;
6536
+ try {
6537
+ snapshot = loadMeshSnapshot(snapshotPath);
6538
+ } catch (error) {
6539
+ const message = error instanceof MeshSnapshotError ? error.message : String(error);
6540
+ console.log(color(`
6541
+ ✗ Snapshot error: ${message}
6542
+ `, COLORS.red));
6543
+ process.exit(1);
6544
+ }
6545
+ const isEmpty = snapshot.knownPeerIds.length === 0 && snapshot.presentPeerIds.length === 0 && snapshot.peers.length === 0;
6546
+ if (isEmpty) {
6547
+ console.log(color("⚠ Snapshot has no peers — generating the static diagram only.", COLORS.yellow));
6548
+ return;
6549
+ }
6550
+ const peerCount = collectSnapshotPeerIds(snapshot).length;
6551
+ console.log(color(`✓ Loaded runtime snapshot: ${peerCount} peer(s)`, COLORS.green));
6552
+ return snapshot;
6553
+ }
6109
6554
  function findAndDisplayProjectConfig() {
6110
6555
  const tsConfigPath = findTsConfig();
6111
6556
  if (!tsConfigPath) {
@@ -6121,7 +6566,7 @@ function findAndDisplayProjectConfig() {
6121
6566
  return { tsConfigPath, projectRoot };
6122
6567
  }
6123
6568
  function displayProjectType(projectRoot) {
6124
- const hasManifest = fs6.existsSync(path6.join(projectRoot, "manifest.json"));
6569
+ const hasManifest = fs7.existsSync(path6.join(projectRoot, "manifest.json"));
6125
6570
  const projectType = hasManifest ? "Chrome Extension" : "Detecting from project structure...";
6126
6571
  console.log(color(` Type: ${projectType}`, COLORS.gray));
6127
6572
  }
@@ -6155,7 +6600,7 @@ function displayArchitectureSummary(analysis) {
6155
6600
  }
6156
6601
  }
6157
6602
  }
6158
- function generateAndWriteDSL(analysis) {
6603
+ function generateAndWriteDSL(analysis, snapshot) {
6159
6604
  console.log(color(`
6160
6605
  \uD83D\uDCDD Generating Structurizr DSL...
6161
6606
  `, COLORS.blue));
@@ -6163,14 +6608,15 @@ function generateAndWriteDSL(analysis) {
6163
6608
  const dsl = generateStructurizrDSL(analysis, {
6164
6609
  includeDynamicDiagrams: true,
6165
6610
  includeComponentDiagrams: true,
6166
- componentDiagramContexts: contextTypes.length > 0 ? contextTypes : ["background"]
6611
+ componentDiagramContexts: contextTypes.length > 0 ? contextTypes : ["background"],
6612
+ snapshot
6167
6613
  });
6168
6614
  const outputDir = path6.join(process.cwd(), "docs");
6169
- if (!fs6.existsSync(outputDir)) {
6170
- fs6.mkdirSync(outputDir, { recursive: true });
6615
+ if (!fs7.existsSync(outputDir)) {
6616
+ fs7.mkdirSync(outputDir, { recursive: true });
6171
6617
  }
6172
6618
  const dslPath = path6.join(outputDir, "architecture.dsl");
6173
- fs6.writeFileSync(dslPath, dsl, "utf-8");
6619
+ fs7.writeFileSync(dslPath, dsl, "utf-8");
6174
6620
  return dslPath;
6175
6621
  }
6176
6622
  function displayNextSteps(dslPath) {
@@ -6206,7 +6652,7 @@ async function exportCommand(_args) {
6206
6652
  `, COLORS.blue));
6207
6653
  try {
6208
6654
  const dslPath = path6.join(process.cwd(), "docs", "architecture.dsl");
6209
- if (!fs6.existsSync(dslPath)) {
6655
+ if (!fs7.existsSync(dslPath)) {
6210
6656
  process.exit(1);
6211
6657
  }
6212
6658
  const outputDir = path6.join(process.cwd(), "docs", "site");
@@ -6244,7 +6690,7 @@ async function serveCommand(args) {
6244
6690
  try {
6245
6691
  const siteDir = path6.join(process.cwd(), "docs", "site");
6246
6692
  const indexPath = path6.join(siteDir, "index.html");
6247
- if (!fs6.existsSync(indexPath)) {
6693
+ if (!fs7.existsSync(indexPath)) {
6248
6694
  process.exit(1);
6249
6695
  }
6250
6696
  const portArg = args.find((arg) => arg.startsWith("--port="));
@@ -6261,7 +6707,7 @@ async function serveCommand(args) {
6261
6707
  fetch(req) {
6262
6708
  const url = new URL(req.url);
6263
6709
  const filePath = path6.join(siteDir, url.pathname === "/" ? "index.html" : url.pathname);
6264
- if (fs6.existsSync(filePath) && fs6.statSync(filePath).isFile()) {
6710
+ if (fs7.existsSync(filePath) && fs7.statSync(filePath).isFile()) {
6265
6711
  const file = BunGlobal.file(filePath);
6266
6712
  return new Response(file);
6267
6713
  }
@@ -6305,6 +6751,10 @@ ${color("Commands:", COLORS.blue)}
6305
6751
  ${color("bun visualize --generate", COLORS.green)}
6306
6752
  Analyze codebase and generate Structurizr DSL
6307
6753
 
6754
+ ${color("bun visualize generate --snapshot <path>", COLORS.green)}
6755
+ Overlay a captured MeshClientPeerStateSnapshot — runtime mesh peers
6756
+ and sync-state-coloured replication edges — onto the diagram
6757
+
6308
6758
  ${color("bun visualize --export", COLORS.green)}
6309
6759
  Generate static HTML site with interactive diagrams (requires Docker)
6310
6760
 
@@ -6341,7 +6791,7 @@ function findTsConfig() {
6341
6791
  path6.join(process.cwd(), "..", "tsconfig.json")
6342
6792
  ];
6343
6793
  for (const loc of locations) {
6344
- if (fs6.existsSync(loc)) {
6794
+ if (fs7.existsSync(loc)) {
6345
6795
  return loc;
6346
6796
  }
6347
6797
  }
@@ -6350,7 +6800,7 @@ function findTsConfig() {
6350
6800
  function findProjectRoot() {
6351
6801
  const locations = [process.cwd(), path6.join(process.cwd(), "..")];
6352
6802
  for (const loc of locations) {
6353
- if (fs6.existsSync(path6.join(loc, "manifest.json")) || fs6.existsSync(path6.join(loc, "package.json")) || fs6.existsSync(path6.join(loc, "tsconfig.json"))) {
6803
+ if (fs7.existsSync(path6.join(loc, "manifest.json")) || fs7.existsSync(path6.join(loc, "package.json")) || fs7.existsSync(path6.join(loc, "tsconfig.json"))) {
6354
6804
  return loc;
6355
6805
  }
6356
6806
  }
@@ -6360,4 +6810,4 @@ main().catch((_error) => {
6360
6810
  process.exit(1);
6361
6811
  });
6362
6812
 
6363
- //# debugId=D0F61C41F83644F864756E2164756E21
6813
+ //# debugId=4CFC761842CDF97B64756E2164756E21