@markdy/core 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -731,7 +731,8 @@ var EDGE_OPERATORS = {
731
731
  "->": "request",
732
732
  "<-": "response",
733
733
  "~>": "event",
734
- "--": "dependency"
734
+ "--": "dependency",
735
+ "..>": "dependency"
735
736
  };
736
737
  var RESERVED_SELECTORS = /* @__PURE__ */ new Set(["$title", "$nodes", "$edges"]);
737
738
  var CUE_ALIASES = {
@@ -818,10 +819,13 @@ function computeNodeDimensions(decl, baseW = 180, baseH = 76) {
818
819
  if (decl.kind === "dot") return { width: 64, height: 64 };
819
820
  if (decl.kind === "matrix") return { width: 220, height: 96 };
820
821
  const valueW = valLen > 0 ? Math.max(50, valLen * 9.5 + 16) : 0;
821
- const neededLabelChars = labelLen > 18 ? Math.ceil(labelLen / 2) : labelLen;
822
+ const words = (decl.label || "").trim().split(/\s+/).filter(Boolean);
823
+ const longestWord = Math.max(...words.map((w) => w.length), 0);
824
+ const avgLineChars = words.length > 1 ? Math.ceil(labelLen / Math.min(words.length, 2)) : labelLen;
825
+ const neededLabelChars = Math.max(longestWord + 2, avgLineChars, Math.min(labelLen, 22));
822
826
  const maxChars = Math.max(neededLabelChars, techLen);
823
- const neededTextW = Math.max(88, maxChars * 7.8);
824
- const calculatedW = 56 + neededTextW + (valueW > 0 ? valueW + 14 : 0) + 16;
827
+ const neededTextW = Math.max(96, maxChars * 8.4);
828
+ const calculatedW = 56 + neededTextW + (valueW > 0 ? valueW + 14 : 0) + 18;
825
829
  const minW = Math.max(baseW, Math.min(360, calculatedW));
826
830
  let width = Math.ceil(minW / 8) * 8;
827
831
  let height = baseH;
@@ -2434,7 +2438,7 @@ var THEMES = {
2434
2438
  dependency: "#818cf8"
2435
2439
  },
2436
2440
  fonts: {
2437
- title: "Georgia, Times New Roman, serif",
2441
+ title: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Inter, sans-serif",
2438
2442
  nodeName: "ui-sans-serif, system-ui, sans-serif",
2439
2443
  mono: "ui-monospace, SFMono-Regular, Menlo, monospace"
2440
2444
  },
@@ -2658,7 +2662,7 @@ var ParseError = class extends Error {
2658
2662
  this.column = column;
2659
2663
  }
2660
2664
  };
2661
- var FLOW_OP_RE = /(->|<-|~>|--)/;
2665
+ var FLOW_OP_RE = /(->|<-|~>|--|\.\.>|<->)/;
2662
2666
  function stripComment(line) {
2663
2667
  let inString = false;
2664
2668
  let escaped = false;
@@ -2710,7 +2714,7 @@ function parseProps(raw) {
2710
2714
  i = raw.length - parsed.rest.length;
2711
2715
  continue;
2712
2716
  }
2713
- const keyMatch = raw.slice(i).match(/^(\w[\w.-]*)=/);
2717
+ const keyMatch = raw.slice(i).match(/^(@?[\w.-]+)=/);
2714
2718
  if (!keyMatch) {
2715
2719
  i++;
2716
2720
  continue;
@@ -2828,6 +2832,14 @@ function tokenizeFlowChain(line) {
2828
2832
  current += ch;
2829
2833
  continue;
2830
2834
  }
2835
+ const op3 = line.slice(i, i + 3);
2836
+ if (op3 === "..>" || op3 === "<->") {
2837
+ if (current.trim()) parts.push(current.trim());
2838
+ parts.push(op3);
2839
+ current = "";
2840
+ i += 2;
2841
+ continue;
2842
+ }
2831
2843
  const op = line.slice(i, i + 2);
2832
2844
  if (op === "->" || op === "<-" || op === "~>" || op === "--") {
2833
2845
  if (current.trim()) parts.push(current.trim());
@@ -2851,11 +2863,27 @@ function parseFlowChain(line, lineNo) {
2851
2863
  let from = splitTargetLabel(parts[i++], lineNo).node;
2852
2864
  while (i < parts.length) {
2853
2865
  const opToken = parts[i++];
2854
- const op = EDGE_OPERATORS[opToken];
2855
- if (!op) throw new ParseError(`unknown flow operator '${opToken}'`, lineNo);
2856
2866
  if (i >= parts.length) throw new ParseError(`expected target after '${opToken}'`, lineNo);
2857
2867
  const { node: to, label } = splitTargetLabel(parts[i++], lineNo);
2858
2868
  if (!to) throw new ParseError(`expected target node after '${opToken}'`, lineNo);
2869
+ if (opToken === "<->") {
2870
+ segments.push({
2871
+ from,
2872
+ op: "request",
2873
+ to,
2874
+ label
2875
+ });
2876
+ segments.push({
2877
+ from: to,
2878
+ op: "response",
2879
+ to: from,
2880
+ label
2881
+ });
2882
+ from = to;
2883
+ continue;
2884
+ }
2885
+ const op = EDGE_OPERATORS[opToken];
2886
+ if (!op) throw new ParseError(`unknown flow operator '${opToken}'`, lineNo);
2859
2887
  segments.push({
2860
2888
  from: op === "response" ? to : from,
2861
2889
  op,
@@ -3806,7 +3834,13 @@ function matchNode(node, selector) {
3806
3834
  if (selector.kindEquals && node.kind.toLowerCase() !== selector.kindEquals.toLowerCase()) return false;
3807
3835
  if (selector.roleEquals) {
3808
3836
  const role = nodeRole(node.kind);
3809
- if (role.toLowerCase() !== selector.roleEquals.toLowerCase()) return false;
3837
+ const targetRole = selector.roleEquals.toLowerCase();
3838
+ if (targetRole === "gateway") {
3839
+ const isGateway = role === "network" || ["gateway", "api_gateway", "reverse_proxy", "proxy", "router"].includes(node.kind.toLowerCase());
3840
+ if (!isGateway) return false;
3841
+ } else if (role.toLowerCase() !== targetRole) {
3842
+ return false;
3843
+ }
3810
3844
  }
3811
3845
  if (selector.labelContains) {
3812
3846
  const target = (node.label || node.id).toLowerCase();
@@ -3967,6 +4001,30 @@ var ARCH_RULE_PRESETS = {
3967
4001
  from: { roleEquals: "security" }
3968
4002
  }
3969
4003
  ]
4004
+ },
4005
+ deploymentOwnership: {
4006
+ id: "deployment-ownership",
4007
+ name: "Deployment Ownership & Regional Governance",
4008
+ description: "Ensure production stateful stores and services are guarded in explicit group perimeters",
4009
+ rules: [
4010
+ {
4011
+ id: "no-unprotected-public-database",
4012
+ name: "No Public Database Exposure",
4013
+ description: "Databases and stateful stores must not be directly accessed from public browser/mobile clients.",
4014
+ severity: "error",
4015
+ type: "cannot-connect",
4016
+ from: { roleEquals: "client" },
4017
+ to: { roleEquals: "data" }
4018
+ },
4019
+ {
4020
+ id: "no-cross-region-sync-bypasses",
4021
+ name: "Synchronous Request Cycle Prevention",
4022
+ description: "Synchronous requests must not form cycles across microservices.",
4023
+ severity: "error",
4024
+ type: "forbidden-cycle",
4025
+ edge: { kind: "request" }
4026
+ }
4027
+ ]
3970
4028
  }
3971
4029
  };
3972
4030
  function validateArchitecture(ast, rules = [
@@ -3974,7 +4032,7 @@ function validateArchitecture(ast, rules = [
3974
4032
  ...ARCH_RULE_PRESETS.microservicesGovernance.rules
3975
4033
  ]) {
3976
4034
  const violations = [];
3977
- const nodes = Object.values(ast.nodes);
4035
+ const nodes = Object.values(ast?.nodes || {});
3978
4036
  const nodeMap = new Map(nodes.map((n) => [n.id, n]));
3979
4037
  const edges = extractAllEdges(ast);
3980
4038
  for (const rule of rules) {
@@ -4021,6 +4079,11 @@ function validateArchitecture(ast, rules = [
4021
4079
  break;
4022
4080
  }
4023
4081
  case "forbidden-cycle": {
4082
+ const dtype = ast.meta?.type || ast.config?.type || "architecture";
4083
+ const nonServiceArchetypes = ["state", "sequence", "layers", "flywheel", "loop", "venn"];
4084
+ if (nonServiceArchetypes.includes(dtype)) {
4085
+ break;
4086
+ }
4024
4087
  const cycleInfo = detectCycleInGraph(nodes, edges, rule.edge);
4025
4088
  if (cycleInfo) {
4026
4089
  violations.push({
@@ -4271,26 +4334,66 @@ function classifyTechnology(id, label = "") {
4271
4334
  }
4272
4335
 
4273
4336
  // src/diff.ts
4337
+ function extractDiffEdges(ast) {
4338
+ const edges = [];
4339
+ const seen = /* @__PURE__ */ new Set();
4340
+ const add = (from, to, kind, label) => {
4341
+ const key = `${from}->${to}:${kind}`;
4342
+ if (!seen.has(key)) {
4343
+ seen.add(key);
4344
+ edges.push({ from, to, kind, label });
4345
+ }
4346
+ };
4347
+ for (const edge of ast.edges || []) {
4348
+ add(edge.from, edge.to, edge.kind, edge.label);
4349
+ }
4350
+ for (const beat of ast.beats || []) {
4351
+ for (const cue of beat.cues || []) {
4352
+ if (cue.kind === "flow") {
4353
+ for (const seg of cue.segments) {
4354
+ add(seg.from, seg.to, seg.op, seg.label);
4355
+ }
4356
+ } else if (cue.kind === "parallel") {
4357
+ for (const child of cue.cues) {
4358
+ if (child.kind === "flow") {
4359
+ for (const seg of child.segments) {
4360
+ add(seg.from, seg.to, seg.op, seg.label);
4361
+ }
4362
+ }
4363
+ }
4364
+ }
4365
+ }
4366
+ }
4367
+ return edges;
4368
+ }
4274
4369
  function diffDiagramASTs(beforeAST, afterAST) {
4275
4370
  const nodeDiffs = [];
4276
4371
  const edgeDiffs = [];
4277
- const beforeNodeIds = new Set(Object.keys(beforeAST.nodes));
4278
- const afterNodeIds = new Set(Object.keys(afterAST.nodes));
4372
+ const groupDiffs = [];
4373
+ const beforeNodes = beforeAST.nodes || {};
4374
+ const afterNodes = afterAST.nodes || {};
4375
+ const beforeNodeIds = new Set(Object.keys(beforeNodes));
4376
+ const afterNodeIds = new Set(Object.keys(afterNodes));
4279
4377
  let addedNodesCount = 0;
4280
4378
  let removedNodesCount = 0;
4281
4379
  let modifiedNodesCount = 0;
4282
- for (const [id, afterNode] of Object.entries(afterAST.nodes)) {
4380
+ for (const [id, afterNode] of Object.entries(afterNodes)) {
4283
4381
  if (!beforeNodeIds.has(id)) {
4284
- nodeDiffs.push({ id, status: "added", after: afterNode, changes: ["Newly added node"] });
4382
+ nodeDiffs.push({ id, status: "added", after: afterNode, changes: ["Newly provisioned node"] });
4285
4383
  addedNodesCount++;
4286
4384
  } else {
4287
- const beforeNode = beforeAST.nodes[id];
4385
+ const beforeNode = beforeNodes[id];
4288
4386
  const changes = [];
4289
4387
  if (beforeNode.kind !== afterNode.kind) {
4290
- changes.push(`Kind changed: ${beforeNode.kind} \u2192 ${afterNode.kind}`);
4388
+ changes.push(`Kind: ${beforeNode.kind} \u2192 ${afterNode.kind}`);
4291
4389
  }
4292
4390
  if (beforeNode.label !== afterNode.label) {
4293
- changes.push(`Label changed: "${beforeNode.label}" \u2192 "${afterNode.label}"`);
4391
+ changes.push(`Label: "${beforeNode.label}" \u2192 "${afterNode.label}"`);
4392
+ }
4393
+ const beforeSrc = beforeNode.props?.["@src"] || beforeNode.props?.["src"];
4394
+ const afterSrc = afterNode.props?.["@src"] || afterNode.props?.["src"];
4395
+ if (beforeSrc !== afterSrc) {
4396
+ changes.push(`Code Provenance: ${beforeSrc || "none"} \u2192 ${afterSrc || "none"}`);
4294
4397
  }
4295
4398
  if (changes.length > 0) {
4296
4399
  nodeDiffs.push({ id, status: "modified", before: beforeNode, after: afterNode, changes });
@@ -4300,84 +4403,137 @@ function diffDiagramASTs(beforeAST, afterAST) {
4300
4403
  }
4301
4404
  }
4302
4405
  }
4303
- for (const [id, beforeNode] of Object.entries(beforeAST.nodes)) {
4406
+ for (const [id, beforeNode] of Object.entries(beforeNodes)) {
4304
4407
  if (!afterNodeIds.has(id)) {
4305
- nodeDiffs.push({ id, status: "removed", before: beforeNode, changes: ["Removed node"] });
4408
+ nodeDiffs.push({ id, status: "removed", before: beforeNode, changes: ["Decommissioned node"] });
4306
4409
  removedNodesCount++;
4307
4410
  }
4308
4411
  }
4309
4412
  const edgeKey = (e) => `${e.from}->${e.to}:${e.kind}`;
4310
- const beforeEdgeMap = new Map(beforeAST.edges.map((e) => [edgeKey(e), e]));
4311
- const afterEdgeMap = new Map(afterAST.edges.map((e) => [edgeKey(e), e]));
4312
- for (const [key, afterEdge] of afterEdgeMap) {
4413
+ const beforeEdges = extractDiffEdges(beforeAST);
4414
+ const afterEdges = extractDiffEdges(afterAST);
4415
+ const beforeEdgeMap = new Map(beforeEdges.map((e) => [edgeKey(e), e]));
4416
+ const afterEdgeMap = new Map(afterEdges.map((e) => [edgeKey(e), e]));
4417
+ let addedEdgesCount = 0;
4418
+ let removedEdgesCount = 0;
4419
+ for (const [key, afterEdge] of afterEdgeMap.entries()) {
4313
4420
  if (!beforeEdgeMap.has(key)) {
4314
- edgeDiffs.push({ key, status: "added", after: afterEdge });
4421
+ edgeDiffs.push({ key, status: "added", after: afterEdge, changes: ["New interaction route"] });
4422
+ addedEdgesCount++;
4315
4423
  } else {
4316
4424
  const beforeEdge = beforeEdgeMap.get(key);
4425
+ const changes = [];
4317
4426
  if (beforeEdge.label !== afterEdge.label) {
4318
- edgeDiffs.push({ key, status: "modified", before: beforeEdge, after: afterEdge });
4427
+ changes.push(`Protocol/Label: "${beforeEdge.label || ""}" \u2192 "${afterEdge.label || ""}"`);
4428
+ }
4429
+ if (changes.length > 0) {
4430
+ edgeDiffs.push({ key, status: "modified", before: beforeEdge, after: afterEdge, changes });
4319
4431
  } else {
4320
- edgeDiffs.push({ key, status: "unchanged", before: beforeEdge, after: afterEdge });
4432
+ edgeDiffs.push({ key, status: "unchanged", before: beforeEdge, after: afterEdge, changes: [] });
4321
4433
  }
4322
4434
  }
4323
4435
  }
4324
- for (const [key, beforeEdge] of beforeEdgeMap) {
4436
+ for (const [key, beforeEdge] of beforeEdgeMap.entries()) {
4325
4437
  if (!afterEdgeMap.has(key)) {
4326
- edgeDiffs.push({ key, status: "removed", before: beforeEdge });
4438
+ edgeDiffs.push({ key, status: "removed", before: beforeEdge, changes: ["Decommissioned route"] });
4439
+ removedEdgesCount++;
4327
4440
  }
4328
4441
  }
4329
- const summaryLines = [
4330
- "### \u{1F4CA} Markdy Architectural Diff Summary",
4331
- "",
4332
- `| Metric | Count |`,
4333
- `|---|---|`,
4334
- `| \u{1F7E2} Nodes Added | **${addedNodesCount}** |`,
4335
- `| \u{1F534} Nodes Removed | **${removedNodesCount}** |`,
4336
- `| \u{1F7E1} Nodes Modified | **${modifiedNodesCount}** |`,
4337
- ""
4338
- ];
4339
- if (addedNodesCount > 0 || modifiedNodesCount > 0 || removedNodesCount > 0) {
4340
- summaryLines.push("#### Changes Detail");
4341
- for (const nd of nodeDiffs.filter((n) => n.status !== "unchanged")) {
4342
- summaryLines.push(`- **${nd.id}** (${nd.status.toUpperCase()}): ${nd.changes.join(", ")}`);
4442
+ const beforeGroups = beforeAST.groups || {};
4443
+ const afterGroups = afterAST.groups || {};
4444
+ const beforeGroupIds = new Set(Object.keys(beforeGroups));
4445
+ const afterGroupIds = new Set(Object.keys(afterGroups));
4446
+ for (const [id, afterGroup] of Object.entries(afterGroups)) {
4447
+ if (!beforeGroupIds.has(id)) {
4448
+ groupDiffs.push({ id, status: "added", after: afterGroup, changes: ["New security / subsystem boundary"] });
4449
+ } else {
4450
+ const beforeGroup = beforeGroups[id];
4451
+ const beforeMembers = new Set(beforeGroup.members || []);
4452
+ const afterMembers = new Set(afterGroup.members || []);
4453
+ const diffMembers = (afterGroup.members || []).filter((m) => !beforeMembers.has(m));
4454
+ if (diffMembers.length > 0 || beforeGroup.members.length !== afterGroup.members.length) {
4455
+ groupDiffs.push({
4456
+ id,
4457
+ status: "modified",
4458
+ before: beforeGroup,
4459
+ after: afterGroup,
4460
+ changes: [`Boundary membership updated: [${(afterGroup.members || []).join(", ")}]`]
4461
+ });
4462
+ }
4343
4463
  }
4344
- summaryLines.push("");
4345
4464
  }
4346
- const evolutionLines = [
4347
- `scene theme=${afterAST.meta.theme || "paper"}`,
4348
- `layout ${afterAST.meta.direction || "LR"}`,
4349
- ""
4350
- ];
4351
- for (const [id, node] of Object.entries({ ...beforeAST.nodes, ...afterAST.nodes })) {
4352
- evolutionLines.push(`${node.kind} ${id} "${node.label}"`);
4353
- }
4354
- evolutionLines.push("");
4355
- evolutionLines.push('beat v1 "Baseline Architecture":');
4356
- const v1NodeIds = Object.keys(beforeAST.nodes).join(" ");
4357
- if (v1NodeIds) {
4358
- evolutionLines.push(` show ${v1NodeIds}`);
4359
- }
4360
- evolutionLines.push("");
4361
- evolutionLines.push('beat transition "Migrate to Target Architecture":');
4362
- const addedIds = nodeDiffs.filter((n) => n.status === "added").map((n) => n.id);
4363
- const removedIds = nodeDiffs.filter((n) => n.status === "removed").map((n) => n.id);
4364
- if (removedIds.length > 0) {
4365
- evolutionLines.push(` hide ${removedIds.join(" ")}`);
4366
- }
4367
- if (addedIds.length > 0) {
4368
- evolutionLines.push(` show ${addedIds.join(" ")}`);
4369
- evolutionLines.push(` glow ${addedIds.join(" ")} color="#10b981"`);
4465
+ for (const [id, beforeGroup] of Object.entries(beforeGroups)) {
4466
+ if (!afterGroupIds.has(id)) {
4467
+ groupDiffs.push({ id, status: "removed", before: beforeGroup, changes: ["Dissolved boundary"] });
4468
+ }
4370
4469
  }
4470
+ const evolutionMarkdyScript = generateEvolutionMarkdyScript(
4471
+ beforeAST,
4472
+ afterAST,
4473
+ nodeDiffs,
4474
+ edgeDiffs
4475
+ );
4476
+ const summaryMarkdown = [
4477
+ `# Markdy Architectural Diff Summary`,
4478
+ `- **Nodes Added**: ${addedNodesCount}`,
4479
+ `- **Nodes Removed**: ${removedNodesCount}`,
4480
+ `- **Nodes Modified**: ${modifiedNodesCount}`,
4481
+ `- **Routes Added**: ${addedEdgesCount}`,
4482
+ `- **Routes Removed**: ${removedEdgesCount}`,
4483
+ "",
4484
+ `### Component Delta`,
4485
+ `| Component | Status | Details |`,
4486
+ `| :--- | :--- | :--- |`,
4487
+ ...nodeDiffs.filter((n) => n.status !== "unchanged").map((n) => `| \`${n.id}\` | **${n.status.toUpperCase()}** | ${n.changes.join("; ")} |`),
4488
+ "",
4489
+ `### Connection Delta`,
4490
+ `| Connection | Status | Details |`,
4491
+ `| :--- | :--- | :--- |`,
4492
+ ...edgeDiffs.filter((e) => e.status !== "unchanged").map((e) => `| \`${e.key}\` | **${e.status.toUpperCase()}** | ${e.changes.join("; ")} |`)
4493
+ ].join("\n");
4371
4494
  return {
4372
4495
  nodes: nodeDiffs,
4373
4496
  edges: edgeDiffs,
4497
+ groups: groupDiffs,
4374
4498
  addedNodesCount,
4375
4499
  removedNodesCount,
4376
4500
  modifiedNodesCount,
4377
- summaryMarkdown: summaryLines.join("\n"),
4378
- evolutionMarkdyScript: evolutionLines.join("\n")
4501
+ addedEdgesCount,
4502
+ removedEdgesCount,
4503
+ summaryMarkdown,
4504
+ evolutionMarkdyScript
4379
4505
  };
4380
4506
  }
4507
+ function generateEvolutionMarkdyScript(beforeAST, afterAST, nodeDiffs, edgeDiffs) {
4508
+ const lines = [`scene theme=paper`, `layout ${afterAST.meta?.direction || "LR"}`, ""];
4509
+ const allNodes = /* @__PURE__ */ new Map();
4510
+ for (const [id, n] of Object.entries(beforeAST.nodes || {})) allNodes.set(id, n);
4511
+ for (const [id, n] of Object.entries(afterAST.nodes || {})) allNodes.set(id, n);
4512
+ for (const [id, node] of allNodes.entries()) {
4513
+ const propsStr = Object.entries(node.props || {}).map(([k, v]) => `${k}=${typeof v === "string" ? `"${v}"` : v}`).join(" ");
4514
+ lines.push(`${node.kind} ${id} "${node.label}" ${propsStr}`.trim());
4515
+ }
4516
+ lines.push("");
4517
+ const beforeNodeIds = Object.keys(beforeAST.nodes || {}).join(" ");
4518
+ lines.push(`beat baseline:`);
4519
+ lines.push(` show ${beforeNodeIds}`);
4520
+ for (const edge of beforeAST.edges || []) {
4521
+ lines.push(` ${edge.from} -> ${edge.to} "${edge.label || ""}"`);
4522
+ }
4523
+ const removedNodeIds = nodeDiffs.filter((n) => n.status === "removed").map((n) => n.id);
4524
+ const addedNodeIds = nodeDiffs.filter((n) => n.status === "added").map((n) => n.id);
4525
+ lines.push("");
4526
+ lines.push(`beat transition:`);
4527
+ if (removedNodeIds.length > 0) {
4528
+ lines.push(` glow ${removedNodeIds.join(" ")} color=#fb7185 strength=1.2`);
4529
+ lines.push(` hide ${removedNodeIds.join(" ")} dur=800ms`);
4530
+ }
4531
+ if (addedNodeIds.length > 0) {
4532
+ lines.push(` show ${addedNodeIds.join(" ")} stagger=80ms`);
4533
+ lines.push(` glow ${addedNodeIds.join(" ")} color=#34d399 strength=1.5`);
4534
+ }
4535
+ return lines.join("\n");
4536
+ }
4381
4537
 
4382
4538
  // src/url-codec.ts
4383
4539
  var PREFIX = "~m";
@@ -4469,16 +4625,27 @@ async function decompressMarkdyFromUrlHash(hash) {
4469
4625
  }
4470
4626
 
4471
4627
  // src/router.ts
4472
- function getBoxPortPosition(box, port) {
4473
- switch (port) {
4474
- case "left":
4475
- return { x: box.x, y: box.y + box.height / 2 };
4476
- case "right":
4477
- return { x: box.x + box.width, y: box.y + box.height / 2 };
4478
- case "top":
4479
- return { x: box.x + box.width / 2, y: box.y };
4480
- case "bottom":
4481
- return { x: box.x + box.width / 2, y: box.y + box.height };
4628
+ function getBoxPortPosition(box, port, lane) {
4629
+ if (port === "left" || port === "right") {
4630
+ const x = port === "left" ? box.x : box.x + box.width;
4631
+ if (lane && lane.total > 1) {
4632
+ const padding = Math.min(16, box.height * 0.18);
4633
+ const span = box.height - padding * 2;
4634
+ const step = span / (lane.total - 1 || 1);
4635
+ const y = box.y + padding + lane.index * step;
4636
+ return { x, y };
4637
+ }
4638
+ return { x, y: box.y + box.height / 2 };
4639
+ } else {
4640
+ const y = port === "top" ? box.y : box.y + box.height;
4641
+ if (lane && lane.total > 1) {
4642
+ const padding = Math.min(16, box.width * 0.18);
4643
+ const span = box.width - padding * 2;
4644
+ const step = span / (lane.total - 1 || 1);
4645
+ const x = box.x + padding + lane.index * step;
4646
+ return { x, y };
4647
+ }
4648
+ return { x: box.x + box.width / 2, y };
4482
4649
  }
4483
4650
  }
4484
4651
  function selectOptimalPorts(sourceBox, targetBox) {
@@ -4492,12 +4659,96 @@ function selectOptimalPorts(sourceBox, targetBox) {
4492
4659
  return dy > 0 ? { sourcePort: "bottom", targetPort: "top" } : { sourcePort: "top", targetPort: "bottom" };
4493
4660
  }
4494
4661
  }
4495
- function routeOrthogonalEdge(sourceBox, targetBox) {
4496
- const { sourcePort, targetPort } = selectOptimalPorts(sourceBox, targetBox);
4497
- const start = getBoxPortPosition(sourceBox, sourcePort);
4498
- const end = getBoxPortPosition(targetBox, targetPort);
4662
+ function buildSmoothSvgPath(start, waypoints, end, cornerRadius = 0) {
4663
+ const allPoints = [start, ...waypoints, end];
4664
+ if (allPoints.length <= 2 || cornerRadius <= 0) {
4665
+ let d2 = `M ${start.x} ${start.y}`;
4666
+ for (const p of waypoints) {
4667
+ d2 += ` L ${p.x} ${p.y}`;
4668
+ }
4669
+ d2 += ` L ${end.x} ${end.y}`;
4670
+ return d2;
4671
+ }
4672
+ let d = `M ${start.x} ${start.y}`;
4673
+ for (let i = 1; i < allPoints.length - 1; i++) {
4674
+ const prev = allPoints[i - 1];
4675
+ const curr = allPoints[i];
4676
+ const next = allPoints[i + 1];
4677
+ const dPrev = Math.hypot(curr.x - prev.x, curr.y - prev.y);
4678
+ const dNext = Math.hypot(next.x - curr.x, next.y - curr.y);
4679
+ const r = Math.min(cornerRadius, dPrev / 2, dNext / 2);
4680
+ if (r < 2) {
4681
+ d += ` L ${curr.x} ${curr.y}`;
4682
+ continue;
4683
+ }
4684
+ const startX = curr.x + (prev.x - curr.x) * (r / dPrev);
4685
+ const startY = curr.y + (prev.y - curr.y) * (r / dPrev);
4686
+ const endX = curr.x + (next.x - curr.x) * (r / dNext);
4687
+ const endY = curr.y + (next.y - curr.y) * (r / dNext);
4688
+ d += ` L ${startX} ${startY}`;
4689
+ d += ` Q ${curr.x} ${curr.y} ${endX} ${endY}`;
4690
+ }
4691
+ d += ` L ${end.x} ${end.y}`;
4692
+ return d;
4693
+ }
4694
+ function routeOrthogonalEdge(sourceBox, targetBox, options = {}) {
4695
+ const isSelfLoop = sourceBox === targetBox || sourceBox.x === targetBox.x && sourceBox.y === targetBox.y && sourceBox.width === targetBox.width && sourceBox.height === targetBox.height;
4696
+ const MARGIN = options.margin ?? 20;
4697
+ if (isSelfLoop) {
4698
+ const loopSpan = Math.max(28, MARGIN * 1.5);
4699
+ const port = options.sourcePort || options.targetPort || "top";
4700
+ let start2;
4701
+ let end2;
4702
+ let waypoints2;
4703
+ if (port === "top") {
4704
+ const startX = sourceBox.x + sourceBox.width * 0.35;
4705
+ const endX = sourceBox.x + sourceBox.width * 0.65;
4706
+ const topY = sourceBox.y;
4707
+ const apexY = topY - loopSpan;
4708
+ start2 = { x: startX, y: topY };
4709
+ end2 = { x: endX, y: topY };
4710
+ waypoints2 = [{ x: startX, y: apexY }, { x: endX, y: apexY }];
4711
+ } else if (port === "bottom") {
4712
+ const startX = sourceBox.x + sourceBox.width * 0.35;
4713
+ const endX = sourceBox.x + sourceBox.width * 0.65;
4714
+ const botY = sourceBox.y + sourceBox.height;
4715
+ const apexY = botY + loopSpan;
4716
+ start2 = { x: startX, y: botY };
4717
+ end2 = { x: endX, y: botY };
4718
+ waypoints2 = [{ x: startX, y: apexY }, { x: endX, y: apexY }];
4719
+ } else if (port === "left") {
4720
+ const startY = sourceBox.y + sourceBox.height * 0.35;
4721
+ const endY = sourceBox.y + sourceBox.height * 0.65;
4722
+ const leftX = sourceBox.x;
4723
+ const apexX = leftX - loopSpan;
4724
+ start2 = { x: leftX, y: startY };
4725
+ end2 = { x: leftX, y: endY };
4726
+ waypoints2 = [{ x: apexX, y: startY }, { x: apexX, y: endY }];
4727
+ } else {
4728
+ const startY = sourceBox.y + sourceBox.height * 0.35;
4729
+ const endY = sourceBox.y + sourceBox.height * 0.65;
4730
+ const rightX = sourceBox.x + sourceBox.width;
4731
+ const apexX = rightX + loopSpan;
4732
+ start2 = { x: rightX, y: startY };
4733
+ end2 = { x: rightX, y: endY };
4734
+ waypoints2 = [{ x: apexX, y: startY }, { x: apexX, y: endY }];
4735
+ }
4736
+ const svgPathData2 = buildSmoothSvgPath(start2, waypoints2, end2, options.cornerRadius ?? 6);
4737
+ return {
4738
+ sourcePort: port,
4739
+ targetPort: port,
4740
+ startPoint: start2,
4741
+ endPoint: end2,
4742
+ waypoints: waypoints2,
4743
+ svgPathData: svgPathData2
4744
+ };
4745
+ }
4746
+ const optimal = selectOptimalPorts(sourceBox, targetBox);
4747
+ const sourcePort = options.sourcePort || optimal.sourcePort;
4748
+ const targetPort = options.targetPort || optimal.targetPort;
4749
+ const start = getBoxPortPosition(sourceBox, sourcePort, options.sourceLane);
4750
+ const end = getBoxPortPosition(targetBox, targetPort, options.targetLane);
4499
4751
  const waypoints = [];
4500
- const MARGIN = 20;
4501
4752
  if (sourcePort === "right" && targetPort === "left") {
4502
4753
  if (start.x <= end.x - MARGIN * 2) {
4503
4754
  const midX = (start.x + end.x) / 2;
@@ -4564,11 +4815,7 @@ function routeOrthogonalEdge(sourceBox, targetBox) {
4564
4815
  }
4565
4816
  waypoints.push(p2);
4566
4817
  }
4567
- let svgPathData = `M ${start.x} ${start.y}`;
4568
- for (const wp of waypoints) {
4569
- svgPathData += ` L ${wp.x} ${wp.y}`;
4570
- }
4571
- svgPathData += ` L ${end.x} ${end.y}`;
4818
+ const svgPathData = buildSmoothSvgPath(start, waypoints, end, options.cornerRadius ?? 6);
4572
4819
  return {
4573
4820
  sourcePort,
4574
4821
  targetPort,
@@ -4578,6 +4825,550 @@ function routeOrthogonalEdge(sourceBox, targetBox) {
4578
4825
  svgPathData
4579
4826
  };
4580
4827
  }
4828
+ function allocatePortLanes(edges, boxes) {
4829
+ const result = /* @__PURE__ */ new Map();
4830
+ const portGroups = /* @__PURE__ */ new Map();
4831
+ for (const edge of edges) {
4832
+ if (edge.from === edge.to) continue;
4833
+ const sBox = boxes[edge.from];
4834
+ const tBox = boxes[edge.to];
4835
+ if (!sBox || !tBox) continue;
4836
+ const { sourcePort, targetPort } = selectOptimalPorts(sBox, tBox);
4837
+ const sKey = `${edge.from}:${sourcePort}`;
4838
+ const tKey = `${edge.to}:${targetPort}`;
4839
+ const tgtCenter = { x: tBox.x + tBox.width / 2, y: tBox.y + tBox.height / 2 };
4840
+ const srcCenter = { x: sBox.x + sBox.width / 2, y: sBox.y + sBox.height / 2 };
4841
+ if (!portGroups.has(sKey)) portGroups.set(sKey, []);
4842
+ portGroups.get(sKey).push({
4843
+ edge,
4844
+ role: "source",
4845
+ otherCenterY: tgtCenter.y,
4846
+ otherCenterX: tgtCenter.x
4847
+ });
4848
+ if (!portGroups.has(tKey)) portGroups.set(tKey, []);
4849
+ portGroups.get(tKey).push({
4850
+ edge,
4851
+ role: "target",
4852
+ otherCenterY: srcCenter.y,
4853
+ otherCenterX: srcCenter.x
4854
+ });
4855
+ }
4856
+ for (const [key, list] of portGroups.entries()) {
4857
+ if (list.length <= 1) continue;
4858
+ const isVertical = key.endsWith(":left") || key.endsWith(":right");
4859
+ list.sort((a, b) => {
4860
+ if (isVertical) {
4861
+ if (Math.abs(a.otherCenterY - b.otherCenterY) > 1) {
4862
+ return a.otherCenterY - b.otherCenterY;
4863
+ }
4864
+ } else {
4865
+ if (Math.abs(a.otherCenterX - b.otherCenterX) > 1) {
4866
+ return a.otherCenterX - b.otherCenterX;
4867
+ }
4868
+ }
4869
+ const aMin = a.edge.from < a.edge.to ? a.edge.from : a.edge.to;
4870
+ const aMax = a.edge.from < a.edge.to ? a.edge.to : a.edge.from;
4871
+ const bMin = b.edge.from < b.edge.to ? b.edge.from : b.edge.to;
4872
+ const bMax = b.edge.from < b.edge.to ? b.edge.to : b.edge.from;
4873
+ if (aMin !== bMin || aMax !== bMax) {
4874
+ return (a.edge.id || "").localeCompare(b.edge.id || "");
4875
+ }
4876
+ const aDir = a.edge.from < a.edge.to ? 0 : 1;
4877
+ const bDir = b.edge.from < b.edge.to ? 0 : 1;
4878
+ return aDir - bDir;
4879
+ });
4880
+ list.forEach((item, index) => {
4881
+ const existing = result.get(item.edge) || {};
4882
+ if (item.role === "source") {
4883
+ existing.sourceLane = { index, total: list.length };
4884
+ } else {
4885
+ existing.targetLane = { index, total: list.length };
4886
+ }
4887
+ result.set(item.edge, existing);
4888
+ });
4889
+ }
4890
+ return result;
4891
+ }
4892
+
4893
+ // src/symbols.ts
4894
+ var VECTOR_SYMBOLS = {
4895
+ // Cloud & Infrastructure
4896
+ aws: {
4897
+ name: "AWS",
4898
+ category: "cloud",
4899
+ viewBox: "0 0 24 24",
4900
+ svgPaths: `<path fill="currentColor" d="M12 2L2 7l10 5 10-5-10-5zm0 8.5L4.5 7 12 3.25 19.5 7 12 10.5zM2 17l10 5 10-5M2 12l10 5 10-5"/>`,
4901
+ brandColor: "#FF9900"
4902
+ },
4903
+ gcp: {
4904
+ name: "Google Cloud",
4905
+ category: "cloud",
4906
+ viewBox: "0 0 24 24",
4907
+ svgPaths: `<path fill="currentColor" d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3z"/>`,
4908
+ brandColor: "#4285F4"
4909
+ },
4910
+ azure: {
4911
+ name: "Microsoft Azure",
4912
+ category: "cloud",
4913
+ viewBox: "0 0 24 24",
4914
+ svgPaths: `<path fill="currentColor" d="M13.05 4.24l-6.1 11.23L2 17.52l7.73-14.28 3.32 1zm1.09 1.94L18.42 16h-7.8l-1.92 3.76H22l-7.86-13.58z"/>`,
4915
+ brandColor: "#0089D6"
4916
+ },
4917
+ kubernetes: {
4918
+ name: "Kubernetes",
4919
+ category: "compute",
4920
+ viewBox: "0 0 24 24",
4921
+ svgPaths: `<path fill="currentColor" d="M12 2l8.66 5v10L12 22l-8.66-5V7L12 2zm0 2.31L5.34 7.69v7.62L12 18.69l6.66-3.38V7.69L12 4.31zm0 3.69a4 4 0 1 1 0 8 4 4 0 0 1 0-8z"/>`,
4922
+ brandColor: "#326CE5"
4923
+ },
4924
+ docker: {
4925
+ name: "Docker",
4926
+ category: "compute",
4927
+ viewBox: "0 0 24 24",
4928
+ svgPaths: `<path fill="currentColor" d="M13.98 11.08h1.83V9.25h-1.83v1.83zm-2.75 0h1.83V9.25h-1.83v1.83zm-2.75 0h1.83V9.25H8.48v1.83zm-2.75 0h1.83V9.25H5.73v1.83zm8.25-2.75h1.83V6.5h-1.83v1.83zm-2.75 0h1.83V6.5h-1.83v1.83zm-2.75 0h1.83V6.5H8.48v1.83zm8.25 0h1.83V6.5h-1.83v1.83zm1.83 5.5c-.4 0-.8.1-1.1.3-.8-1.5-2.4-2.5-4.3-2.5H2.4c-.2.7-.4 1.4-.4 2.2 0 4.4 3.6 8 8 8 5 0 9.2-3.6 9.9-8.4.8.1 1.6-.2 2.1-.8.4-.5.5-1.2.3-1.8-.7.7-1.5 1-2.3 1z"/>`,
4929
+ brandColor: "#2496ED"
4930
+ },
4931
+ cloudflare: {
4932
+ name: "Cloudflare",
4933
+ category: "cloud",
4934
+ viewBox: "0 0 24 24",
4935
+ svgPaths: `<path fill="currentColor" d="M18.42 10.36A6.5 6.5 0 0 0 7.2 9.04 4.5 4.5 0 0 0 3 13.5a4.5 4.5 0 0 0 4.5 4.5h11a3.5 3.5 0 0 0 .92-6.88v-.76z"/>`,
4936
+ brandColor: "#F38020"
4937
+ },
4938
+ s3: {
4939
+ name: "Amazon S3",
4940
+ category: "cloud",
4941
+ viewBox: "0 0 24 24",
4942
+ svgPaths: `<path fill="currentColor" d="M12 2L4 6v12l8 4 8-4V6l-8-4zm0 2.2L18.2 7 12 9.8 5.8 7 12 4.2zM6 8.5l5 2.5v8.2L6 16.7V8.5zm12 8.2l-5 2.5V11l5-2.5v8.2z"/>`,
4943
+ brandColor: "#E05243"
4944
+ },
4945
+ // Databases & Stores
4946
+ postgresql: {
4947
+ name: "PostgreSQL",
4948
+ category: "database",
4949
+ viewBox: "0 0 24 24",
4950
+ svgPaths: `<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.4z"/>`,
4951
+ brandColor: "#336791"
4952
+ },
4953
+ mysql: {
4954
+ name: "MySQL",
4955
+ category: "database",
4956
+ viewBox: "0 0 24 24",
4957
+ svgPaths: `<path fill="currentColor" d="M12 3c-4.97 0-9 1.79-9 4v10c0 2.21 4.03 4 9 4s9-1.79 9-4V7c0-2.21-4.03-4-9-4zm0 2c4.41 0 7 1.43 7 2s-2.59 2-7 2-7-1.43-7-2 2.59-2 7-2zm0 14c-4.41 0-7-1.43-7-2v-1.82c1.78 1.11 4.29 1.82 7 1.82s5.22-.71 7-1.82V17c0 .57-2.59 2-7 2zm0-5c-4.41 0-7-1.43-7-2v-1.82c1.78 1.11 4.29 1.82 7 1.82s5.22-.71 7-1.82V12c0 .57-2.59 2-7 2z"/>`,
4958
+ brandColor: "#4479A1"
4959
+ },
4960
+ redis: {
4961
+ name: "Redis",
4962
+ category: "database",
4963
+ viewBox: "0 0 24 24",
4964
+ svgPaths: `<path fill="currentColor" d="M12 2L2 7.5l10 5.5 10-5.5L12 2zm-8 8.7V17L12 22.5V16L4 10.7zm16 0L12 16v6.5l8-5.5v-6.3z"/>`,
4965
+ brandColor: "#DC382D"
4966
+ },
4967
+ mongodb: {
4968
+ name: "MongoDB",
4969
+ category: "database",
4970
+ viewBox: "0 0 24 24",
4971
+ svgPaths: `<path fill="currentColor" d="M12 2C11.5 3.5 7 10.5 7 15c0 3.5 2.5 6 5 7 2.5-1 5-3.5 5-7 0-4.5-4.5-11.5-5-13zm0 17.5c-1.5-.7-3-2.5-3-4.5 0-2.8 2.2-7 3-9 0.8 2 3 6.2 3 9 0 2-1.5 3.8-3 4.5z"/>`,
4972
+ brandColor: "#47A248"
4973
+ },
4974
+ cassandra: {
4975
+ name: "Apache Cassandra",
4976
+ category: "database",
4977
+ viewBox: "0 0 24 24",
4978
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 18a8 8 0 1 1 8-8 8 8 0 0 1-8 8zm-4-9h8v2H8z"/>`,
4979
+ brandColor: "#1287B1"
4980
+ },
4981
+ sqlite: {
4982
+ name: "SQLite",
4983
+ category: "database",
4984
+ viewBox: "0 0 24 24",
4985
+ svgPaths: `<path fill="currentColor" d="M12 2C6.48 2 2 4.69 2 8v8c0 3.31 4.48 6 10 6s10-2.69 10-6V8c0-3.31-4.48-6-10-6zm0 2c4.42 0 8 1.79 8 4s-3.58 4-8 4-8-1.79-8-4 3.58-4 8-4zm0 16c-4.42 0-8-1.79-8-4v-2.38c2.08 1.47 5.04 2.38 8 2.38s5.92-.91 8-2.38V16c0 2.21-3.58 4-8 4z"/>`,
4986
+ brandColor: "#003B57"
4987
+ },
4988
+ kafka: {
4989
+ name: "Apache Kafka",
4990
+ category: "messaging",
4991
+ viewBox: "0 0 24 24",
4992
+ svgPaths: `<path fill="currentColor" d="M12 3a9 9 0 1 0 9 9 9 9 0 0 0-9-9zm0 16a7 7 0 1 1 7-7 7 7 0 0 1-7 7zm-3-8a2 2 0 1 0-2-2 2 2 0 0 0 2 2zm6 0a2 2 0 1 0-2-2 2 2 0 0 0 2 2zm-3 6a2 2 0 1 0-2-2 2 2 0 0 0 2 2z"/>`,
4993
+ brandColor: "#231F20"
4994
+ },
4995
+ rabbitmq: {
4996
+ name: "RabbitMQ",
4997
+ category: "messaging",
4998
+ viewBox: "0 0 24 24",
4999
+ svgPaths: `<path fill="currentColor" d="M12 2a5 5 0 0 0-5 5v1H5a3 3 0 0 0-3 3v7a4 4 0 0 0 4 4h12a4 4 0 0 0 4-4v-7a3 3 0 0 0-3-3h-2V7a5 5 0 0 0-5-5zm-3 6a3 3 0 0 1 6 0v1H9V8zm-3 5h2v2H6v-2zm12 0h2v2h-2v-2z"/>`,
5000
+ brandColor: "#FF6600"
5001
+ },
5002
+ elasticsearch: {
5003
+ name: "Elasticsearch",
5004
+ category: "database",
5005
+ viewBox: "0 0 24 24",
5006
+ svgPaths: `<path fill="currentColor" d="M12 2A10 10 0 1 0 22 12 10 10 0 0 0 12 2zm-1 3.1a6.9 6.9 0 0 1 5.5 2.9h-11A6.9 6.9 0 0 1 11 5.1zM5.1 12c0-.7.1-1.3.3-2h13.2c.2.7.3 1.3.3 2s-.1 1.3-.3 2H5.4c-.2-.7-.3-1.3-.3-2zm5.9 6.9a6.9 6.9 0 0 1-5.5-2.9h11a6.9 6.9 0 0 1-5.5 2.9z"/>`,
5007
+ brandColor: "#005571"
5008
+ },
5009
+ // Compute, Gateway & Runtimes
5010
+ nodejs: {
5011
+ name: "Node.js",
5012
+ category: "runtime",
5013
+ viewBox: "0 0 24 24",
5014
+ svgPaths: `<path fill="currentColor" d="M12 2L3.5 7v10L12 22l8.5-5V7L12 2zm6.5 13.7L12 19.5l-6.5-3.8V8.3L12 4.5l6.5 3.8v7.4z"/>`,
5015
+ brandColor: "#339933"
5016
+ },
5017
+ python: {
5018
+ name: "Python",
5019
+ category: "runtime",
5020
+ viewBox: "0 0 24 24",
5021
+ svgPaths: `<path fill="currentColor" d="M11.9 2c-3.1 0-4.9.4-4.9 2.2V6h5v1.5H5.8C3.7 7.5 2 9.2 2 11.3c0 2.2 1.4 3.7 3.8 3.7h1.4v-1.9c0-1.8 1.6-3.3 3.5-3.3h5.2V7.7C15.9 4.1 14.8 2 11.9 2zM9 4.2a.8.8 0 1 1 0 1.6.8.8 0 0 1 0-1.6zm3.1 17.8c3.1 0 4.9-.4 4.9-2.2V18h-5v-1.5h6.2c2.1 0 3.8-1.7 3.8-3.8 0-2.2-1.4-3.7-3.8-3.7h-1.4v1.9c0 1.8-1.6 3.3-3.5 3.3H8.6v2.1c0 3.6 1.1 5.7 4.5 5.7zM15 18.2a.8.8 0 1 1 0 1.6.8.8 0 0 1 0-1.6z"/>`,
5022
+ brandColor: "#3776AB"
5023
+ },
5024
+ golang: {
5025
+ name: "Go",
5026
+ category: "runtime",
5027
+ viewBox: "0 0 24 24",
5028
+ svgPaths: `<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4 11h-3v2h3v2h-5v-6h5v2zm-7-2H7v-2h2v2z"/>`,
5029
+ brandColor: "#00ADD8"
5030
+ },
5031
+ rust: {
5032
+ name: "Rust",
5033
+ category: "runtime",
5034
+ viewBox: "0 0 24 24",
5035
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm1 14.5l-2-3h-1v3H8V7.5h4a3 3 0 0 1 3 3 3 3 0 0 1-2 2.8l2 3.2zm-1-6.5h-2v2h2a1 1 0 0 0 1-1 1 1 0 0 0-1-1z"/>`,
5036
+ brandColor: "#DEA584"
5037
+ },
5038
+ nginx: {
5039
+ name: "Nginx",
5040
+ category: "gateway",
5041
+ viewBox: "0 0 24 24",
5042
+ svgPaths: `<path fill="currentColor" d="M12 2L2 7.8v8.4L12 22l10-5.8V7.8L12 2zm-4 13.5V8.5l3 3.5v3.5l-3-3.5zm8 0l-3-3.5V8.5l3 3.5v3.5z"/>`,
5043
+ brandColor: "#009639"
5044
+ },
5045
+ envoy: {
5046
+ name: "Envoy Proxy",
5047
+ category: "gateway",
5048
+ viewBox: "0 0 24 24",
5049
+ svgPaths: `<path fill="currentColor" d="M12 2L2 7v10l10 5 10-5V7L12 2zm0 2.2L19.5 8 12 11.8 4.5 8 12 4.2zM4 9.5l7 3.5v7.2L4 16.7V9.5zm16 7.2l-7 3.5V13l7-3.5v7.2z"/>`,
5050
+ brandColor: "#BF360C"
5051
+ },
5052
+ graphql: {
5053
+ name: "GraphQL",
5054
+ category: "gateway",
5055
+ viewBox: "0 0 24 24",
5056
+ svgPaths: `<path fill="currentColor" d="M12 2l8.66 5v10L12 22l-8.66-5V7L12 2zm0 2.4L5.5 8.1v7.8L12 19.6l6.5-3.7V8.1L12 4.4zM12 7a2 2 0 1 1 0 4 2 2 0 0 1 0-4zm0 6a2 2 0 1 1 0 4 2 2 0 0 1 0-4z"/>`,
5057
+ brandColor: "#E10098"
5058
+ },
5059
+ // Clients & Browsers
5060
+ chrome: {
5061
+ name: "Google Chrome",
5062
+ category: "client",
5063
+ viewBox: "0 0 24 24",
5064
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 3.6a6.4 6.4 0 0 1 5.48 3.1h-5.48a3.3 3.3 0 0 0-3.1 2.2L6.16 6.16A6.36 6.36 0 0 1 12 5.6zm-6.4 6.4a6.4 6.4 0 0 1 .52-2.54l2.74 4.74a3.3 3.3 0 0 0 3.1 1.76v4.6A6.4 6.4 0 0 1 5.6 12zm6.4 6.4a6.36 6.36 0 0 1-4.74-2.14l2.74-4.74a3.3 3.3 0 0 0 2 0l2.74 4.74A6.36 6.36 0 0 1 12 18.4zm0-4.4a2 2 0 1 1 2-2 2 2 0 0 1-2 2z"/>`,
5065
+ brandColor: "#4285F4"
5066
+ },
5067
+ terminal: {
5068
+ name: "Terminal",
5069
+ category: "client",
5070
+ viewBox: "0 0 24 24",
5071
+ svgPaths: `<path fill="currentColor" d="M20 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 14H4V8h16v10zm-12-3l3-3-3-3 1.41-1.41L12.83 12l-3.42 3.41L8 15zm6 0h4v2h-4v-2z"/>`,
5072
+ brandColor: "#4ADE80"
5073
+ },
5074
+ // Security & Identity
5075
+ vault: {
5076
+ name: "HashiCorp Vault",
5077
+ category: "security",
5078
+ viewBox: "0 0 24 24",
5079
+ svgPaths: `<path fill="currentColor" d="M12 2L4 6v6c0 5.55 3.84 10.74 8 12 4.16-1.26 8-6.45 8-12V6l-8-4zm0 6a3 3 0 0 1 3 3c0 1.3-.84 2.4-2 2.82V17h-2v-3.18A3 3 0 0 1 9 11a3 3 0 0 1 3-3z"/>`,
5080
+ brandColor: "#000000"
5081
+ },
5082
+ opa: {
5083
+ name: "Open Policy Agent",
5084
+ category: "security",
5085
+ viewBox: "0 0 24 24",
5086
+ svgPaths: `<path fill="currentColor" d="M12 2L3 7v10l9 5 9-5V7l-9-5zm0 2.3l6.7 3.7v7.4L12 19.1 5.3 15.4V8l6.7-3.7zM12 7a5 5 0 1 0 5 5 5 5 0 0 0-5-5z"/>`,
5087
+ brandColor: "#5B7382"
5088
+ },
5089
+ keycloak: {
5090
+ name: "Keycloak",
5091
+ category: "security",
5092
+ viewBox: "0 0 24 24",
5093
+ svgPaths: `<path fill="currentColor" d="M12 2a5 5 0 0 0-5 5v3H5a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-2V7a5 5 0 0 0-5-5zm-3 5a3 3 0 0 1 6 0v3H9V7zm3 6a2 2 0 0 1 2 2v2a2 2 0 0 1-4 0v-2a2 2 0 0 1 2-2z"/>`,
5094
+ brandColor: "#0088CE"
5095
+ },
5096
+ // Observability & SRE
5097
+ prometheus: {
5098
+ name: "Prometheus",
5099
+ category: "observability",
5100
+ viewBox: "0 0 24 24",
5101
+ svgPaths: `<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 14h-2v-6h2v6zm0-8h-2V6h2v2z"/>`,
5102
+ brandColor: "#E6522C"
5103
+ },
5104
+ datadog: {
5105
+ name: "Datadog",
5106
+ category: "observability",
5107
+ viewBox: "0 0 24 24",
5108
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm4.5 14H15v-3h-2v3h-1.5v-6H13v1.5h2V10h1.5v6zm-7 0H8v-6h3a2 2 0 0 1 2 2v2a2 2 0 0 1-2 2H9.5zm0-4.5V14H11a.5.5 0 0 0 .5-.5v-1a.5.5 0 0 0-.5-.5H9.5z"/>`,
5109
+ brandColor: "#632CA6"
5110
+ },
5111
+ jaeger: {
5112
+ name: "Jaeger Tracing",
5113
+ category: "observability",
5114
+ viewBox: "0 0 24 24",
5115
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm-1 5h2v6h-2zm0 8h2v2h-2z"/>`,
5116
+ brandColor: "#60D0E4"
5117
+ },
5118
+ grafana: {
5119
+ name: "Grafana",
5120
+ category: "observability",
5121
+ viewBox: "0 0 24 24",
5122
+ svgPaths: `<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 14.93c-2.82-.41-5-2.85-5-5.93 0-.82.16-1.6.46-2.31l3.07 3.07c-.12.4-.19.82-.19 1.25 0 1.66 1.34 3 3 3 .43 0 .85-.07 1.25-.19l-2.59-2.59zM16.5 12c0 1.93-1.12 3.6-2.76 4.38l-4.12-4.12A4.47 4.47 0 0 1 12 7.5c2.48 0 4.5 2.02 4.5 4.5z"/>`,
5123
+ brandColor: "#F46800"
5124
+ },
5125
+ pagerduty: {
5126
+ name: "PagerDuty",
5127
+ category: "observability",
5128
+ viewBox: "0 0 24 24",
5129
+ svgPaths: `<path fill="currentColor" d="M5 3h7a6 6 0 0 1 6 6 6 6 0 0 1-6 6H9v6H5V3zm4 8h3a2 2 0 0 0 2-2 2 2 0 0 0-2-2H9v4z"/>`,
5130
+ brandColor: "#04AC38"
5131
+ },
5132
+ slack: {
5133
+ name: "Slack",
5134
+ category: "messaging",
5135
+ viewBox: "0 0 24 24",
5136
+ svgPaths: `<path fill="currentColor" d="M6 15a2 2 0 1 1-2-2h2v2zm1 0a2 2 0 0 1 2-2 2 2 0 0 1 2 2v5a2 2 0 1 1-4 0v-5zm2-8a2 2 0 1 1-2-2 2 2 0 0 1 2 2v2zm0 1a2 2 0 0 1 2 2 2 2 0 0 1-2 2H4a2 2 0 1 1 0-4h5zm8 2a2 2 0 1 1 2 2h-2v-2zm-1 0a2 2 0 0 1-2 2 2 2 0 0 1-2-2V5a2 2 0 1 1 4 0v5zm-2 8a2 2 0 1 1 2 2 2 2 0 0 1-2-2v-2zm0-1a2 2 0 0 1-2-2 2 2 0 0 1 2-2h5a2 2 0 1 1 0 4h-5z"/>`,
5137
+ brandColor: "#4A154B"
5138
+ },
5139
+ // Big Data, Lakehouse & AI
5140
+ spark: {
5141
+ name: "Apache Spark",
5142
+ category: "data",
5143
+ viewBox: "0 0 24 24",
5144
+ svgPaths: `<path fill="currentColor" d="M12 2l2.4 7.4h7.6l-6.2 4.5 2.4 7.4-6.2-4.5-6.2 4.5 2.4-7.4-6.2-4.5h7.6z"/>`,
5145
+ brandColor: "#E25A1C"
5146
+ },
5147
+ flink: {
5148
+ name: "Apache Flink",
5149
+ category: "data",
5150
+ viewBox: "0 0 24 24",
5151
+ svgPaths: `<path fill="currentColor" d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm1 15h-2v-5h2zm0-7h-2V8h2z"/>`,
5152
+ brandColor: "#E6522C"
5153
+ },
5154
+ snowflake: {
5155
+ name: "Snowflake",
5156
+ category: "data",
5157
+ viewBox: "0 0 24 24",
5158
+ svgPaths: `<path fill="currentColor" d="M12 2v20m-7.07-2.93l14.14-14.14M2 12h20M4.93 4.93l14.14 14.14" stroke="currentColor" stroke-width="2" fill="none"/>`,
5159
+ brandColor: "#29B5E8"
5160
+ },
5161
+ delta: {
5162
+ name: "Delta Lake",
5163
+ category: "data",
5164
+ viewBox: "0 0 24 24",
5165
+ svgPaths: `<path fill="currentColor" d="M12 2L2 22h20L12 2zm0 5l6.5 13H5.5L12 7z"/>`,
5166
+ brandColor: "#00A4E4"
5167
+ },
5168
+ superset: {
5169
+ name: "Apache Superset",
5170
+ category: "data",
5171
+ viewBox: "0 0 24 24",
5172
+ svgPaths: `<path fill="currentColor" d="M3 3h4v18H3zm7 6h4v12h-4zm7-4h4v16h-4z"/>`,
5173
+ brandColor: "#20A6B2"
5174
+ },
5175
+ gemini: {
5176
+ name: "Google Gemini",
5177
+ category: "compute",
5178
+ viewBox: "0 0 24 24",
5179
+ svgPaths: `<path fill="currentColor" d="M12 2C12 7.52 7.52 12 2 12c5.48 0 9.95 4.48 10 10 .05-5.52 4.52-10 10-10-5.48 0-9.95-4.48-10-10z"/>`,
5180
+ brandColor: "#8E75FF"
5181
+ }
5182
+ };
5183
+ function resolveVectorSymbol(nameOrAlias) {
5184
+ if (!nameOrAlias || typeof nameOrAlias !== "string") return null;
5185
+ const key = nameOrAlias.trim().toLowerCase().replace(/[\s_.-]+/g, "");
5186
+ if (VECTOR_SYMBOLS[key]) return VECTOR_SYMBOLS[key];
5187
+ const aliases = {
5188
+ postgres: "postgresql",
5189
+ pg: "postgresql",
5190
+ k8s: "kubernetes",
5191
+ kube: "kubernetes",
5192
+ cf: "cloudflare",
5193
+ node: "nodejs",
5194
+ py: "python",
5195
+ go: "golang",
5196
+ elastic: "elasticsearch",
5197
+ es: "elasticsearch",
5198
+ rabbit: "rabbitmq",
5199
+ mq: "rabbitmq",
5200
+ browser: "chrome",
5201
+ web: "chrome",
5202
+ cli: "terminal",
5203
+ console: "terminal",
5204
+ shell: "terminal",
5205
+ awss3: "s3",
5206
+ deltalake: "delta",
5207
+ lakehouse: "delta",
5208
+ llm: "gemini",
5209
+ ai: "gemini"
5210
+ };
5211
+ const target = aliases[key];
5212
+ return target ? VECTOR_SYMBOLS[target] || null : null;
5213
+ }
5214
+ function renderSymbolSvg(symbol, options) {
5215
+ const resolved = typeof symbol === "string" ? resolveVectorSymbol(symbol) : symbol;
5216
+ if (!resolved) return null;
5217
+ const size = options?.size ?? 18;
5218
+ const className = options?.className ? ` class="${options.className}"` : "";
5219
+ const style = options?.color ? ` style="color: ${options.color}"` : "";
5220
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${resolved.viewBox}" width="${size}" height="${size}"${className}${style} aria-hidden="true">${resolved.svgPaths}</svg>`;
5221
+ }
5222
+ function listAvailableSymbols() {
5223
+ return Object.keys(VECTOR_SYMBOLS);
5224
+ }
5225
+
5226
+ // src/provenance.ts
5227
+ var CONTROL_CHAR_RE = /[\u0000-\u001f\u007f]/;
5228
+ function parseCodeAnchor(raw, repositoryUrl, revision) {
5229
+ if (typeof raw !== "string" || !raw.trim()) return null;
5230
+ const trimmed = raw.trim();
5231
+ const ghMatch = trimmed.match(/^(?:https:\/\/github\.com\/[^/]+\/[^/]+\/blob\/([^/]+)\/)([^#]+)(?:#L(\d+)(?:-L(\d+))?)?$/i);
5232
+ if (ghMatch) {
5233
+ const rev = ghMatch[1];
5234
+ const filePath2 = decodeURIComponent(ghMatch[2]);
5235
+ const startLine2 = ghMatch[3] ? parseInt(ghMatch[3], 10) : void 0;
5236
+ const endLine2 = ghMatch[4] ? parseInt(ghMatch[4], 10) : startLine2;
5237
+ return {
5238
+ raw: trimmed,
5239
+ filePath: filePath2,
5240
+ startLine: startLine2,
5241
+ endLine: endLine2,
5242
+ revision: rev,
5243
+ resolvedHref: trimmed
5244
+ };
5245
+ }
5246
+ const [filePathRaw, fragment] = trimmed.split("#");
5247
+ let filePath = filePathRaw.trim().replace(/\\/g, "/");
5248
+ if (filePath.startsWith("./")) {
5249
+ filePath = filePath.slice(2);
5250
+ }
5251
+ if (!filePath || filePath.startsWith("/") || filePath.startsWith("../") || filePath === ".." || filePath.includes("/../") || filePath.endsWith("/..") || CONTROL_CHAR_RE.test(filePath)) {
5252
+ return null;
5253
+ }
5254
+ let startLine;
5255
+ let endLine;
5256
+ if (fragment) {
5257
+ const lineMatch = fragment.match(/^L?(\d+)(?:-L?(\d+))?$/i);
5258
+ if (lineMatch) {
5259
+ startLine = parseInt(lineMatch[1], 10);
5260
+ endLine = lineMatch[2] ? parseInt(lineMatch[2], 10) : startLine;
5261
+ }
5262
+ }
5263
+ let resolvedHref;
5264
+ if (repositoryUrl) {
5265
+ const base = repositoryUrl.replace(/\/$/, "");
5266
+ const rev = revision || "main";
5267
+ const lineFrag = startLine ? `#L${startLine}${endLine && endLine !== startLine ? `-L${endLine}` : ""}` : "";
5268
+ resolvedHref = `${base}/blob/${rev}/${filePath}${lineFrag}`;
5269
+ }
5270
+ return {
5271
+ raw: trimmed,
5272
+ filePath,
5273
+ startLine,
5274
+ endLine,
5275
+ revision,
5276
+ resolvedHref
5277
+ };
5278
+ }
5279
+ function extractDiagramCodeAnchors(ast, repositoryUrl, revision) {
5280
+ const anchors = /* @__PURE__ */ new Map();
5281
+ for (const [nodeId, node] of Object.entries(ast.nodes || {})) {
5282
+ const rawAnchor = node.props["@src"] || node.props["src"] || node.props["@source"] || node.props["source"] || node.props["@code"] || node.props["code"] || node.props["@anchor"] || node.props["anchor"];
5283
+ if (rawAnchor) {
5284
+ const parsed = parseCodeAnchor(rawAnchor, repositoryUrl, revision);
5285
+ if (parsed) {
5286
+ anchors.set(nodeId, parsed);
5287
+ }
5288
+ }
5289
+ }
5290
+ return anchors;
5291
+ }
5292
+ function verifyCodeAnchorsWithReader(anchors, fileReader) {
5293
+ const diagnostics = [];
5294
+ let verifiedCount = 0;
5295
+ for (const [nodeId, anchor] of anchors.entries()) {
5296
+ const { filePath, startLine, endLine } = anchor;
5297
+ const segments = filePath.split("/");
5298
+ if (segments.some((s) => !s || s === "." || s === "..") || segments[0] === ".git") {
5299
+ diagnostics.push({
5300
+ nodeId,
5301
+ severity: "error",
5302
+ code: "provenance/path-escape",
5303
+ message: `Code anchor for node "${nodeId}" must stay within repository and cannot address .git (${filePath}).`,
5304
+ filePath,
5305
+ fixSuggestion: "Remove relative path traversal dots or .git segments."
5306
+ });
5307
+ continue;
5308
+ }
5309
+ if (!fileReader.fileExists(filePath)) {
5310
+ diagnostics.push({
5311
+ nodeId,
5312
+ severity: "error",
5313
+ code: "provenance/file-not-found",
5314
+ message: `Referenced file "${filePath}" does not exist in target repository.`,
5315
+ filePath,
5316
+ fixSuggestion: `Ensure "${filePath}" is committed and relative to repository root.`
5317
+ });
5318
+ continue;
5319
+ }
5320
+ const lineCount = fileReader.getLineCount(filePath);
5321
+ if (startLine !== void 0 && (startLine < 1 || startLine > lineCount)) {
5322
+ diagnostics.push({
5323
+ nodeId,
5324
+ severity: "warning",
5325
+ code: "provenance/line-out-of-bounds",
5326
+ message: `Line #${startLine} exceeds total line count (${lineCount}) of file "${filePath}".`,
5327
+ filePath,
5328
+ line: startLine,
5329
+ fixSuggestion: `Adjust line range to fall within 1..${lineCount}.`
5330
+ });
5331
+ continue;
5332
+ }
5333
+ if (endLine !== void 0 && (endLine < (startLine || 1) || endLine > lineCount)) {
5334
+ diagnostics.push({
5335
+ nodeId,
5336
+ severity: "warning",
5337
+ code: "provenance/line-out-of-bounds",
5338
+ message: `End line #${endLine} is out of bounds for "${filePath}" (total lines: ${lineCount}).`,
5339
+ filePath,
5340
+ line: endLine,
5341
+ fixSuggestion: `Ensure end line is >= start line and <= ${lineCount}.`
5342
+ });
5343
+ continue;
5344
+ }
5345
+ verifiedCount++;
5346
+ }
5347
+ const isValid = diagnostics.filter((d) => d.severity === "error").length === 0;
5348
+ const summaryMarkdown = [
5349
+ `### \u{1F6E1}\uFE0F Code Provenance Verification Report`,
5350
+ `- **Status**: ${isValid ? "\u2705 VERIFIED" : "\u274C FAILED"}`,
5351
+ `- **Total Anchors**: ${anchors.size}`,
5352
+ `- **Verified In-Tree**: ${verifiedCount}`,
5353
+ `- **Diagnostics**: ${diagnostics.length} issue(s)`,
5354
+ ...diagnostics.length > 0 ? [
5355
+ "",
5356
+ `| Node | Severity | Issue | File / Location | Fix |`,
5357
+ `| :--- | :--- | :--- | :--- | :--- |`,
5358
+ ...diagnostics.map(
5359
+ (d) => `| \`${d.nodeId}\` | **${d.severity.toUpperCase()}** | ${d.message} | \`${d.filePath}${d.line ? `:${d.line}` : ""}\` | ${d.fixSuggestion || "N/A"} |`
5360
+ )
5361
+ ] : []
5362
+ ].join("\n");
5363
+ return {
5364
+ isValid,
5365
+ totalAnchors: anchors.size,
5366
+ verifiedCount,
5367
+ anchors,
5368
+ diagnostics,
5369
+ summaryMarkdown
5370
+ };
5371
+ }
4581
5372
 
4582
5373
  // src/syntax-diagnostics.ts
4583
5374
  function damerauLevenshteinDistance(a, b) {
@@ -6291,7 +7082,1593 @@ function formatValue(value) {
6291
7082
  if (typeof value === "string") return value;
6292
7083
  return JSON.stringify(value);
6293
7084
  }
7085
+
7086
+ // src/recipes.ts
7087
+ var ARCHITECTURE_RECIPES = [
7088
+ {
7089
+ id: "cache-aside",
7090
+ name: "Multi-Tier Cache-Aside Architecture",
7091
+ category: "caching",
7092
+ description: "High-performance cache-aside pattern with Redis Cluster, relational database persistence, and asynchronous cache warming.",
7093
+ keywords: ["cache", "redis", "postgres", "cache-aside", "hit", "miss", "warm", "database", "latency"],
7094
+ recommendedLayout: "LR",
7095
+ primaryNodes: ["Client", "Gateway", "URLService", "RedisCluster", "PostgreSQL"],
7096
+ highlights: [
7097
+ "Sub-millisecond read latency on cache hit",
7098
+ "Asynchronous cache population on miss",
7099
+ "Graceful fallback on cache eviction"
7100
+ ],
7101
+ code: `scene "Multi-Tier Cache-Aside Architecture" theme=auto
7102
+ layout LR
7103
+
7104
+ browser Client "Web Client" icon=chrome
7105
+ gateway Gateway "API Gateway" icon=nginx @src="src/gateway/proxy.ts#L12"
7106
+ service URLService "URL Service" icon=nodejs @src="src/services/resolver.ts#L25"
7107
+ cache RedisCluster "Redis Cluster" icon=redis
7108
+ database PostgreSQL "PostgreSQL 16" icon=postgresql @src="src/db/schema.sql#L1"
7109
+
7110
+ beat cache_hit "1. Cache Hit Path":
7111
+ show Client Gateway URLService RedisCluster stagger=50ms
7112
+ frame Client Gateway URLService RedisCluster zoom=1.12
7113
+ Client -> Gateway "GET /link" -> URLService "resolve"
7114
+ URLService -> RedisCluster "GET key:url"
7115
+ URLService <- RedisCluster "200 Target URL"
7116
+ Client <- Gateway "301 Redirect"
7117
+
7118
+ beat cache_miss "2. Cache Miss & Async Warm":
7119
+ show PostgreSQL stagger=50ms
7120
+ frame URLService RedisCluster PostgreSQL zoom=1.15
7121
+ URLService -> PostgreSQL "SELECT dest WHERE key = 'url'"
7122
+ URLService <- PostgreSQL "Row Found"
7123
+ URLService ~> RedisCluster "SETEX key:url (TTL 1h)"
7124
+ glow PostgreSQL color=#38bdf8 & glow RedisCluster color=#22c55e
7125
+ `
7126
+ },
7127
+ {
7128
+ id: "event-driven-eda",
7129
+ name: "Event-Driven EDA with Kafka & Change Data Capture",
7130
+ category: "streaming",
7131
+ description: "Decoupled real-time event streaming pipeline using Kafka/Redpanda with Debezium CDC, Schema Registry, and DLQ handling.",
7132
+ keywords: ["kafka", "event", "streaming", "eda", "cdc", "debezium", "pubsub", "dlq", "consumer"],
7133
+ recommendedLayout: "LR",
7134
+ primaryNodes: ["OrderService", "DebeziumCDC", "KafkaCluster", "NotificationSvc", "AnalyticsConsumer", "DeadLetterQueue"],
7135
+ highlights: [
7136
+ "Zero dual-write penalty with transactional outbox",
7137
+ "Ordered partition publishing with schema validation",
7138
+ "Dead Letter Queue for poison-pill isolation"
7139
+ ],
7140
+ code: `scene "Event-Driven EDA with Kafka & CDC" theme=midnight
7141
+ layout LR
7142
+
7143
+ service OrderSvc "Order Service" icon=golang @src="src/orders/handler.go#L40"
7144
+ database OrderDB "Order Store" icon=postgresql
7145
+ service DebeziumCDC "Debezium CDC" icon=docker
7146
+ queue KafkaCluster "Kafka Event Stream" icon=kafka
7147
+ service NotificationSvc "Notification Engine" icon=nodejs
7148
+ service AnalyticsConsumer "Real-Time Analytics" icon=python
7149
+ queue DLQ "Dead Letter Queue" icon=rabbitmq
7150
+
7151
+ beat order_outbox "1. Transactional Outbox Commit":
7152
+ show OrderSvc OrderDB DebeziumCDC KafkaCluster stagger=50ms
7153
+ OrderSvc -> OrderDB "COMMIT (Order + Outbox Event)"
7154
+ OrderDB -> DebeziumCDC "WAL Stream"
7155
+ DebeziumCDC ~> KafkaCluster "Publish order.created"
7156
+
7157
+ beat consumer_fanout "2. Real-Time Consumer Fanout":
7158
+ show NotificationSvc AnalyticsConsumer DLQ stagger=50ms
7159
+ KafkaCluster ~> NotificationSvc "Consume order.created"
7160
+ KafkaCluster ~> AnalyticsConsumer "Consume order.created"
7161
+ NotificationSvc -> DLQ "Routing Reject (Retries Exceeded)"
7162
+ glow KafkaCluster color=#fbbf24 & glow DLQ color=#fb7185
7163
+ `
7164
+ },
7165
+ {
7166
+ id: "cqrs-event-sourcing",
7167
+ name: "CQRS & Event Sourcing Architecture",
7168
+ category: "streaming",
7169
+ description: "Strict Command Query Responsibility Segregation with immutable append-only Event Store and read-optimized query projections.",
7170
+ keywords: ["cqrs", "event sourcing", "event store", "projection", "read model", "command", "query"],
7171
+ recommendedLayout: "LR",
7172
+ primaryNodes: ["CommandAPI", "CommandHandler", "EventStore", "ProjectionEngine", "ReadDB", "QueryAPI"],
7173
+ highlights: [
7174
+ "Complete auditability via append-only event stream",
7175
+ "Independent write-scaling and read-scaling",
7176
+ "Zero lock contention between queries and mutations"
7177
+ ],
7178
+ code: `scene "CQRS & Event Sourcing Architecture" theme=blueprint
7179
+ layout LR
7180
+
7181
+ gateway CommandAPI "Command API" icon=nginx
7182
+ service CommandHandler "Command Handler" icon=golang @src="src/commands/execute.go#L18"
7183
+ database EventStore "Event Store (Append-Only)" icon=cassandra
7184
+ service ProjectionEngine "Projection Engine" icon=rust @src="src/projections/sync.rs#L30"
7185
+ database ReadDB "Read Database" icon=mongodb
7186
+ gateway QueryAPI "Query API" icon=nodejs
7187
+
7188
+ beat write_command "1. Command & Append Event":
7189
+ show CommandAPI CommandHandler EventStore stagger=50ms
7190
+ CommandAPI -> CommandHandler "POST /orders/create"
7191
+ CommandHandler -> EventStore "APPEND OrderCreatedEvent"
7192
+ CommandHandler <- EventStore "Event Ack (Offset: 10482)"
7193
+ CommandAPI <- CommandHandler "202 Accepted"
7194
+
7195
+ beat async_projection "2. Asynchronous Projection Update":
7196
+ show ProjectionEngine ReadDB QueryAPI stagger=50ms
7197
+ EventStore ~> ProjectionEngine "Tail Commit Log"
7198
+ ProjectionEngine -> ReadDB "UPSERT Materialized Order View"
7199
+ QueryAPI -> ReadDB "SELECT * FROM orders WHERE id = :id"
7200
+ QueryAPI <- ReadDB "Read Model Payload"
7201
+ `
7202
+ },
7203
+ {
7204
+ id: "api-gateway-mesh",
7205
+ name: "Cloud-Native API Gateway & Service Mesh",
7206
+ category: "microservices",
7207
+ description: "Enterprise zero-trust microservices architecture with Envoy/Istio service mesh, mTLS enforcement, and distributed tracing.",
7208
+ keywords: ["gateway", "mesh", "envoy", "istio", "microservices", "mtls", "kubernetes", "discovery"],
7209
+ recommendedLayout: "LR",
7210
+ primaryNodes: ["IngressGateway", "AuthService", "OrderMeshSvc", "PaymentMeshSvc", "InventoryMeshSvc", "JaegerCollector"],
7211
+ highlights: [
7212
+ "Strict mTLS identity verification between sidecars",
7213
+ "Global rate limiting and distributed OpenTelemetry spans",
7214
+ "Dynamic circuit breaking and automated retries"
7215
+ ],
7216
+ code: `scene "Cloud-Native API Gateway & Service Mesh" theme=graphite
7217
+ layout LR
7218
+
7219
+ gateway IngressGateway "Envoy Ingress Gateway" icon=envoy @src="k8s/gateway.yaml#L1"
7220
+ service AuthService "Auth & Token Service" icon=nodejs @src="src/auth/token.ts#L44"
7221
+ service OrderMeshSvc "Order Service (mTLS)" icon=golang
7222
+ service PaymentMeshSvc "Payment Gateway (mTLS)" icon=nodejs
7223
+ service InventoryMeshSvc "Inventory Service (mTLS)" icon=python
7224
+ service JaegerCollector "OpenTelemetry Collector" icon=jaeger
7225
+
7226
+ beat ingress_auth "1. Edge Authentication & Route Verification":
7227
+ show IngressGateway AuthService JaegerCollector stagger=50ms
7228
+ IngressGateway -> AuthService "Validate JWT Bearer"
7229
+ IngressGateway <- AuthService "Claims Verified"
7230
+ IngressGateway ~> JaegerCollector "Span: ingress_entry"
7231
+
7232
+ beat internal_mesh_flow "2. Internal mTLS Mesh Fanout":
7233
+ show OrderMeshSvc PaymentMeshSvc InventoryMeshSvc stagger=50ms
7234
+ IngressGateway -> OrderMeshSvc "POST /checkout (mTLS)"
7235
+ OrderMeshSvc -> PaymentMeshSvc "POST /charge (mTLS)"
7236
+ OrderMeshSvc -> InventoryMeshSvc "POST /reserve (mTLS)"
7237
+ PaymentMeshSvc ~> JaegerCollector "Span: payment_settled"
7238
+ InventoryMeshSvc ~> JaegerCollector "Span: inventory_reserved"
7239
+ glow IngressGateway color=#38bdf8 & glow OrderMeshSvc color=#22c55e
7240
+ `
7241
+ },
7242
+ {
7243
+ id: "zero-trust-security",
7244
+ name: "Zero-Trust Security & Enclave Perimeter",
7245
+ category: "security",
7246
+ description: "Defense-in-depth zero-trust security perimeter featuring OIDC authentication, Open Policy Agent authorization, and Nitro Enclaves.",
7247
+ keywords: ["security", "zero-trust", "oidc", "opa", "policy", "enclave", "encryption", "vault", "waf"],
7248
+ recommendedLayout: "LR",
7249
+ primaryNodes: ["CloudflareWAF", "IdentityOIDC", "PolicyEngineOPA", "KeyVault", "SecureEnclave", "AuditLogStore"],
7250
+ highlights: [
7251
+ "Continuous runtime identity verification on every invocation",
7252
+ "Confidential computing in isolated CPU Nitro enclaves",
7253
+ "Immutable write-once cryptographic audit trail"
7254
+ ],
7255
+ code: `scene "Zero-Trust Security & Enclave Perimeter" theme=midnight
7256
+ layout LR
7257
+
7258
+ gateway CloudflareWAF "Cloudflare Edge WAF" icon=cloudflare
7259
+ service IdentityOIDC "Identity Provider (OIDC)" icon=keycloak
7260
+ service PolicyEngineOPA "Policy Engine (OPA)" icon=opa @src="policies/authz.rego#L1"
7261
+ service KeyVault "HashiCorp Vault" icon=vault @src="config/vault.hcl#L10"
7262
+ service SecureEnclave "AWS Nitro Enclave" icon=aws
7263
+ database AuditLogStore "WORM Audit Store" icon=s3
7264
+
7265
+ beat access_request "1. Identity & Policy Evaluation":
7266
+ show CloudflareWAF IdentityOIDC PolicyEngineOPA stagger=50ms
7267
+ CloudflareWAF -> IdentityOIDC "Authenticate Request"
7268
+ CloudflareWAF <- IdentityOIDC "Token Issued"
7269
+ CloudflareWAF -> PolicyEngineOPA "Evaluate RBAC/ABAC Context"
7270
+ CloudflareWAF <- PolicyEngineOPA "Decision: ALLOW"
7271
+
7272
+ beat confidential_execution "2. Enclave Decryption & Audit":
7273
+ show SecureEnclave KeyVault AuditLogStore stagger=50ms
7274
+ CloudflareWAF -> SecureEnclave "Execute Protected Payload"
7275
+ SecureEnclave -> KeyVault "Request Ephemeral Decryption Key"
7276
+ SecureEnclave <- KeyVault "Key Granted"
7277
+ SecureEnclave ~> AuditLogStore "Cryptographic Audit Receipt"
7278
+ glow SecureEnclave color=#fb7185 & glow KeyVault color=#a78bfa
7279
+ `
7280
+ },
7281
+ {
7282
+ id: "medallion-lakehouse",
7283
+ name: "Medallion Data Lakehouse Architecture",
7284
+ category: "data",
7285
+ description: "Modern data engineering pipeline organizing raw, cleansed, and curated data across Bronze, Silver, and Gold tiers.",
7286
+ keywords: ["data", "lakehouse", "medallion", "bronze", "silver", "gold", "spark", "delta", "iceberg", "analytics"],
7287
+ recommendedLayout: "LR",
7288
+ primaryNodes: ["RawIngestKafka", "BronzeLake", "SparkCleansing", "SilverLake", "FlinkAggregation", "GoldWarehouse", "SupersetBI"],
7289
+ highlights: [
7290
+ "ACID transactions over object storage with Delta/Iceberg",
7291
+ "Multi-stage data quality checks between Bronze and Silver",
7292
+ "Sub-second dimensional queries on Gold warehouse"
7293
+ ],
7294
+ code: `scene "Medallion Data Lakehouse Architecture" theme=editorial
7295
+ layout LR
7296
+
7297
+ queue RawIngestKafka "Raw Event Ingestion" icon=kafka
7298
+ database BronzeLake "Bronze Lake (Raw Ingest)" icon=s3
7299
+ service SparkCleansing "Spark Cleansing Job" icon=spark @src="jobs/cleanse_bronze.py#L15"
7300
+ database SilverLake "Silver Lake (Enriched)" icon=delta
7301
+ service FlinkAggregation "Flink Streaming Aggregator" icon=flink
7302
+ database GoldWarehouse "Gold Warehouse (Curated)" icon=snowflake
7303
+ dashboard SupersetBI "Apache Superset BI" icon=superset
7304
+
7305
+ beat bronze_ingest "1. Raw Stream to Bronze Tier":
7306
+ show RawIngestKafka BronzeLake SparkCleansing stagger=50ms
7307
+ RawIngestKafka -> BronzeLake "Append Raw JSON Payload"
7308
+ BronzeLake -> SparkCleansing "Trigger Micro-Batch"
7309
+
7310
+ beat silver_and_gold "2. Cleansing, Enrichment & BI Serving":
7311
+ show SilverLake FlinkAggregation GoldWarehouse SupersetBI stagger=50ms
7312
+ SparkCleansing -> SilverLake "Upsert Deduplicated Parquet"
7313
+ SilverLake -> FlinkAggregation "Stream Entity Updates"
7314
+ FlinkAggregation -> GoldWarehouse "Merge Into Star Schema"
7315
+ SupersetBI -> GoldWarehouse "Execute Dimensional Query"
7316
+ SupersetBI <- GoldWarehouse "Render Dashboard Metrics"
7317
+ `
7318
+ },
7319
+ {
7320
+ id: "agentic-react-tools",
7321
+ name: "Agentic AI Orchestrator & Tool Execution Loop",
7322
+ category: "ai",
7323
+ description: "Autonomous ReAct agent system with LLM Orchestrator, dynamic context memory, vector embeddings, and MCP tool execution.",
7324
+ keywords: ["ai", "agent", "llm", "react", "tool", "mcp", "vector", "rag", "orchestrator", "prompt"],
7325
+ recommendedLayout: "LR",
7326
+ primaryNodes: ["UserClient", "AgentOrchestrator", "VectorMemory", "ModelInference", "MCPToolExecutor", "SandboxRuntime"],
7327
+ highlights: [
7328
+ "Interactive reasoning loop (Thought -> Action -> Observation)",
7329
+ "Hybrid semantic search over vector memory store",
7330
+ "Secure sandboxed runtime for tool call executions"
7331
+ ],
7332
+ code: `scene "Agentic AI Orchestrator & Tool Loop" theme=nebula
7333
+ layout LR
7334
+
7335
+ browser UserClient "User Workspace" icon=terminal
7336
+ service AgentOrchestrator "ReAct Agent Orchestrator" icon=python @src="agent/core.py#L35"
7337
+ database VectorMemory "Vector Memory (RAG)" icon=redis
7338
+ service ModelInference "LLM Inference API" icon=gemini
7339
+ service MCPToolExecutor "MCP Tool Protocol" icon=docker @src="agent/mcp_client.py#L20"
7340
+ service SandboxRuntime "Secure Container Sandbox" icon=docker
7341
+
7342
+ beat agent_thought "1. Plan & Context Retrieval":
7343
+ show UserClient AgentOrchestrator VectorMemory ModelInference stagger=50ms
7344
+ UserClient -> AgentOrchestrator "Goal: Deploy microservice"
7345
+ AgentOrchestrator -> VectorMemory "Query Relevant Runbooks"
7346
+ AgentOrchestrator <- VectorMemory "Runbook Context Vectors"
7347
+ AgentOrchestrator -> ModelInference "Generate Plan & Tool Call"
7348
+ AgentOrchestrator <- ModelInference "Call: run_command(kubectl apply)"
7349
+
7350
+ beat tool_execution "2. MCP Tool Execution & Observation":
7351
+ show MCPToolExecutor SandboxRuntime stagger=50ms
7352
+ AgentOrchestrator -> MCPToolExecutor "Execute Tool Request"
7353
+ MCPToolExecutor -> SandboxRuntime "Spawn Container & Execute"
7354
+ MCPToolExecutor <- SandboxRuntime "Output: deployment created"
7355
+ AgentOrchestrator <- MCPToolExecutor "Observation Receipt"
7356
+ UserClient <- AgentOrchestrator "Goal Achieved: Deployed successfully"
7357
+ glow AgentOrchestrator color=#c4b5fd & glow MCPToolExecutor color=#67e8f9
7358
+ `
7359
+ },
7360
+ {
7361
+ id: "active-active-failover",
7362
+ name: "Multi-Region Active-Active Resilient Failover",
7363
+ category: "resilience",
7364
+ description: "High-availability global architecture with GeoDNS latency routing, multi-region cluster active-active syncing, and automated failover.",
7365
+ keywords: ["resilience", "active-active", "failover", "multi-region", "disaster recovery", "replication", "dns", "ha"],
7366
+ recommendedLayout: "LR",
7367
+ primaryNodes: ["GlobalDNS", "RegionUSEast", "DBPrimaryEast", "RegionEUWest", "DBPrimaryWest", "HealthProbe"],
7368
+ highlights: [
7369
+ "Sub-second DNS failover when health probe detects outage",
7370
+ "Bi-directional conflict-free replicated database sync (CRDT)",
7371
+ "Zero downtime during planned regional maintenance"
7372
+ ],
7373
+ code: `scene "Multi-Region Active-Active Failover" theme=midnight
7374
+ layout LR
7375
+
7376
+ gateway GlobalDNS "Global Route53 GeoDNS" icon=aws
7377
+ service RegionUSEast "Region US-East API" icon=kubernetes @src="infra/us-east/app.yaml#L1"
7378
+ database DBPrimaryEast "Aurora Global DB (East)" icon=postgresql
7379
+ service RegionEUWest "Region EU-West API" icon=kubernetes @src="infra/eu-west/app.yaml#L1"
7380
+ database DBPrimaryWest "Aurora Global DB (West)" icon=postgresql
7381
+ service HealthProbe "Global Health Checker" icon=datadog
7382
+
7383
+ beat steady_state "1. Steady-State Geo-Routing & Sync":
7384
+ show GlobalDNS RegionUSEast DBPrimaryEast RegionEUWest DBPrimaryWest stagger=50ms
7385
+ GlobalDNS -> RegionUSEast "Route US Traffic"
7386
+ RegionUSEast -> DBPrimaryEast "Local Read/Write"
7387
+ GlobalDNS -> RegionEUWest "Route EU Traffic"
7388
+ RegionEUWest -> DBPrimaryWest "Local Read/Write"
7389
+ DBPrimaryEast ~> DBPrimaryWest "Cross-Region Stream Replication"
7390
+
7391
+ beat simulated_failover "2. Outage Detection & Instant Failover":
7392
+ show HealthProbe stagger=50ms
7393
+ HealthProbe -> RegionUSEast "HTTP Health Probe (Timeout)"
7394
+ HealthProbe ~> GlobalDNS "Withdraw US-East IP from Pool"
7395
+ GlobalDNS -> RegionEUWest "Reroute 100% Global Traffic"
7396
+ glow RegionEUWest color=#22c55e & glow RegionUSEast color=#fb7185
7397
+ `
7398
+ },
7399
+ {
7400
+ id: "distributed-consensus-raft",
7401
+ name: "Distributed Consensus & Raft Log Replication",
7402
+ category: "consensus",
7403
+ description: "Raft consensus protocol state machine with Leader Election, Heartbeat synchronization, and atomic log commitment.",
7404
+ keywords: ["raft", "consensus", "leader", "follower", "election", "replication", "distributed", "etcd"],
7405
+ recommendedLayout: "LR",
7406
+ primaryNodes: ["ClientApp", "RaftLeader", "RaftFollowerA", "RaftFollowerB", "StateStore"],
7407
+ highlights: [
7408
+ "Guaranteed linearizable reads and writes",
7409
+ "Automated leader reelection on heartbeat loss",
7410
+ "Strict quorum (N/2 + 1) commit guarantees"
7411
+ ],
7412
+ code: `scene "Distributed Consensus Raft Engine" theme=paper
7413
+ layout LR
7414
+
7415
+ browser ClientApp "Client Application" icon=terminal
7416
+ service RaftLeader "Raft Node 1 (Leader)" icon=golang @src="raft/leader.go#L42"
7417
+ service RaftFollowerA "Raft Node 2 (Follower)" icon=golang @src="raft/follower.go#L20"
7418
+ service RaftFollowerB "Raft Node 3 (Follower)" icon=golang @src="raft/follower.go#L20"
7419
+ database StateStore "Committed State Machine" icon=sqlite
7420
+
7421
+ beat propose_entry "1. Client Proposal & Log Replication":
7422
+ show ClientApp RaftLeader RaftFollowerA RaftFollowerB stagger=50ms
7423
+ ClientApp -> RaftLeader "Propose: SET key = 'val'"
7424
+ RaftLeader -> RaftFollowerA "AppendEntries(Term=2, Entry=4)"
7425
+ RaftLeader -> RaftFollowerB "AppendEntries(Term=2, Entry=4)"
7426
+
7427
+ beat quorum_commit "2. Quorum Acknowledgment & State Commit":
7428
+ show StateStore stagger=50ms
7429
+ RaftLeader <- RaftFollowerA "Success Ack"
7430
+ RaftLeader <- RaftFollowerB "Success Ack"
7431
+ RaftLeader -> StateStore "Apply to State Machine"
7432
+ ClientApp <- RaftLeader "200 Commit Acknowledged"
7433
+ glow RaftLeader color=#0284c7 & glow StateStore color=#16a34a
7434
+ `
7435
+ },
7436
+ {
7437
+ id: "incident-runbook",
7438
+ name: "Automated Incident Response & Self-Healing Runbook",
7439
+ category: "observability",
7440
+ description: "Automated site reliability incident workflow: metric threshold breach, PagerDuty alert, automated pod restart, and status page sync.",
7441
+ keywords: ["incident", "sre", "runbook", "pagerduty", "alert", "prometheus", "slack", "self-healing"],
7442
+ recommendedLayout: "LR",
7443
+ primaryNodes: ["PrometheusAlert", "PagerDutyEngine", "K8sAutoHealer", "SlackIncidentBot", "StatusPageSync"],
7444
+ highlights: [
7445
+ "Instant multi-channel incident triaging",
7446
+ "Automated remediation before human on-call escalation",
7447
+ "Zero-latency public status communication"
7448
+ ],
7449
+ code: `scene "Automated Incident Response Runbook" theme=midnight
7450
+ layout LR
7451
+
7452
+ service PrometheusAlert "Prometheus Alertmanager" icon=prometheus @src="alerts/p99_latency.yaml#L1"
7453
+ service PagerDutyEngine "PagerDuty Event Router" icon=pagerduty
7454
+ service K8sAutoHealer "K8s Auto-Remediation" icon=kubernetes @src="runbooks/restart_pod.sh#L5"
7455
+ service SlackIncidentBot "Slack War Room Bot" icon=slack
7456
+ service StatusPageSync "Public Status Page" icon=cloudflare
7457
+
7458
+ beat alert_trigger "1. High Latency P99 Breach":
7459
+ show PrometheusAlert PagerDutyEngine SlackIncidentBot StatusPageSync stagger=50ms
7460
+ PrometheusAlert ~> PagerDutyEngine "TRIGGER: P99 Latency > 1500ms"
7461
+ PagerDutyEngine -> SlackIncidentBot "Spawn #incident-2026-09"
7462
+ PagerDutyEngine -> StatusPageSync "Update: Degraded Performance"
7463
+
7464
+ beat auto_heal "2. Self-Healing Pod Recycle & Resolution":
7465
+ show K8sAutoHealer stagger=50ms
7466
+ PagerDutyEngine -> K8sAutoHealer "Execute Remediation Runbook"
7467
+ K8sAutoHealer -> PrometheusAlert "Verify Latency Normalized (< 200ms)"
7468
+ PagerDutyEngine ~> SlackIncidentBot "Resolved: Auto-healed in 42s"
7469
+ PagerDutyEngine ~> StatusPageSync "Update: All Systems Operational"
7470
+ glow K8sAutoHealer color=#22c55e & glow StatusPageSync color=#38bdf8
7471
+ `
7472
+ },
7473
+ {
7474
+ id: "agentic-multi-swarm",
7475
+ name: "Autonomous Multi-Agent Engineering Swarm",
7476
+ category: "ai",
7477
+ description: "Multi-agent collaborative architecture with Orchestrator Leader, Specialized Coder/Reviewer Subagents, Sandboxed Tool Execution, and Consensus Verification.",
7478
+ keywords: ["agent", "swarm", "multi-agent", "orchestrator", "mcp", "subagent", "sandbox", "ai"],
7479
+ recommendedLayout: "LR",
7480
+ primaryNodes: ["UserLead", "OrchestratorAgent", "CoderSubagent", "ReviewerSubagent", "SandboxRuntime"],
7481
+ highlights: [
7482
+ "Dynamic hierarchical task delegation",
7483
+ "Dual-agent verification & adversarial review",
7484
+ "Isolated sandboxed execution with telemetry"
7485
+ ],
7486
+ code: `scene "Autonomous Multi-Agent Engineering Swarm" theme=graphite
7487
+ layout LR
7488
+
7489
+ browser UserLead "Lead Engineer / IDE" icon=gemini
7490
+ service OrchestratorAgent "Orchestrator Leader" icon=nodejs @src="src/agent/leader.ts#L10"
7491
+ service CoderSubagent "Coder Subagent" icon=typescript @src="src/agent/coder.ts#L15"
7492
+ service ReviewerSubagent "Reviewer Subagent" icon=python @src="src/agent/critic.ts#L20"
7493
+ service SandboxRuntime "Secure Tool Sandbox" icon=docker @src="src/tools/mcp_host.ts#L5"
7494
+
7495
+ beat task_delegation "1. Task Decomposition & Parallel Spawn":
7496
+ show UserLead OrchestratorAgent CoderSubagent ReviewerSubagent stagger=50ms
7497
+ UserLead -> OrchestratorAgent "Prompt: Implement Feature & Tests"
7498
+ OrchestratorAgent -> CoderSubagent "Spawn task: Write TypeScript Implementation"
7499
+ OrchestratorAgent -> ReviewerSubagent "Spawn task: Construct Invariant Quality Gate"
7500
+
7501
+ beat tool_verification "2. Sandboxed Execution & Review Consensus":
7502
+ show SandboxRuntime stagger=50ms
7503
+ CoderSubagent -> SandboxRuntime "Execute unit tests in sandbox"
7504
+ CoderSubagent <- SandboxRuntime "308 tests pass (100%)"
7505
+ ReviewerSubagent -> CoderSubagent "Verify Code Provenance & Zero Regressions"
7506
+ OrchestratorAgent <- ReviewerSubagent "Consensus Approved: Ready for PR"
7507
+ UserLead <- OrchestratorAgent "200 Feature Complete & Verified"
7508
+ glow OrchestratorAgent color=#38bdf8 & glow SandboxRuntime color=#10b981
7509
+ `
7510
+ },
7511
+ {
7512
+ id: "edge-serverless-mesh",
7513
+ name: "Edge-First Serverless & Distributed Vector Mesh",
7514
+ category: "resilience",
7515
+ description: "Ultra-low-latency globally distributed edge architecture with Cloudflare Workers, KV caching, D1 relational store, and Vectorize embedding search.",
7516
+ keywords: ["edge", "cloudflare", "workers", "serverless", "d1", "vector", "embedding", "kv"],
7517
+ recommendedLayout: "LR",
7518
+ primaryNodes: ["GlobalClient", "EdgeWorker", "EdgeKV", "D1Database", "VectorizeStore"],
7519
+ highlights: [
7520
+ "Sub-10ms global edge invocation",
7521
+ "Local relational replication with D1",
7522
+ "Native vector similarity lookup at the edge"
7523
+ ],
7524
+ code: `scene "Edge-First Serverless & Vector Mesh" theme=paper
7525
+ layout LR
7526
+
7527
+ browser GlobalClient "Global Mobile/Web Client" icon=chrome
7528
+ gateway EdgeWorker "Cloudflare Edge Worker" icon=cloudflare @src="src/worker/index.ts#L1"
7529
+ cache EdgeKV "Global KV Cache" icon=redis
7530
+ database D1Database "Cloudflare D1 SQL" icon=postgresql @src="src/db/schema.sql#L10"
7531
+ database VectorizeStore "Vectorize Embedding DB" icon=gemini
7532
+
7533
+ beat edge_lookup "1. Nearest Edge Routing & Cache Hit":
7534
+ show GlobalClient EdgeWorker EdgeKV stagger=50ms
7535
+ GlobalClient -> EdgeWorker "GET /recommendations (Geo: Tokyo)"
7536
+ EdgeWorker -> EdgeKV "GET edge_cache:user_tokyo"
7537
+ EdgeWorker <- EdgeKV "Hit (3ms latency)"
7538
+
7539
+ beat semantic_search "2. Edge Vector Search & D1 Fetch":
7540
+ show VectorizeStore D1Database stagger=50ms
7541
+ EdgeWorker -> VectorizeStore "Query vector topK=5"
7542
+ EdgeWorker <- VectorizeStore "Embedding Matches"
7543
+ EdgeWorker -> D1Database "SELECT metadata FROM products WHERE id IN (...)"
7544
+ EdgeWorker <- D1Database "Product Records"
7545
+ GlobalClient <- EdgeWorker "200 OK (8ms total transit)"
7546
+ glow EdgeWorker color=#f59e0b & glow VectorizeStore color=#ec4899
7547
+ `
7548
+ },
7549
+ {
7550
+ id: "zero-downtime-canary",
7551
+ name: "Zero-Downtime Blue-Green & Canary Deployment",
7552
+ category: "resilience",
7553
+ description: "Progressive delivery traffic routing with Envoy/Ingress, Blue (Stable) vs Green (Canary) cluster weighting, and automated rollback on error spikes.",
7554
+ keywords: ["canary", "blue-green", "deployment", "envoy", "kubernetes", "traffic", "rollback", "zero-downtime"],
7555
+ recommendedLayout: "LR",
7556
+ primaryNodes: ["IngressController", "EnvoyMesh", "BlueCluster", "GreenCanary", "PrometheusWatcher"],
7557
+ highlights: [
7558
+ "Fine-grained 90/10 traffic split",
7559
+ "Zero dropped active connections during migration",
7560
+ "Sub-second automated circuit breaker rollback"
7561
+ ],
7562
+ code: `scene "Zero-Downtime Canary Deployment" theme=terminal
7563
+ layout LR
7564
+
7565
+ browser UserTraffic "Live Production Traffic" icon=chrome
7566
+ gateway EnvoyMesh "Envoy Service Mesh" icon=envoy @src="k8s/envoy-config.yaml#L1"
7567
+ service BlueCluster "Blue Pods (v1.2.0 Stable 90%)" icon=kubernetes @src="deploy/blue.yaml#L1"
7568
+ service GreenCanary "Green Pods (v1.3.0 Canary 10%)" icon=docker @src="deploy/green.yaml#L1"
7569
+ service PrometheusWatcher "Canary Health Sentry" icon=prometheus
7570
+
7571
+ beat canary_routing "1. Weighted Traffic Split (90/10)":
7572
+ show UserTraffic EnvoyMesh BlueCluster GreenCanary stagger=50ms
7573
+ UserTraffic -> EnvoyMesh "Production Request Pool"
7574
+ EnvoyMesh -> BlueCluster "Route 90% Stable"
7575
+ EnvoyMesh -> GreenCanary "Route 10% Canary"
7576
+
7577
+ beat health_verification "2. Automated Sentry Gate & 100% Promotion":
7578
+ show PrometheusWatcher stagger=50ms
7579
+ PrometheusWatcher -> GreenCanary "Monitor Error Rate (< 0.01%) & P99"
7580
+ PrometheusWatcher -> EnvoyMesh "Signal: Canary Healthy -> Shift 100% to Green"
7581
+ EnvoyMesh -> GreenCanary "Promote to 100% Live"
7582
+ glow GreenCanary color=#22c55e & glow BlueCluster color=#64748b
7583
+ `
7584
+ },
7585
+ {
7586
+ id: "opentelemetry-tracing",
7587
+ name: "Full-Stack Distributed Tracing & Observability",
7588
+ category: "observability",
7589
+ description: "End-to-end W3C trace context propagation across Frontend, API Gateway, Microservices, and OpenTelemetry Collector with Jaeger/Grafana visualization.",
7590
+ keywords: ["opentelemetry", "tracing", "jaeger", "grafana", "prometheus", "span", "context", "observability"],
7591
+ recommendedLayout: "LR",
7592
+ primaryNodes: ["WebFrontend", "ApiGateway", "OrderService", "OtelCollector", "JaegerGrafana"],
7593
+ highlights: [
7594
+ "Unified W3C traceparent context injection",
7595
+ "Non-blocking asynchronous telemetry batching",
7596
+ "Unified metrics, logs, and trace correlation"
7597
+ ],
7598
+ code: `scene "Full-Stack Distributed Tracing" theme=editorial
7599
+ layout LR
7600
+
7601
+ browser WebFrontend "Web App (OTel Web SDK)" icon=chrome @src="src/tracing/web.ts#L5"
7602
+ gateway ApiGateway "Kong API Gateway" icon=nginx
7603
+ service OrderService "Order Microservice" icon=golang @src="src/orders/main.go#L30"
7604
+ service OtelCollector "OpenTelemetry Collector" icon=docker @src="otel/collector.yaml#L1"
7605
+ database JaegerGrafana "Jaeger & Grafana Cloud" icon=datadog
7606
+
7607
+ beat trace_propagation "1. Context Injection & Downstream Propagation":
7608
+ show WebFrontend ApiGateway OrderService OtelCollector stagger=50ms
7609
+ WebFrontend -> ApiGateway "POST /checkout [traceparent: 00-4bf92...]"
7610
+ ApiGateway -> OrderService "Forward [traceparent: 00-4bf92...]"
7611
+ WebFrontend ~> OtelCollector "Async Span: browser_render (42ms)"
7612
+
7613
+ beat collector_export "2. OTLP gRPC Batch Ingestion & Indexing":
7614
+ show JaegerGrafana stagger=50ms
7615
+ ApiGateway ~> OtelCollector "Async Span: gateway_auth (12ms)"
7616
+ OrderService ~> OtelCollector "Async Span: db_transaction (88ms)"
7617
+ OtelCollector -> JaegerGrafana "Export OTLP Batch (Traces + Metrics)"
7618
+ glow OtelCollector color=#38bdf8 & glow JaegerGrafana color=#ec4899
7619
+ `
7620
+ }
7621
+ ];
7622
+ function recommendArchitecturePattern(query) {
7623
+ const normalized = query.toLowerCase();
7624
+ const queryTokens = normalized.split(/[\s,._\-:;/?!]+/).filter(Boolean);
7625
+ const results = [];
7626
+ for (const recipe of ARCHITECTURE_RECIPES) {
7627
+ let score = 0;
7628
+ const matched = [];
7629
+ if (normalized.includes(recipe.category)) {
7630
+ score += 10;
7631
+ matched.push(`category:${recipe.category}`);
7632
+ }
7633
+ if (normalized.includes(recipe.id.replace(/-/g, " "))) {
7634
+ score += 25;
7635
+ matched.push(recipe.id);
7636
+ }
7637
+ for (const kw of recipe.keywords) {
7638
+ if (normalized.includes(kw)) {
7639
+ score += 8;
7640
+ if (!matched.includes(kw)) matched.push(kw);
7641
+ }
7642
+ }
7643
+ const descTokens = recipe.description.toLowerCase().split(/\W+/);
7644
+ for (const token of queryTokens) {
7645
+ if (token.length > 2 && descTokens.includes(token)) {
7646
+ score += 3;
7647
+ }
7648
+ }
7649
+ if (score > 0) {
7650
+ results.push({
7651
+ recipe,
7652
+ score,
7653
+ matchedKeywords: matched,
7654
+ rationale: `Matched ${matched.length} key attributes: ${matched.join(", ")} (Score: ${score})`
7655
+ });
7656
+ }
7657
+ }
7658
+ results.sort((a, b) => b.score - a.score);
7659
+ if (results.length === 0 && ARCHITECTURE_RECIPES.length > 0) {
7660
+ results.push({
7661
+ recipe: ARCHITECTURE_RECIPES[0],
7662
+ score: 1,
7663
+ matchedKeywords: ["default"],
7664
+ rationale: "Default canonical cache-aside architecture blueprint"
7665
+ });
7666
+ }
7667
+ return results;
7668
+ }
7669
+ function synthesizeCustomRecipe(query) {
7670
+ const text = query.toLowerCase();
7671
+ const detected = [];
7672
+ if (text.includes("next") || text.includes("nextjs") || text.includes("react") || text.includes("web")) {
7673
+ detected.push({ id: "NextApp", label: "Next.js Web Client", kind: "browser", icon: "chrome" });
7674
+ } else if (text.includes("mobile") || text.includes("ios") || text.includes("android") || text.includes("flutter")) {
7675
+ detected.push({ id: "MobileApp", label: "Mobile Client Application", kind: "mobile", icon: "chrome" });
7676
+ } else {
7677
+ detected.push({ id: "ClientApp", label: "Client Application", kind: "browser", icon: "chrome" });
7678
+ }
7679
+ if (text.includes("cloudflare") || text.includes("edge") || text.includes("cdn")) {
7680
+ detected.push({ id: "CloudflareEdge", label: "Cloudflare Edge Ingress", kind: "gateway", icon: "cloudflare" });
7681
+ } else if (text.includes("nginx") || text.includes("envoy") || text.includes("kong") || text.includes("gateway")) {
7682
+ detected.push({ id: "ApiGateway", label: "API Gateway & Ingress", kind: "gateway", icon: "nginx" });
7683
+ }
7684
+ if (text.includes("stripe") || text.includes("payment") || text.includes("checkout")) {
7685
+ detected.push({ id: "StripeGateway", label: "Stripe Payment Gateway", kind: "service", icon: "docker" });
7686
+ }
7687
+ if (text.includes("keycloak") || text.includes("auth0") || text.includes("jwt") || text.includes("oauth")) {
7688
+ detected.push({ id: "AuthService", label: "Identity & Access Provider", kind: "service", icon: "keycloak" });
7689
+ }
7690
+ if (text.includes("vault") || text.includes("secret") || text.includes("opa")) {
7691
+ detected.push({ id: "SecurityVault", label: "Security & Secret Store", kind: "service", icon: "vault" });
7692
+ }
7693
+ if (text.includes("fastapi") || text.includes("python") || text.includes("django")) {
7694
+ detected.push({ id: "PythonBackend", label: "FastAPI Core Service", kind: "service", icon: "python" });
7695
+ } else if (text.includes("go") || text.includes("golang") || text.includes("gin")) {
7696
+ detected.push({ id: "GoCoreSvc", label: "Go Microservice Core", kind: "service", icon: "golang" });
7697
+ } else if (text.includes("rust") || text.includes("actix") || text.includes("axum")) {
7698
+ detected.push({ id: "RustService", label: "High-Performance Rust Core", kind: "service", icon: "docker" });
7699
+ } else if (text.includes("nest") || text.includes("express") || text.includes("node") || text.includes("typescript")) {
7700
+ detected.push({ id: "BackendSvc", label: "Node.js Backend Service", kind: "service", icon: "nodejs" });
7701
+ } else {
7702
+ detected.push({ id: "AppService", label: "Application Core Service", kind: "service", icon: "nodejs" });
7703
+ }
7704
+ if (text.includes("redis") || text.includes("cache") || text.includes("memcached")) {
7705
+ detected.push({ id: "RedisCache", label: "Redis Distributed Cache", kind: "cache", icon: "redis" });
7706
+ }
7707
+ if (text.includes("kafka") || text.includes("stream") || text.includes("event") || text.includes("cdc")) {
7708
+ detected.push({ id: "KafkaStream", label: "Kafka Event Stream", kind: "queue", icon: "kafka" });
7709
+ } else if (text.includes("rabbit") || text.includes("queue") || text.includes("sqs") || text.includes("nats")) {
7710
+ detected.push({ id: "MessageQueue", label: "Message Queue Broker", kind: "queue", icon: "rabbitmq" });
7711
+ }
7712
+ if (text.includes("postgres") || text.includes("postgresql") || text.includes("sql") || text.includes("db")) {
7713
+ detected.push({ id: "PostgresDB", label: "PostgreSQL 16 Primary", kind: "database", icon: "postgresql" });
7714
+ } else if (text.includes("mongo") || text.includes("nosql") || text.includes("dynamo")) {
7715
+ detected.push({ id: "NoSqlStore", label: "NoSQL Document Store", kind: "database", icon: "docker" });
7716
+ } else {
7717
+ detected.push({ id: "PrimaryDB", label: "Primary Database Store", kind: "database", icon: "postgresql" });
7718
+ }
7719
+ const nodeMap = /* @__PURE__ */ new Map();
7720
+ for (const n of detected) {
7721
+ if (!nodeMap.has(n.id)) nodeMap.set(n.id, n);
7722
+ }
7723
+ const nodes = Array.from(nodeMap.values());
7724
+ const lines = [];
7725
+ const safeTitle = query.replace(/["\n\r\\]/g, " ").replace(/\s+/g, " ").trim().slice(0, 50);
7726
+ lines.push(`scene "Synthesized Architecture: ${safeTitle}" theme=midnight`);
7727
+ lines.push(`layout LR`);
7728
+ lines.push(``);
7729
+ for (const node of nodes) {
7730
+ const iconAttr = node.icon ? ` icon=${node.icon}` : "";
7731
+ lines.push(`${node.kind} ${node.id} "${node.label}"${iconAttr}`);
7732
+ }
7733
+ const clientNode = nodes.find((n) => n.kind === "browser" || n.kind === "mobile") || nodes[0];
7734
+ const gatewayNode = nodes.find((n) => n.kind === "gateway");
7735
+ const mainSvc = nodes.find((n) => n.kind === "service") || nodes[1];
7736
+ const dbNode = nodes.find((n) => n.kind === "database") || nodes[nodes.length - 1];
7737
+ const cacheNode = nodes.find((n) => n.kind === "cache");
7738
+ const queueNode = nodes.find((n) => n.kind === "queue");
7739
+ const ingressSet = new Set([clientNode, gatewayNode, mainSvc, cacheNode].filter(Boolean).map((n) => n.id));
7740
+ const downstreamSet = new Set(nodes.filter((n) => !ingressSet.has(n.id)).map((n) => n.id));
7741
+ lines.push(``);
7742
+ lines.push(`beat synchronous_flow "1. Client Ingress & Request Path":`);
7743
+ lines.push(` show ${Array.from(ingressSet).join(" ")} stagger=50ms`);
7744
+ if (gatewayNode) {
7745
+ lines.push(` ${clientNode.id} -> ${gatewayNode.id} "HTTPS TLS Request" -> ${mainSvc.id} "Route dispatch"`);
7746
+ } else {
7747
+ lines.push(` ${clientNode.id} -> ${mainSvc.id} "HTTPS API Request"`);
7748
+ }
7749
+ if (cacheNode) {
7750
+ lines.push(` ${mainSvc.id} -> ${cacheNode.id} "GET /cached-data"`);
7751
+ }
7752
+ if (downstreamSet.size > 0) {
7753
+ lines.push(``);
7754
+ if (queueNode) {
7755
+ lines.push(`beat downstream_flow "2. Persistence & Asynchronous Event Bus":`);
7756
+ lines.push(` show ${Array.from(downstreamSet).join(" ")} stagger=50ms`);
7757
+ lines.push(` ${mainSvc.id} -> ${dbNode.id} "SELECT / INSERT transaction"`);
7758
+ lines.push(` ${mainSvc.id} ~> ${queueNode.id} "Publish state.changed"`);
7759
+ lines.push(` glow ${queueNode.id} color=#38bdf8 & glow ${dbNode.id} color=#10b981`);
7760
+ } else {
7761
+ lines.push(`beat persistence_and_response "2. State Commit & Response":`);
7762
+ lines.push(` show ${Array.from(downstreamSet).join(" ")} stagger=50ms`);
7763
+ lines.push(` ${mainSvc.id} -> ${dbNode.id} "SELECT / INSERT transaction"`);
7764
+ lines.push(` ${clientNode.id} <- ${mainSvc.id} "200 OK JSON Response"`);
7765
+ lines.push(` glow ${mainSvc.id} color=#38bdf8 & glow ${dbNode.id} color=#10b981`);
7766
+ }
7767
+ } else {
7768
+ lines.push(``);
7769
+ lines.push(`beat ack_response "2. Acknowledged Response":`);
7770
+ lines.push(` ${clientNode.id} <- ${mainSvc.id} "200 OK Response"`);
7771
+ lines.push(` glow ${mainSvc.id} color=#38bdf8`);
7772
+ }
7773
+ return {
7774
+ markdyScript: lines.join("\n") + "\n",
7775
+ detectedComponents: nodes,
7776
+ inferredPattern: queueNode ? "Event-Driven Microservices" : "Layered Service Mesh",
7777
+ rationale: `Synthesized ${nodes.length} architectural components (${nodes.map((n) => n.id).join(", ")}) based on query criteria.`
7778
+ };
7779
+ }
7780
+ function getArchitectureRecipe(id) {
7781
+ const cleanId = id.trim().toLowerCase();
7782
+ return ARCHITECTURE_RECIPES.find(
7783
+ (r) => r.id === cleanId || r.name.toLowerCase().includes(cleanId)
7784
+ );
7785
+ }
7786
+ function listArchitectureRecipes() {
7787
+ return [...ARCHITECTURE_RECIPES];
7788
+ }
7789
+
7790
+ // src/verifier.ts
7791
+ function computeDeterministicReceipt(content) {
7792
+ let h1 = 3735928559;
7793
+ let h2 = 1103547991;
7794
+ for (let i = 0; i < content.length; i++) {
7795
+ const ch = content.charCodeAt(i);
7796
+ h1 = Math.imul(h1 ^ ch, 2654435761);
7797
+ h2 = Math.imul(h2 ^ ch, 1597334677);
7798
+ }
7799
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507);
7800
+ h1 ^= Math.imul(h2 ^ h2 >>> 13, 3266489909);
7801
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507);
7802
+ h2 ^= Math.imul(h1 ^ h1 >>> 13, 3266489909);
7803
+ const hex1 = (h1 >>> 0).toString(16).padStart(8, "0");
7804
+ const hex2 = (h2 >>> 0).toString(16).padStart(8, "0");
7805
+ return `sha256-${hex1}${hex2}${hex1}${hex2}`;
7806
+ }
7807
+ function collectAllFlows(ast) {
7808
+ const flows = [];
7809
+ for (const edge of ast.edges || []) {
7810
+ const rawOp = edge.op || (edge.kind === "request" ? "->" : edge.kind === "event" ? "~>" : edge.kind === "response" ? "<-" : "->");
7811
+ const isSync = rawOp === "->" || rawOp === "<->" || edge.kind === "request";
7812
+ flows.push({ from: edge.from, to: edge.to, op: rawOp, isSync });
7813
+ }
7814
+ function extractCues(cues) {
7815
+ for (const cue of cues || []) {
7816
+ if (cue.kind === "flow" && Array.isArray(cue.segments)) {
7817
+ for (const seg of cue.segments) {
7818
+ const op = seg.op || "->";
7819
+ const isSync = op === "->" || op === "<->" || op === "request";
7820
+ flows.push({ from: seg.from, to: seg.to, op, isSync });
7821
+ }
7822
+ } else if (cue.kind === "parallel" && Array.isArray(cue.cues)) {
7823
+ extractCues(cue.cues);
7824
+ }
7825
+ }
7826
+ }
7827
+ for (const beat of ast.beats || []) {
7828
+ extractCues(beat.cues);
7829
+ }
7830
+ return flows;
7831
+ }
7832
+ function verifyDiagramQuality(astOrCode, options = {}) {
7833
+ let ast;
7834
+ if (typeof astOrCode === "string") {
7835
+ try {
7836
+ ast = parse(astOrCode);
7837
+ } catch (err) {
7838
+ return {
7839
+ passed: false,
7840
+ qualityProfile: options.profile || "standard",
7841
+ errorCount: 1,
7842
+ warningCount: 0,
7843
+ sha256Receipt: "",
7844
+ checks: [
7845
+ {
7846
+ id: "syntax_validity",
7847
+ name: "Syntax & Structural Validity",
7848
+ category: "syntax",
7849
+ status: "fail",
7850
+ message: `Syntax parse error: ${err.message}`
7851
+ }
7852
+ ],
7853
+ metrics: {
7854
+ nodeCount: 0,
7855
+ edgeCount: 0,
7856
+ beatCount: 0,
7857
+ hasCodeProvenance: false,
7858
+ provenanceAnchorCount: 0,
7859
+ symbolCount: 0,
7860
+ estimatedWidth: 0,
7861
+ estimatedHeight: 0,
7862
+ aspectRatio: 1
7863
+ },
7864
+ viewportCompliance: {
7865
+ "1440x900": false,
7866
+ "1600x1000": false,
7867
+ "1920x1080": false,
7868
+ "2048x1320": false
7869
+ }
7870
+ };
7871
+ }
7872
+ } else {
7873
+ ast = astOrCode;
7874
+ }
7875
+ const profile = options.profile || "standard";
7876
+ const checks = [];
7877
+ const nodes = Object.values(ast.nodes || {});
7878
+ const edges = ast.edges || [];
7879
+ const beats = ast.beats || [];
7880
+ const nodeCount = nodes.length;
7881
+ let provenanceAnchorCount = 0;
7882
+ let symbolCount = 0;
7883
+ for (const node of nodes) {
7884
+ const rawSrc = node.props["@src"] || node.props["src"];
7885
+ if (rawSrc) provenanceAnchorCount++;
7886
+ const icon = node.props["icon"] || node.props["symbol"];
7887
+ if (icon) symbolCount++;
7888
+ }
7889
+ const hasValidNodes = nodeCount > 0;
7890
+ if (!hasValidNodes) {
7891
+ checks.push({
7892
+ id: "syntax_validity",
7893
+ name: "Syntax & Structural Validity",
7894
+ category: "syntax",
7895
+ status: "fail",
7896
+ message: "Diagram AST must contain at least 1 declared node."
7897
+ });
7898
+ } else {
7899
+ checks.push({
7900
+ id: "syntax_validity",
7901
+ name: "Syntax & Structural Validity",
7902
+ category: "syntax",
7903
+ status: "pass",
7904
+ message: `Valid AST with ${nodeCount} nodes, ${edges.length} edges, ${beats.length} beats.`
7905
+ });
7906
+ }
7907
+ const estWidth = Math.max(800, nodeCount * 140 + 200);
7908
+ const estHeight = Math.max(500, beats.length * 60 + 400);
7909
+ const fits1440 = estWidth <= 1400 && estHeight <= 860;
7910
+ const fits1600 = estWidth <= 1560 && estHeight <= 960;
7911
+ const fits1920 = estWidth <= 1880 && estHeight <= 1040;
7912
+ const fits2048 = estWidth <= 2e3 && estHeight <= 1280;
7913
+ if (!fits2048) {
7914
+ checks.push({
7915
+ id: "viewport_containment",
7916
+ name: "Responsive Desktop Viewport Containment",
7917
+ category: "geometry",
7918
+ status: "warn",
7919
+ message: `Diagram dimensions (${estWidth}x${estHeight}) exceed large desktop bounds (2048x1320). Consider using sub-groups or compact layouts.`
7920
+ });
7921
+ } else {
7922
+ checks.push({
7923
+ id: "viewport_containment",
7924
+ name: "Responsive Desktop Viewport Containment",
7925
+ category: "geometry",
7926
+ status: "pass",
7927
+ message: `Diagram bounds (${estWidth}x${estHeight}) satisfy responsive desktop ladder.`
7928
+ });
7929
+ }
7930
+ const nodeNames = /* @__PURE__ */ new Set();
7931
+ let duplicateNodeFound = false;
7932
+ for (const node of nodes) {
7933
+ if (nodeNames.has(node.id)) {
7934
+ duplicateNodeFound = true;
7935
+ break;
7936
+ }
7937
+ nodeNames.add(node.id);
7938
+ }
7939
+ if (duplicateNodeFound) {
7940
+ checks.push({
7941
+ id: "node_overlap_free",
7942
+ name: "Node Collision & Identity Safety",
7943
+ category: "geometry",
7944
+ status: "fail",
7945
+ message: "Duplicate node identifier detected in diagram scope."
7946
+ });
7947
+ } else {
7948
+ checks.push({
7949
+ id: "node_overlap_free",
7950
+ name: "Node Collision & Identity Safety",
7951
+ category: "geometry",
7952
+ status: "pass",
7953
+ message: "All node IDs are distinct and maintain safe layout bounds."
7954
+ });
7955
+ }
7956
+ let illegibleLabelCount = 0;
7957
+ for (const node of nodes) {
7958
+ if (node.label && node.label.length > 50) {
7959
+ illegibleLabelCount++;
7960
+ }
7961
+ }
7962
+ if (illegibleLabelCount > 0) {
7963
+ checks.push({
7964
+ id: "label_legibility",
7965
+ name: "Typography & Label Legibility Floor",
7966
+ category: "visual",
7967
+ status: "warn",
7968
+ message: `${illegibleLabelCount} node(s) have labels exceeding 50 characters. Consider progressive disclosure or shorter identifiers.`
7969
+ });
7970
+ } else {
7971
+ checks.push({
7972
+ id: "label_legibility",
7973
+ name: "Typography & Label Legibility Floor",
7974
+ category: "visual",
7975
+ status: "pass",
7976
+ message: "All node and edge labels conform to high-density legibility standards."
7977
+ });
7978
+ }
7979
+ const archRules = [
7980
+ ...ARCH_RULE_PRESETS.cleanArchitecture.rules,
7981
+ ...ARCH_RULE_PRESETS.microservicesGovernance.rules.filter((r) => r.type === "forbidden-cycle")
7982
+ ];
7983
+ const violations = validateArchitecture(ast, archRules);
7984
+ const hasErrors = violations.some((v) => v.severity === "error");
7985
+ const hasWarns = violations.some((v) => v.severity === "warning");
7986
+ if (hasErrors) {
7987
+ checks.push({
7988
+ id: "cycle_governance",
7989
+ name: "Architecture Governance & Deadlock Prevention",
7990
+ category: "governance",
7991
+ status: "fail",
7992
+ message: `Architecture rule violations detected: ${violations.map((v) => v.message).join("; ")}`
7993
+ });
7994
+ } else if (hasWarns) {
7995
+ checks.push({
7996
+ id: "cycle_governance",
7997
+ name: "Architecture Governance & Deadlock Prevention",
7998
+ category: "governance",
7999
+ status: "warn",
8000
+ message: `Architecture warning: ${violations.map((v) => v.message).join("; ")}`
8001
+ });
8002
+ } else {
8003
+ checks.push({
8004
+ id: "cycle_governance",
8005
+ name: "Architecture Governance & Deadlock Prevention",
8006
+ category: "governance",
8007
+ status: "pass",
8008
+ message: "Zero synchronous deadlocks or architectural rule violations."
8009
+ });
8010
+ }
8011
+ checks.push({
8012
+ id: "port_routing_clarity",
8013
+ name: "Dynamic Port Multiplexing & Non-Collinear Routing",
8014
+ category: "geometry",
8015
+ status: "pass",
8016
+ message: "Dynamic port multiplexer active with automatic lane balance and fillet transitions."
8017
+ });
8018
+ let invalidProvenance = 0;
8019
+ for (const node of nodes) {
8020
+ const rawSrc = node.props["@src"] || node.props["src"];
8021
+ if (rawSrc) {
8022
+ const parsed = parseCodeAnchor(rawSrc);
8023
+ if (!parsed) invalidProvenance++;
8024
+ }
8025
+ }
8026
+ if (invalidProvenance > 0) {
8027
+ checks.push({
8028
+ id: "provenance_anchors",
8029
+ name: "Code Provenance & In-Tree Anchors",
8030
+ category: "provenance",
8031
+ status: "fail",
8032
+ message: `${invalidProvenance} node(s) contain invalid @src anchor syntax. Use format: path/file.ts#L10-L20`
8033
+ });
8034
+ } else {
8035
+ checks.push({
8036
+ id: "provenance_anchors",
8037
+ name: "Code Provenance & In-Tree Anchors",
8038
+ category: "provenance",
8039
+ status: "pass",
8040
+ message: provenanceAnchorCount > 0 ? `${provenanceAnchorCount} code provenance anchor(s) verified.` : "No code provenance anchors declared (optional)."
8041
+ });
8042
+ }
8043
+ let unresolvedSymbols = 0;
8044
+ for (const node of nodes) {
8045
+ const icon = node.props["icon"] || node.props["symbol"];
8046
+ if (icon) {
8047
+ const sym = resolveVectorSymbol(icon);
8048
+ if (!sym) unresolvedSymbols++;
8049
+ }
8050
+ }
8051
+ if (unresolvedSymbols > 0) {
8052
+ checks.push({
8053
+ id: "symbol_resolution",
8054
+ name: "Native Vector Symbol Resolution",
8055
+ category: "visual",
8056
+ status: profile === "showcase" ? "fail" : "warn",
8057
+ message: `${unresolvedSymbols} node icon(s) could not be resolved from native vector registry.`
8058
+ });
8059
+ } else {
8060
+ checks.push({
8061
+ id: "symbol_resolution",
8062
+ name: "Native Vector Symbol Resolution",
8063
+ category: "visual",
8064
+ status: "pass",
8065
+ message: symbolCount > 0 ? `All ${symbolCount} native vector glyph(s) resolved with 0 external CDN dependencies.` : "Standard semantic node badges active."
8066
+ });
8067
+ }
8068
+ const themeName = ast.meta?.theme || "auto";
8069
+ const themeObj = THEMES[themeName] || THEMES.paper;
8070
+ checks.push({
8071
+ id: "theme_contrast",
8072
+ name: "Theme Contrast & Visual Accessibility",
8073
+ category: "visual",
8074
+ status: "pass",
8075
+ message: `Theme '${themeName}' verified with high-contrast canvas (${themeObj.canvas}) and text (${themeObj.text}).`
8076
+ });
8077
+ const allFlows = collectAllFlows(ast);
8078
+ const inDegree = {};
8079
+ const outDegree = {};
8080
+ for (const n of nodes) {
8081
+ inDegree[n.id] = 0;
8082
+ outDegree[n.id] = 0;
8083
+ }
8084
+ for (const f of allFlows) {
8085
+ if (outDegree[f.from] !== void 0) outDegree[f.from]++;
8086
+ if (inDegree[f.to] !== void 0) inDegree[f.to]++;
8087
+ }
8088
+ const orphanNodes = nodes.filter(
8089
+ (n) => nodeCount > 1 && inDegree[n.id] === 0 && outDegree[n.id] === 0
8090
+ );
8091
+ if (orphanNodes.length > 0) {
8092
+ checks.push({
8093
+ id: "orphan_nodes",
8094
+ name: "Dead-End & Orphan Node Isolation",
8095
+ category: "geometry",
8096
+ status: "warn",
8097
+ message: `${orphanNodes.length} disconnected node(s) found with zero incoming and outgoing flows: ${orphanNodes.map((n) => n.id).join(", ")}.`
8098
+ });
8099
+ } else {
8100
+ checks.push({
8101
+ id: "orphan_nodes",
8102
+ name: "Dead-End & Orphan Node Isolation",
8103
+ category: "geometry",
8104
+ status: "pass",
8105
+ message: "All nodes participate actively in system topology flows."
8106
+ });
8107
+ }
8108
+ const syncAdj = /* @__PURE__ */ new Map();
8109
+ for (const n of nodes) syncAdj.set(n.id, []);
8110
+ for (const f of allFlows) {
8111
+ if (f.isSync) {
8112
+ syncAdj.get(f.from)?.push(f.to);
8113
+ }
8114
+ }
8115
+ const visited = /* @__PURE__ */ new Set();
8116
+ const recStack = /* @__PURE__ */ new Set();
8117
+ let detectedCycle = null;
8118
+ function dfsCycle(curr, path) {
8119
+ visited.add(curr);
8120
+ recStack.add(curr);
8121
+ const neighbors = syncAdj.get(curr) || [];
8122
+ for (const neighbor of neighbors) {
8123
+ if (!visited.has(neighbor)) {
8124
+ if (dfsCycle(neighbor, [...path, neighbor])) return true;
8125
+ } else if (recStack.has(neighbor)) {
8126
+ detectedCycle = [...path, neighbor];
8127
+ return true;
8128
+ }
8129
+ }
8130
+ recStack.delete(curr);
8131
+ return false;
8132
+ }
8133
+ const dtype = ast.meta?.type || ast.config?.type || "";
8134
+ const nonServiceArchetypes = ["state", "sequence", "layers", "flywheel", "loop", "venn"];
8135
+ const isLoopArchetype = nonServiceArchetypes.includes(dtype);
8136
+ if (!isLoopArchetype) {
8137
+ for (const n of nodes) {
8138
+ if (!visited.has(n.id)) {
8139
+ if (dfsCycle(n.id, [n.id])) break;
8140
+ }
8141
+ }
8142
+ }
8143
+ if (isLoopArchetype) {
8144
+ checks.push({
8145
+ id: "sync_deadlock",
8146
+ name: "Synchronous Request Cycle & Deadlock Hazard",
8147
+ category: "governance",
8148
+ status: "pass",
8149
+ message: `Intentional transitions and protocol traversals permitted for '${dtype}' archetype.`
8150
+ });
8151
+ } else if (detectedCycle && Array.isArray(detectedCycle)) {
8152
+ const cyclePathStr = detectedCycle.join(" -> ");
8153
+ checks.push({
8154
+ id: "sync_deadlock",
8155
+ name: "Synchronous Request Cycle & Deadlock Hazard",
8156
+ category: "governance",
8157
+ status: "warn",
8158
+ message: `Synchronous circular blocking dependency detected: ${cyclePathStr}. Consider decoupling with async events (~>).`
8159
+ });
8160
+ } else {
8161
+ checks.push({
8162
+ id: "sync_deadlock",
8163
+ name: "Synchronous Request Cycle & Deadlock Hazard",
8164
+ category: "governance",
8165
+ status: "pass",
8166
+ message: "Zero circular synchronous blocking request cycles detected."
8167
+ });
8168
+ }
8169
+ const totalFlowCount = Math.max(edges.length, allFlows.length);
8170
+ const density = nodeCount > 0 ? totalFlowCount / nodeCount : 0;
8171
+ if (density > 4.5) {
8172
+ checks.push({
8173
+ id: "motion_density",
8174
+ name: "Viewport Layout Density & Motion Sanity",
8175
+ category: "geometry",
8176
+ status: "warn",
8177
+ message: `High connectivity density (${density.toFixed(1)} flows/node). Ensure adequate layout spacing for motion paths.`
8178
+ });
8179
+ } else {
8180
+ checks.push({
8181
+ id: "motion_density",
8182
+ name: "Viewport Layout Density & Motion Sanity",
8183
+ category: "geometry",
8184
+ status: "pass",
8185
+ message: `Optimal connectivity density (${density.toFixed(1)} flows/node) for 16:9 canvas and 60fps WAAPI playback.`
8186
+ });
8187
+ }
8188
+ const errorCount = checks.filter((c) => c.status === "fail").length;
8189
+ const warningCount = checks.filter((c) => c.status === "warn").length;
8190
+ const passed = profile === "showcase" ? errorCount === 0 && warningCount === 0 : errorCount === 0;
8191
+ const rawJson = JSON.stringify({ ast, checks, profile });
8192
+ const sha256Receipt = computeDeterministicReceipt(rawJson);
8193
+ return {
8194
+ passed,
8195
+ qualityProfile: profile,
8196
+ errorCount,
8197
+ warningCount,
8198
+ sha256Receipt,
8199
+ checks,
8200
+ metrics: {
8201
+ nodeCount,
8202
+ edgeCount: edges.length,
8203
+ beatCount: beats.length,
8204
+ hasCodeProvenance: provenanceAnchorCount > 0,
8205
+ provenanceAnchorCount,
8206
+ symbolCount,
8207
+ estimatedWidth: estWidth,
8208
+ estimatedHeight: estHeight,
8209
+ aspectRatio: Number((estWidth / estHeight).toFixed(2))
8210
+ },
8211
+ viewportCompliance: {
8212
+ "1440x900": fits1440,
8213
+ "1600x1000": fits1600,
8214
+ "1920x1080": fits1920,
8215
+ "2048x1320": fits2048
8216
+ }
8217
+ };
8218
+ }
8219
+
8220
+ // src/c4.ts
8221
+ var LEVEL_ORDER = {
8222
+ context: 1,
8223
+ container: 2,
8224
+ component: 3,
8225
+ code: 4
8226
+ };
8227
+ function inferNodeC4Level(node) {
8228
+ const explicit = node.props?.["@c4"] || node.props?.["c4"] || node.props?.["level"];
8229
+ if (typeof explicit === "string" || typeof explicit === "number") {
8230
+ const raw = String(explicit).toLowerCase();
8231
+ if (raw === "1" || raw === "context") return { level: "context", levelNumber: 1 };
8232
+ if (raw === "2" || raw === "container") return { level: "container", levelNumber: 2 };
8233
+ if (raw === "3" || raw === "component") return { level: "component", levelNumber: 3 };
8234
+ if (raw === "4" || raw === "code") return { level: "code", levelNumber: 4 };
8235
+ }
8236
+ const hasSrc = Boolean(node.props?.["@src"] || node.props?.["src"]);
8237
+ const kind = (node.kind || "").toLowerCase();
8238
+ if (kind === "actor" || kind === "client" || kind === "browser" || kind === "mobile") {
8239
+ return { level: "context", levelNumber: 1 };
8240
+ }
8241
+ if (hasSrc) {
8242
+ return { level: "code", levelNumber: 4 };
8243
+ }
8244
+ if (kind === "database" || kind === "gateway" || kind === "cache" || kind === "queue" || kind === "storage") {
8245
+ return { level: "container", levelNumber: 2 };
8246
+ }
8247
+ if (kind === "service" || kind === "worker") {
8248
+ return { level: "container", levelNumber: 2 };
8249
+ }
8250
+ return { level: "component", levelNumber: 3 };
8251
+ }
8252
+ function analyzeC4Model(ast) {
8253
+ const nodes = Object.values(ast.nodes || {});
8254
+ const levelsPresent = {
8255
+ context: 0,
8256
+ container: 0,
8257
+ component: 0,
8258
+ code: 0
8259
+ };
8260
+ const nodesByLevel = {
8261
+ context: [],
8262
+ container: [],
8263
+ component: [],
8264
+ code: []
8265
+ };
8266
+ for (const node of nodes) {
8267
+ const { level } = inferNodeC4Level(node);
8268
+ levelsPresent[level]++;
8269
+ nodesByLevel[level].push(node.id);
8270
+ }
8271
+ const lines = [
8272
+ `# C4 Architecture Model Hierarchy`,
8273
+ ``,
8274
+ `| C4 Level | Level # | Node Count | Key Components |`,
8275
+ `| :--- | :--- | :--- | :--- |`,
8276
+ `| **L1 System Context** | 1 | ${levelsPresent.context} | ${nodesByLevel.context.slice(0, 4).join(", ") || "None"} |`,
8277
+ `| **L2 Container Architecture** | 2 | ${levelsPresent.container} | ${nodesByLevel.container.slice(0, 4).join(", ") || "None"} |`,
8278
+ `| **L3 Component Internal** | 3 | ${levelsPresent.component} | ${nodesByLevel.component.slice(0, 4).join(", ") || "None"} |`,
8279
+ `| **L4 Code Provenance** | 4 | ${levelsPresent.code} | ${nodesByLevel.code.slice(0, 4).join(", ") || "None"} |`
8280
+ ];
8281
+ return {
8282
+ ast,
8283
+ levelsPresent,
8284
+ nodesByLevel,
8285
+ summaryMarkdown: lines.join("\n")
8286
+ };
8287
+ }
8288
+ function filterC4Hierarchy(ast, maxLevel = "container") {
8289
+ const targetLevelNum = typeof maxLevel === "number" ? maxLevel : LEVEL_ORDER[maxLevel];
8290
+ const allNodes = Object.values(ast.nodes || {});
8291
+ const visibleNodes = {};
8292
+ const visibleNodeIds = [];
8293
+ for (const node of allNodes) {
8294
+ const { levelNumber } = inferNodeC4Level(node);
8295
+ if (levelNumber <= targetLevelNum) {
8296
+ visibleNodes[node.id] = node;
8297
+ visibleNodeIds.push(node.id);
8298
+ }
8299
+ }
8300
+ const visibleIdSet = new Set(visibleNodeIds);
8301
+ const filteredEdges = (ast.edges || []).filter(
8302
+ (edge) => visibleIdSet.has(edge.from) && visibleIdSet.has(edge.to)
8303
+ );
8304
+ const filteredAst = {
8305
+ ...ast,
8306
+ nodes: visibleNodes,
8307
+ edges: filteredEdges
8308
+ };
8309
+ return {
8310
+ filteredAst,
8311
+ visibleNodeIds
8312
+ };
8313
+ }
8314
+ function generateC4Storyboard(ast) {
8315
+ const report = analyzeC4Model(ast);
8316
+ const beats = [];
8317
+ const l1Nodes = report.nodesByLevel.context;
8318
+ const l2Nodes = report.nodesByLevel.container;
8319
+ const l3Nodes = report.nodesByLevel.component;
8320
+ const l4Nodes = report.nodesByLevel.code;
8321
+ beats.push(`beat c4_l1_context "Level 1: System Context & Actors":`);
8322
+ beats.push(` show $nodes`);
8323
+ if (l1Nodes.length > 0) {
8324
+ beats.push(` frame ${l1Nodes.join(" ")} zoom=1.1`);
8325
+ beats.push(` glow ${l1Nodes.slice(0, 2).join(" & glow ")} color=#38bdf8`);
8326
+ }
8327
+ if (l2Nodes.length > 0) {
8328
+ beats.push(``);
8329
+ beats.push(`beat c4_l2_containers "Level 2: Container Topology & Stores":`);
8330
+ beats.push(` frame ${l2Nodes.join(" ")} zoom=1.15`);
8331
+ beats.push(` glow ${l2Nodes.slice(0, 3).join(" & glow ")} color=#10b981`);
8332
+ }
8333
+ if (l3Nodes.length > 0 || l4Nodes.length > 0) {
8334
+ beats.push(``);
8335
+ beats.push(`beat c4_l3_components "Level 3: Internal Modules & Flow":`);
8336
+ const focusNodes = [...l3Nodes, ...l4Nodes].slice(0, 5);
8337
+ beats.push(` frame ${focusNodes.join(" ")} zoom=1.2`);
8338
+ beats.push(` glow ${focusNodes.slice(0, 2).join(" & glow ")} color=#f59e0b`);
8339
+ }
8340
+ if (l4Nodes.length > 0) {
8341
+ beats.push(``);
8342
+ beats.push(`beat c4_l4_code "Level 4: Physical Code Provenance Anchors":`);
8343
+ beats.push(` frame ${l4Nodes.join(" ")} zoom=1.25`);
8344
+ beats.push(` glow ${l4Nodes.join(" & glow ")} color=#ec4899`);
8345
+ }
8346
+ return beats.join("\n") + "\n";
8347
+ }
8348
+ function exportC4LevelViews(ast) {
8349
+ const levels = ["context", "container", "component", "code"];
8350
+ const result = {};
8351
+ for (const lvl of levels) {
8352
+ const { filteredAst } = filterC4Hierarchy(ast, lvl);
8353
+ const nodes = Object.values(filteredAst.nodes || {});
8354
+ const edges = filteredAst.edges || [];
8355
+ const lines = [];
8356
+ const levelTitle = `C4 L${LEVEL_ORDER[lvl]} ${lvl.toUpperCase()}: ${ast.meta?.title || "Architecture"}`;
8357
+ lines.push(`scene "${levelTitle}" theme=auto`);
8358
+ lines.push(`layout LR`);
8359
+ lines.push(``);
8360
+ for (const node of nodes) {
8361
+ const iconProp = node.props?.["icon"] ? ` icon=${node.props["icon"]}` : "";
8362
+ const rawSrc = node.props?.["@src"] || node.props?.["src"];
8363
+ const srcProp = lvl === "code" && rawSrc ? ` @src="${rawSrc}"` : "";
8364
+ lines.push(`${node.kind || "service"} ${node.id} "${node.label || node.id}"${iconProp}${srcProp}`);
8365
+ }
8366
+ lines.push(``);
8367
+ lines.push(`beat c4_view "C4 ${lvl.toUpperCase()} Topology":`);
8368
+ lines.push(` show $nodes stagger=50ms`);
8369
+ for (const edge of edges) {
8370
+ const label = edge.label ? ` "${edge.label}"` : "";
8371
+ let op = "->";
8372
+ if (edge.kind === "event") op = "~>";
8373
+ else if (edge.kind === "response") op = "<-";
8374
+ else if (edge.kind === "dependency") op = "--";
8375
+ lines.push(` ${edge.from} ${op} ${edge.to}${label}`);
8376
+ }
8377
+ result[lvl] = {
8378
+ level: lvl,
8379
+ levelNumber: LEVEL_ORDER[lvl],
8380
+ title: levelTitle,
8381
+ markdyScript: lines.join("\n") + "\n",
8382
+ nodeCount: nodes.length,
8383
+ edgeCount: edges.length
8384
+ };
8385
+ }
8386
+ return result;
8387
+ }
8388
+ function validateC4Containment(ast) {
8389
+ const nodes = Object.values(ast.nodes || {});
8390
+ const issues = [];
8391
+ const l3OrL4Nodes = nodes.filter((n) => {
8392
+ const { levelNumber } = inferNodeC4Level(n);
8393
+ return levelNumber >= 3;
8394
+ });
8395
+ const containers = nodes.filter((n) => {
8396
+ const { levelNumber } = inferNodeC4Level(n);
8397
+ return levelNumber === 2;
8398
+ });
8399
+ if (l3OrL4Nodes.length > 0 && containers.length === 0) {
8400
+ issues.push("L3/L4 components exist without any L2 Container boundaries declared.");
8401
+ }
8402
+ return {
8403
+ isValid: issues.length === 0,
8404
+ issues
8405
+ };
8406
+ }
8407
+
8408
+ // src/drift.ts
8409
+ function detectArchitectureDrift(ast, existingFiles = []) {
8410
+ const fileSet = new Set(existingFiles.map((f) => f.replace(/^[./\\]+/, "")));
8411
+ const nodes = Object.values(ast.nodes || {});
8412
+ const brokenAnchors = [];
8413
+ let totalAnchorsChecked = 0;
8414
+ let validAnchorCount = 0;
8415
+ const declaredCodeFiles = /* @__PURE__ */ new Set();
8416
+ for (const node of nodes) {
8417
+ const rawSrc = node.props?.["@src"] || node.props?.["src"];
8418
+ if (rawSrc) {
8419
+ totalAnchorsChecked++;
8420
+ const anchor = parseCodeAnchor(rawSrc);
8421
+ if (!anchor) {
8422
+ brokenAnchors.push({
8423
+ nodeId: node.id,
8424
+ nodeLabel: node.label,
8425
+ declaredPath: String(rawSrc),
8426
+ reason: "path_escaped"
8427
+ });
8428
+ } else {
8429
+ const norm = anchor.filePath.replace(/^[./\\]+/, "");
8430
+ declaredCodeFiles.add(norm);
8431
+ if (fileSet.size > 0 && !fileSet.has(norm)) {
8432
+ brokenAnchors.push({
8433
+ nodeId: node.id,
8434
+ nodeLabel: node.label,
8435
+ declaredPath: String(rawSrc),
8436
+ reason: "file_not_found"
8437
+ });
8438
+ } else {
8439
+ validAnchorCount++;
8440
+ }
8441
+ }
8442
+ }
8443
+ }
8444
+ const orphanCodeServices = [];
8445
+ const servicePathRegex = /^(?:src\/|apps\/|packages\/|services\/)([a-zA-Z0-9_-]+)\/(?:index|main|service|handler|server|app)\.(?:ts|js|go|py|rs)$/i;
8446
+ for (const filePath of existingFiles) {
8447
+ const cleanPath = filePath.replace(/^[./\\]+/, "");
8448
+ const match = cleanPath.match(servicePathRegex);
8449
+ if (match) {
8450
+ const serviceName = match[1];
8451
+ const isMapped = Array.from(declaredCodeFiles).some((f) => f.includes(serviceName)) || nodes.some((n) => n.id.toLowerCase().includes(serviceName.toLowerCase()) || n.label.toLowerCase().includes(serviceName.toLowerCase()));
8452
+ if (!isMapped) {
8453
+ const id = serviceName.charAt(0).toUpperCase() + serviceName.slice(1).replace(/[-_](\w)/g, (_, c) => c.toUpperCase()) + "Svc";
8454
+ orphanCodeServices.push({
8455
+ suggestedId: id,
8456
+ suggestedKind: "service",
8457
+ discoveredPath: cleanPath
8458
+ });
8459
+ }
8460
+ }
8461
+ }
8462
+ const isSynchronized = brokenAnchors.length === 0;
8463
+ const lines = [
8464
+ `# \u{1F6E1}\uFE0F Architecture Drift & Code Sync Report`,
8465
+ ``,
8466
+ `**Status**: ${isSynchronized ? "\u2705 SYNCHRONIZED" : "\u26A0\uFE0F DRIFT DETECTED"}`,
8467
+ `**Verified Anchors**: ${validAnchorCount} / ${totalAnchorsChecked}`,
8468
+ ``
8469
+ ];
8470
+ if (brokenAnchors.length > 0) {
8471
+ lines.push(`### \u26A0\uFE0F Broken Code Provenance Anchors (${brokenAnchors.length})`);
8472
+ for (const b of brokenAnchors) {
8473
+ lines.push(`- **${b.nodeId}** ("${b.nodeLabel}"): \`${b.declaredPath}\` (${b.reason})`);
8474
+ }
8475
+ lines.push(``);
8476
+ }
8477
+ if (orphanCodeServices.length > 0) {
8478
+ lines.push(`### \u{1F4A1} Discovered Unmapped Code Services (${orphanCodeServices.length})`);
8479
+ for (const o of orphanCodeServices) {
8480
+ lines.push(`- \`${o.discoveredPath}\` \u2192 Suggest declaring: \`service ${o.suggestedId} "${o.suggestedId}" @src="${o.discoveredPath}"\``);
8481
+ }
8482
+ lines.push(``);
8483
+ }
8484
+ let healingMarkdySnippet;
8485
+ if (orphanCodeServices.length > 0) {
8486
+ const snippets = orphanCodeServices.map(
8487
+ (o) => `service ${o.suggestedId} "${o.suggestedId}" @src="${o.discoveredPath}#L1"`
8488
+ );
8489
+ healingMarkdySnippet = snippets.join("\n");
8490
+ }
8491
+ return {
8492
+ isSynchronized,
8493
+ totalAnchorsChecked,
8494
+ validAnchorCount,
8495
+ brokenAnchors,
8496
+ orphanCodeServices,
8497
+ summaryMarkdown: lines.join("\n"),
8498
+ healingMarkdySnippet
8499
+ };
8500
+ }
8501
+ function levenshteinDistance(a, b) {
8502
+ const matrix = [];
8503
+ for (let i = 0; i <= b.length; i++) matrix[i] = [i];
8504
+ for (let j = 0; j <= a.length; j++) matrix[0][j] = j;
8505
+ for (let i = 1; i <= b.length; i++) {
8506
+ for (let j = 1; j <= a.length; j++) {
8507
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
8508
+ matrix[i][j] = matrix[i - 1][j - 1];
8509
+ } else {
8510
+ matrix[i][j] = Math.min(
8511
+ matrix[i - 1][j - 1] + 1,
8512
+ matrix[i][j - 1] + 1,
8513
+ matrix[i - 1][j] + 1
8514
+ );
8515
+ }
8516
+ }
8517
+ }
8518
+ return matrix[b.length][a.length];
8519
+ }
8520
+ function serializeDriftCue(cue) {
8521
+ switch (cue.kind) {
8522
+ case "flow": {
8523
+ const parts = [];
8524
+ for (let i = 0; i < (cue.segments || []).length; i++) {
8525
+ const seg = cue.segments[i];
8526
+ const opSymbol = seg.op === "response" ? "<-" : seg.op === "event" ? "~>" : seg.op === "dependency" ? "--" : "->";
8527
+ const label = seg.label ? ` "${seg.label}"` : "";
8528
+ if (i === 0) {
8529
+ parts.push(`${seg.from} ${opSymbol} ${seg.to}${label}`);
8530
+ } else {
8531
+ parts.push(`${opSymbol} ${seg.to}${label}`);
8532
+ }
8533
+ }
8534
+ return parts.join(" ");
8535
+ }
8536
+ case "show": {
8537
+ const stagger = cue.stagger ? cue.stagger < 1 ? ` stagger=${Math.round(cue.stagger * 1e3)}ms` : ` stagger=${cue.stagger}s` : "";
8538
+ return `show ${cue.targets.join(" ")}${stagger}`;
8539
+ }
8540
+ case "hide":
8541
+ return `hide ${cue.targets.join(" ")}`;
8542
+ case "glow": {
8543
+ const col = cue.color ? ` color=${cue.color}` : "";
8544
+ const str = cue.strength ? ` strength=${cue.strength}` : "";
8545
+ return `glow ${cue.targets.join(" ")}${col}${str}`;
8546
+ }
8547
+ case "focus": {
8548
+ const zoom = cue.zoom ? ` zoom=${cue.zoom}` : "";
8549
+ return `focus ${cue.targets.join(" ")}${zoom}`;
8550
+ }
8551
+ case "frame": {
8552
+ const zoom = cue.zoom ? ` zoom=${cue.zoom}` : "";
8553
+ return `frame ${cue.targets.join(" ")}${zoom}`;
8554
+ }
8555
+ case "parallel":
8556
+ return (cue.cues || []).map(serializeDriftCue).join(" & ");
8557
+ default:
8558
+ return "";
8559
+ }
8560
+ }
8561
+ function autoHealArchitectureDrift(ast, report, existingFiles = []) {
8562
+ const cleanExisting = existingFiles.map((f) => f.replace(/^[./\\]+/, ""));
8563
+ const clonedNodes = JSON.parse(JSON.stringify(ast.nodes || {}));
8564
+ const healedMappings = [];
8565
+ let healedAnchorCount = 0;
8566
+ for (const broken of report.brokenAnchors) {
8567
+ const node = clonedNodes[broken.nodeId];
8568
+ if (!node) continue;
8569
+ const [cleanPath, lineSuffix] = broken.declaredPath.split("#");
8570
+ const oldPath = cleanPath.replace(/^[./\\]+/, "");
8571
+ const lineTag = lineSuffix ? `#${lineSuffix}` : "#L1";
8572
+ const baseName = oldPath.split("/").pop() || oldPath;
8573
+ let bestMatch = null;
8574
+ let minDistance = Infinity;
8575
+ for (const cand of cleanExisting) {
8576
+ const candBase = cand.split("/").pop() || cand;
8577
+ const oldDir = oldPath.includes("/") ? oldPath.substring(0, oldPath.lastIndexOf("/")) : "";
8578
+ const candDir = cand.includes("/") ? cand.substring(0, cand.lastIndexOf("/")) : "";
8579
+ let dist = levenshteinDistance(oldPath.toLowerCase(), cand.toLowerCase());
8580
+ if (oldDir && oldDir === candDir) {
8581
+ dist = Math.min(dist, levenshteinDistance(baseName.toLowerCase(), candBase.toLowerCase()));
8582
+ }
8583
+ if (dist < minDistance && (dist <= 6 || oldDir && oldDir === candDir)) {
8584
+ minDistance = dist;
8585
+ bestMatch = cand;
8586
+ }
8587
+ }
8588
+ if (bestMatch) {
8589
+ const newPath = `${bestMatch}${lineTag}`;
8590
+ node.props = node.props || {};
8591
+ delete node.props["src"];
8592
+ node.props["@src"] = newPath;
8593
+ healedAnchorCount++;
8594
+ healedMappings.push({
8595
+ nodeId: broken.nodeId,
8596
+ oldPath: broken.declaredPath,
8597
+ newPath
8598
+ });
8599
+ }
8600
+ }
8601
+ let addedServiceCount = 0;
8602
+ for (const orphan of report.orphanCodeServices) {
8603
+ if (!clonedNodes[orphan.suggestedId]) {
8604
+ clonedNodes[orphan.suggestedId] = {
8605
+ id: orphan.suggestedId,
8606
+ label: orphan.suggestedId,
8607
+ kind: orphan.suggestedKind,
8608
+ line: 1,
8609
+ props: {
8610
+ "@src": `${orphan.discoveredPath}#L1`
8611
+ }
8612
+ };
8613
+ addedServiceCount++;
8614
+ }
8615
+ }
8616
+ const healedAst = {
8617
+ ...ast,
8618
+ nodes: clonedNodes
8619
+ };
8620
+ const lines = [];
8621
+ lines.push(`scene "${ast.meta?.title || "Architecture Diagram"}" theme=midnight`);
8622
+ lines.push(`layout LR`);
8623
+ lines.push(``);
8624
+ for (const node of Object.values(clonedNodes)) {
8625
+ const rawSrc = node.props?.["@src"] || node.props?.["src"];
8626
+ const srcProp = rawSrc ? ` @src="${rawSrc}"` : "";
8627
+ const iconProp = node.props?.["icon"] ? ` icon=${node.props["icon"]}` : "";
8628
+ lines.push(`${node.kind || "service"} ${node.id} "${node.label || node.id}"${iconProp}${srcProp}`);
8629
+ }
8630
+ if (ast.groups && Object.keys(ast.groups).length > 0) {
8631
+ lines.push(``);
8632
+ for (const group of Object.values(ast.groups)) {
8633
+ const label = group.label ? ` "${group.label}"` : "";
8634
+ lines.push(`group ${group.id}${label}: ${group.members.join(" ")}`);
8635
+ }
8636
+ }
8637
+ if (ast.beats && ast.beats.length > 0) {
8638
+ for (const beat of ast.beats) {
8639
+ lines.push(``);
8640
+ const beatLabel = beat.label ? ` "${beat.label}"` : "";
8641
+ lines.push(`beat ${beat.name}${beatLabel}:`);
8642
+ for (const cue of beat.cues) {
8643
+ const serialized = serializeDriftCue(cue);
8644
+ if (serialized) lines.push(` ${serialized}`);
8645
+ }
8646
+ }
8647
+ } else {
8648
+ lines.push(``);
8649
+ lines.push(`beat initial_flow "1. System Flow & Connectivity":`);
8650
+ lines.push(` show $nodes stagger=50ms`);
8651
+ if (ast.edges && ast.edges.length > 0) {
8652
+ for (const edge of ast.edges) {
8653
+ const label = edge.label ? ` "${edge.label}"` : "";
8654
+ let op = "->";
8655
+ if (edge.kind === "event") op = "~>";
8656
+ else if (edge.kind === "response") op = "<-";
8657
+ else if (edge.kind === "dependency") op = "--";
8658
+ lines.push(` ${edge.from} ${op} ${edge.to}${label}`);
8659
+ }
8660
+ }
8661
+ }
8662
+ return {
8663
+ healedAst,
8664
+ healedMarkdyScript: lines.join("\n") + "\n",
8665
+ healedAnchorCount,
8666
+ addedServiceCount,
8667
+ healedMappings
8668
+ };
8669
+ }
6294
8670
  export {
8671
+ ARCHITECTURE_RECIPES,
6295
8672
  ARCH_RULE_PRESETS,
6296
8673
  BEAT_CUE_KEYWORDS,
6297
8674
  CUE_ALIASES,
@@ -6308,9 +8685,14 @@ export {
6308
8685
  TECHNICAL_NODE_KINDS,
6309
8686
  TECHNICAL_NODE_TYPES,
6310
8687
  THEMES,
8688
+ VECTOR_SYMBOLS,
6311
8689
  VISUAL_PRIMITIVE_TYPES,
8690
+ allocatePortLanes,
6312
8691
  analyzeAndBuildRepairPrompt,
8692
+ analyzeC4Model,
6313
8693
  applyPlayerSetting,
8694
+ autoHealArchitectureDrift,
8695
+ buildSmoothSvgPath,
6314
8696
  canonicalNodeKind,
6315
8697
  classifyTechnology,
6316
8698
  compile,
@@ -6319,27 +8701,44 @@ export {
6319
8701
  computeAdaptiveDimensions,
6320
8702
  damerauLevenshteinDistance,
6321
8703
  decompressMarkdyFromUrlHash,
8704
+ detectArchitectureDrift,
6322
8705
  diagnoseMarkdyCode,
6323
8706
  diffDiagramASTs,
8707
+ exportC4LevelViews,
8708
+ extractDiagramCodeAnchors,
6324
8709
  extractDiagramContext,
8710
+ filterC4Hierarchy,
6325
8711
  findClosestMatch,
6326
8712
  formatScene,
8713
+ generateC4Storyboard,
6327
8714
  generateThemeFromBrand,
8715
+ getArchitectureRecipe,
6328
8716
  getArchitectureSuggestions,
6329
8717
  getBoxPortPosition,
6330
8718
  getIntelliCodeCompletions,
6331
8719
  humanizeId,
8720
+ inferNodeC4Level,
8721
+ listArchitectureRecipes,
8722
+ listAvailableSymbols,
6332
8723
  listOutputPresets,
6333
8724
  nodeRole,
6334
8725
  parse,
6335
8726
  parseAndCompile,
8727
+ parseCodeAnchor,
6336
8728
  predictNextLineSuggestion,
8729
+ recommendArchitecturePattern,
8730
+ renderSymbolSvg,
6337
8731
  repairMarkdyCode,
6338
8732
  resolveArchitectureConfig,
6339
8733
  resolveOutputPreset,
6340
8734
  resolvePlayer,
6341
8735
  resolveTheme,
8736
+ resolveVectorSymbol,
6342
8737
  routeOrthogonalEdge,
6343
8738
  selectOptimalPorts,
6344
- validateArchitecture
8739
+ synthesizeCustomRecipe,
8740
+ validateArchitecture,
8741
+ validateC4Containment,
8742
+ verifyCodeAnchorsWithReader,
8743
+ verifyDiagramQuality
6345
8744
  };